Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6:
[S390] mm: add ZONE_DMA to 31-bit config again
[S390] mm: add page fault retry handling
[S390] mm: handle kernel caused page fault oom situations
[S390] delay: implement ndelay
[S390] topology,sched: fix cpu_coregroup_mask/cpu_book_mask definitions
[S390] hwsampler: allow cpu hotplug
[S390] uaccess: turn __access_ok() into a define
[S390] irq: merge irq.c and s390_ext.c
[S390] irq: fix service signal external interrupt handling
[S390] pfault: always enable service signal interrupt
diff --git a/Documentation/ABI/obsolete/o2cb b/Documentation/ABI/removed/o2cb
similarity index 65%
rename from Documentation/ABI/obsolete/o2cb
rename to Documentation/ABI/removed/o2cb
index 9c49d8e..7f5daa4 100644
--- a/Documentation/ABI/obsolete/o2cb
+++ b/Documentation/ABI/removed/o2cb
@@ -1,11 +1,10 @@
What: /sys/o2cb symlink
-Date: Dec 2005
-KernelVersion: 2.6.16
+Date: May 2011
+KernelVersion: 2.6.40
Contact: ocfs2-devel@oss.oracle.com
-Description: This is a symlink: /sys/o2cb to /sys/fs/o2cb. The symlink will
- be removed when new versions of ocfs2-tools which know to look
+Description: This is a symlink: /sys/o2cb to /sys/fs/o2cb. The symlink is
+ removed when new versions of ocfs2-tools which know to look
in /sys/fs/o2cb are sufficiently prevalent. Don't code new
software to look here, it should try /sys/fs/o2cb instead.
- See Documentation/ABI/stable/o2cb for more information on usage.
Users: ocfs2-tools. It's sufficient to mail proposed changes to
ocfs2-devel@oss.oracle.com.
diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-cleancache b/Documentation/ABI/testing/sysfs-kernel-mm-cleancache
new file mode 100644
index 0000000..662ae64
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-mm-cleancache
@@ -0,0 +1,11 @@
+What: /sys/kernel/mm/cleancache/
+Date: April 2011
+Contact: Dan Magenheimer <dan.magenheimer@oracle.com>
+Description:
+ /sys/kernel/mm/cleancache/ contains a number of files which
+ record a count of various cleancache operations
+ (sum across all filesystems):
+ succ_gets
+ failed_gets
+ puts
+ flushes
diff --git a/Documentation/DocBook/dvb/dvbproperty.xml b/Documentation/DocBook/dvb/dvbproperty.xml
index 52d5e3c..b5365f6 100644
--- a/Documentation/DocBook/dvb/dvbproperty.xml
+++ b/Documentation/DocBook/dvb/dvbproperty.xml
@@ -141,13 +141,15 @@
</row></tbody></tgroup></informaltable>
</section>
+<section>
+ <title>Property types</title>
<para>
On <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>/<link linkend="FE_SET_PROPERTY">FE_SET_PROPERTY</link>,
the actual action is determined by the dtv_property cmd/data pairs. With one single ioctl, is possible to
get/set up to 64 properties. The actual meaning of each property is described on the next sections.
</para>
-<para>The Available frontend property types are:</para>
+<para>The available frontend property types are:</para>
<programlisting>
#define DTV_UNDEFINED 0
#define DTV_TUNE 1
@@ -193,6 +195,7 @@
#define DTV_ISDBT_LAYER_ENABLED 41
#define DTV_ISDBS_TS_ID 42
</programlisting>
+</section>
<section id="fe_property_common">
<title>Parameters that are common to all Digital TV standards</title>
diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl
index c8abb23..e5fe094 100644
--- a/Documentation/DocBook/media-entities.tmpl
+++ b/Documentation/DocBook/media-entities.tmpl
@@ -293,6 +293,7 @@
<!ENTITY sub-yuyv SYSTEM "v4l/pixfmt-yuyv.xml">
<!ENTITY sub-yvyu SYSTEM "v4l/pixfmt-yvyu.xml">
<!ENTITY sub-srggb10 SYSTEM "v4l/pixfmt-srggb10.xml">
+<!ENTITY sub-srggb12 SYSTEM "v4l/pixfmt-srggb12.xml">
<!ENTITY sub-srggb8 SYSTEM "v4l/pixfmt-srggb8.xml">
<!ENTITY sub-y10 SYSTEM "v4l/pixfmt-y10.xml">
<!ENTITY sub-y12 SYSTEM "v4l/pixfmt-y12.xml">
@@ -373,9 +374,9 @@
<!ENTITY sub-media-indices SYSTEM "media-indices.tmpl">
<!ENTITY sub-media-controller SYSTEM "v4l/media-controller.xml">
-<!ENTITY sub-media-open SYSTEM "v4l/media-func-open.xml">
-<!ENTITY sub-media-close SYSTEM "v4l/media-func-close.xml">
-<!ENTITY sub-media-ioctl SYSTEM "v4l/media-func-ioctl.xml">
+<!ENTITY sub-media-func-open SYSTEM "v4l/media-func-open.xml">
+<!ENTITY sub-media-func-close SYSTEM "v4l/media-func-close.xml">
+<!ENTITY sub-media-func-ioctl SYSTEM "v4l/media-func-ioctl.xml">
<!ENTITY sub-media-ioc-device-info SYSTEM "v4l/media-ioc-device-info.xml">
<!ENTITY sub-media-ioc-enum-entities SYSTEM "v4l/media-ioc-enum-entities.xml">
<!ENTITY sub-media-ioc-enum-links SYSTEM "v4l/media-ioc-enum-links.xml">
diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml
index 2dc25e1..873ac3a 100644
--- a/Documentation/DocBook/v4l/media-controller.xml
+++ b/Documentation/DocBook/v4l/media-controller.xml
@@ -78,9 +78,9 @@
<appendix id="media-user-func">
<title>Function Reference</title>
<!-- Keep this alphabetically sorted. -->
- &sub-media-open;
- &sub-media-close;
- &sub-media-ioctl;
+ &sub-media-func-open;
+ &sub-media-func-close;
+ &sub-media-func-ioctl;
<!-- All ioctls go here. -->
&sub-media-ioc-device-info;
&sub-media-ioc-enum-entities;
diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml
index dbfe3b0..deb6602 100644
--- a/Documentation/DocBook/v4l/pixfmt.xml
+++ b/Documentation/DocBook/v4l/pixfmt.xml
@@ -673,6 +673,7 @@
&sub-srggb8;
&sub-sbggr16;
&sub-srggb10;
+ &sub-srggb12;
</section>
<section id="yuv-formats">
diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/v4l/subdev-formats.xml
index a26b10c..8d3409d 100644
--- a/Documentation/DocBook/v4l/subdev-formats.xml
+++ b/Documentation/DocBook/v4l/subdev-formats.xml
@@ -2531,13 +2531,13 @@
<constant>_JPEG</constant> prefix the format code is made of
the following information.
<itemizedlist>
- <listitem>The number of bus samples per entropy encoded byte.</listitem>
- <listitem>The bus width.</listitem>
+ <listitem><para>The number of bus samples per entropy encoded byte.</para></listitem>
+ <listitem><para>The bus width.</para></listitem>
</itemizedlist>
+ </para>
- <para>For instance, for a JPEG baseline process and an 8-bit bus width
- the format will be named <constant>V4L2_MBUS_FMT_JPEG_1X8</constant>.
- </para>
+ <para>For instance, for a JPEG baseline process and an 8-bit bus width
+ the format will be named <constant>V4L2_MBUS_FMT_JPEG_1X8</constant>.
</para>
<para>The following table lists existing JPEG compressed formats.</para>
diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c
index e9c7778..f6318f6 100644
--- a/Documentation/accounting/getdelays.c
+++ b/Documentation/accounting/getdelays.c
@@ -177,6 +177,8 @@
rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY,
CTRL_ATTR_FAMILY_NAME, (void *)name,
strlen(TASKSTATS_GENL_NAME)+1);
+ if (rc < 0)
+ return 0; /* sendto() failure? */
rep_len = recv(sd, &ans, sizeof(ans), 0);
if (ans.n.nlmsg_type == NLMSG_ERROR ||
@@ -191,30 +193,37 @@
return id;
}
+#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
+
static void print_delayacct(struct taskstats *t)
{
- printf("\n\nCPU %15s%15s%15s%15s\n"
- " %15llu%15llu%15llu%15llu\n"
- "IO %15s%15s\n"
- " %15llu%15llu\n"
- "SWAP %15s%15s\n"
- " %15llu%15llu\n"
- "RECLAIM %12s%15s\n"
- " %15llu%15llu\n",
- "count", "real total", "virtual total", "delay total",
+ printf("\n\nCPU %15s%15s%15s%15s%15s\n"
+ " %15llu%15llu%15llu%15llu%15.3fms\n"
+ "IO %15s%15s%15s\n"
+ " %15llu%15llu%15llums\n"
+ "SWAP %15s%15s%15s\n"
+ " %15llu%15llu%15llums\n"
+ "RECLAIM %12s%15s%15s\n"
+ " %15llu%15llu%15llums\n",
+ "count", "real total", "virtual total",
+ "delay total", "delay average",
(unsigned long long)t->cpu_count,
(unsigned long long)t->cpu_run_real_total,
(unsigned long long)t->cpu_run_virtual_total,
(unsigned long long)t->cpu_delay_total,
- "count", "delay total",
+ average_ms((double)t->cpu_delay_total, t->cpu_count),
+ "count", "delay total", "delay average",
(unsigned long long)t->blkio_count,
(unsigned long long)t->blkio_delay_total,
- "count", "delay total",
+ average_ms(t->blkio_delay_total, t->blkio_count),
+ "count", "delay total", "delay average",
(unsigned long long)t->swapin_count,
(unsigned long long)t->swapin_delay_total,
- "count", "delay total",
+ average_ms(t->swapin_delay_total, t->swapin_count),
+ "count", "delay total", "delay average",
(unsigned long long)t->freepages_count,
- (unsigned long long)t->freepages_delay_total);
+ (unsigned long long)t->freepages_delay_total,
+ average_ms(t->freepages_delay_total, t->freepages_count));
}
static void task_context_switch_counts(struct taskstats *t)
@@ -433,8 +442,6 @@
}
do {
- int i;
-
rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
PRINTF("received %d bytes\n", rep_len);
@@ -459,7 +466,6 @@
na = (struct nlattr *) GENLMSG_DATA(&msg);
len = 0;
- i = 0;
while (len < rep_len) {
len += NLA_ALIGN(na->nla_len);
switch (na->nla_type) {
diff --git a/Documentation/atomic_ops.txt b/Documentation/atomic_ops.txt
index ac4d471..3bd585b 100644
--- a/Documentation/atomic_ops.txt
+++ b/Documentation/atomic_ops.txt
@@ -12,7 +12,7 @@
C integer type will fail. Something like the following should
suffice:
- typedef struct { volatile int counter; } atomic_t;
+ typedef struct { int counter; } atomic_t;
Historically, counter has been declared volatile. This is now discouraged.
See Documentation/volatile-considered-harmful.txt for the complete rationale.
diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt
index aedf1bd..0ed99f0 100644
--- a/Documentation/cgroups/cgroups.txt
+++ b/Documentation/cgroups/cgroups.txt
@@ -236,7 +236,8 @@
- cgroup.procs: list of tgids in the cgroup. This list is not
guaranteed to be sorted or free of duplicate tgids, and userspace
should sort/uniquify the list if this property is required.
- This is a read-only file, for now.
+ Writing a thread group id into this file moves all threads in that
+ group into this cgroup.
- notify_on_release flag: run the release agent on exit?
- release_agent: the path to use for release notifications (this file
exists in the top cgroup only)
@@ -430,6 +431,12 @@
# echo 0 > tasks
+You can use the cgroup.procs file instead of the tasks file to move all
+threads in a threadgroup at once. Echoing the pid of any task in a
+threadgroup to cgroup.procs causes all tasks in that threadgroup to be
+be attached to the cgroup. Writing 0 to cgroup.procs moves all tasks
+in the writing task's threadgroup.
+
Note: Since every task is always a member of exactly one cgroup in each
mounted hierarchy, to remove a task from its current cgroup you must
move it into a new cgroup (possibly the root cgroup) by writing to the
@@ -575,7 +582,7 @@
called multiple times against a cgroup.
int can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct task_struct *task, bool threadgroup)
+ struct task_struct *task)
(cgroup_mutex held by caller)
Called prior to moving a task into a cgroup; if the subsystem
@@ -584,9 +591,14 @@
unspecified task can be moved into the cgroup. Note that this isn't
called on a fork. If this method returns 0 (success) then this should
remain valid while the caller holds cgroup_mutex and it is ensured that either
-attach() or cancel_attach() will be called in future. If threadgroup is
-true, then a successful result indicates that all threads in the given
-thread's threadgroup can be moved together.
+attach() or cancel_attach() will be called in future.
+
+int can_attach_task(struct cgroup *cgrp, struct task_struct *tsk);
+(cgroup_mutex held by caller)
+
+As can_attach, but for operations that must be run once per task to be
+attached (possibly many when using cgroup_attach_proc). Called after
+can_attach.
void cancel_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
struct task_struct *task, bool threadgroup)
@@ -598,15 +610,24 @@
This will be called only about subsystems whose can_attach() operation have
succeeded.
+void pre_attach(struct cgroup *cgrp);
+(cgroup_mutex held by caller)
+
+For any non-per-thread attachment work that needs to happen before
+attach_task. Needed by cpuset.
+
void attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct cgroup *old_cgrp, struct task_struct *task,
- bool threadgroup)
+ struct cgroup *old_cgrp, struct task_struct *task)
(cgroup_mutex held by caller)
Called after the task has been attached to the cgroup, to allow any
post-attachment activity that requires memory allocations or blocking.
-If threadgroup is true, the subsystem should take care of all threads
-in the specified thread's threadgroup. Currently does not support any
+
+void attach_task(struct cgroup *cgrp, struct task_struct *tsk);
+(cgroup_mutex held by caller)
+
+As attach, but for operations that must be run once per task to be attached,
+like can_attach_task. Called before attach. Currently does not support any
subsystem that might need the old_cgrp for every thread in the group.
void fork(struct cgroup_subsy *ss, struct task_struct *task)
@@ -630,7 +651,7 @@
void post_clone(struct cgroup_subsys *ss, struct cgroup *cgrp)
(cgroup_mutex held by caller)
-Called at the end of cgroup_clone() to do any parameter
+Called during cgroup_create() to do any parameter
initialization which might be required before a task could attach. For
example in cpusets, no task may attach before 'cpus' and 'mems' are set
up.
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 95788ad..ff31b1c 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -262,16 +262,6 @@
---------------------------
-What: /sys/o2cb symlink
-When: January 2010
-Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb
- exists as a symlink for backwards compatibility for old versions of
- ocfs2-tools. 2 years should be sufficient time to phase in new versions
- which know to look in /sys/fs/o2cb.
-Who: ocfs2-devel@oss.oracle.com
-
----------------------------
-
What: Ability for non root users to shm_get hugetlb pages based on mlock
resource limits
When: 2.6.31
diff --git a/Documentation/filesystems/configfs/configfs_example_explicit.c b/Documentation/filesystems/configfs/configfs_example_explicit.c
index fd53869f..1420233 100644
--- a/Documentation/filesystems/configfs/configfs_example_explicit.c
+++ b/Documentation/filesystems/configfs/configfs_example_explicit.c
@@ -464,9 +464,8 @@
return 0;
out_unregister:
- for (; i >= 0; i--) {
+ for (i--; i >= 0; i--)
configfs_unregister_subsystem(example_subsys[i]);
- }
return ret;
}
@@ -475,9 +474,8 @@
{
int i;
- for (i = 0; example_subsys[i]; i++) {
+ for (i = 0; example_subsys[i]; i++)
configfs_unregister_subsystem(example_subsys[i]);
- }
}
module_init(configfs_example_init);
diff --git a/Documentation/filesystems/configfs/configfs_example_macros.c b/Documentation/filesystems/configfs/configfs_example_macros.c
index d8e30a0..327dfbc 100644
--- a/Documentation/filesystems/configfs/configfs_example_macros.c
+++ b/Documentation/filesystems/configfs/configfs_example_macros.c
@@ -427,9 +427,8 @@
return 0;
out_unregister:
- for (; i >= 0; i--) {
+ for (i--; i >= 0; i--)
configfs_unregister_subsystem(example_subsys[i]);
- }
return ret;
}
@@ -438,9 +437,8 @@
{
int i;
- for (i = 0; example_subsys[i]; i++) {
+ for (i = 0; example_subsys[i]; i++)
configfs_unregister_subsystem(example_subsys[i]);
- }
}
module_init(configfs_example_init);
diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
index c79ec58..3ae9bc9 100644
--- a/Documentation/filesystems/ext4.txt
+++ b/Documentation/filesystems/ext4.txt
@@ -226,10 +226,6 @@
noacl This option disables POSIX Access Control List
support.
-reservation
-
-noreservation
-
bsddf (*) Make 'df' act like BSD.
minixdf Make 'df' act like Minix.
diff --git a/Documentation/filesystems/ocfs2.txt b/Documentation/filesystems/ocfs2.txt
index 9ed920a..7618a28 100644
--- a/Documentation/filesystems/ocfs2.txt
+++ b/Documentation/filesystems/ocfs2.txt
@@ -46,9 +46,15 @@
intr (*) Allow signals to interrupt cluster operations.
nointr Do not allow signals to interrupt cluster
operations.
+noatime Do not update access time.
+relatime(*) Update atime if the previous atime is older than
+ mtime or ctime
+strictatime Always update atime, but the minimum update interval
+ is specified by atime_quantum.
atime_quantum=60(*) OCFS2 will not update atime unless this number
of seconds has passed since the last update.
- Set to zero to always update atime.
+ Set to zero to always update atime. This option need
+ work with strictatime.
data=ordered (*) All data are forced directly out to the main file
system prior to its metadata being committed to the
journal.
diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt
index 7bff3e4..3fc0c31 100644
--- a/Documentation/filesystems/xfs.txt
+++ b/Documentation/filesystems/xfs.txt
@@ -39,6 +39,12 @@
drive level write caching to be enabled, for devices that
support write barriers.
+ discard
+ Issue command to let the block device reclaim space freed by the
+ filesystem. This is useful for SSD devices, thinly provisioned
+ LUNs and virtual machine images, but may have a performance
+ impact. This option is incompatible with the nodelaylog option.
+
dmapi
Enable the DMAPI (Data Management API) event callouts.
Use with the "mtpt" option.
diff --git a/Documentation/power/regulator/machine.txt b/Documentation/power/regulator/machine.txt
index bdec39b..b42419b 100644
--- a/Documentation/power/regulator/machine.txt
+++ b/Documentation/power/regulator/machine.txt
@@ -53,11 +53,11 @@
Regulator-1 supplies power to Regulator-2. This relationship must be registered
with the core so that Regulator-1 is also enabled when Consumer A enables its
-supply (Regulator-2). The supply regulator is set by the supply_regulator_dev
+supply (Regulator-2). The supply regulator is set by the supply_regulator
field below:-
static struct regulator_init_data regulator2_data = {
- .supply_regulator_dev = &platform_regulator1_device.dev,
+ .supply_regulator = "regulator_name",
.constraints = {
.min_uV = 1800000,
.max_uV = 2000000,
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 36f0075..5e7cb39 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -161,7 +161,8 @@
%s signal number
%t UNIX time of dump
%h hostname
- %e executable filename
+ %e executable filename (may be shortened)
+ %E executable path
%<OTHER> both are dropped
. If the first character of the pattern is a '|', the kernel will treat
the rest of the pattern as a command to run. The core dump will be
diff --git a/Documentation/vm/cleancache.txt b/Documentation/vm/cleancache.txt
new file mode 100644
index 0000000..36c367c
--- /dev/null
+++ b/Documentation/vm/cleancache.txt
@@ -0,0 +1,278 @@
+MOTIVATION
+
+Cleancache is a new optional feature provided by the VFS layer that
+potentially dramatically increases page cache effectiveness for
+many workloads in many environments at a negligible cost.
+
+Cleancache can be thought of as a page-granularity victim cache for clean
+pages that the kernel's pageframe replacement algorithm (PFRA) would like
+to keep around, but can't since there isn't enough memory. So when the
+PFRA "evicts" a page, it first attempts to use cleancache code to
+put the data contained in that page into "transcendent memory", memory
+that is not directly accessible or addressable by the kernel and is
+of unknown and possibly time-varying size.
+
+Later, when a cleancache-enabled filesystem wishes to access a page
+in a file on disk, it first checks cleancache to see if it already
+contains it; if it does, the page of data is copied into the kernel
+and a disk access is avoided.
+
+Transcendent memory "drivers" for cleancache are currently implemented
+in Xen (using hypervisor memory) and zcache (using in-kernel compressed
+memory) and other implementations are in development.
+
+FAQs are included below.
+
+IMPLEMENTATION OVERVIEW
+
+A cleancache "backend" that provides transcendent memory registers itself
+to the kernel's cleancache "frontend" by calling cleancache_register_ops,
+passing a pointer to a cleancache_ops structure with funcs set appropriately.
+Note that cleancache_register_ops returns the previous settings so that
+chaining can be performed if desired. The functions provided must conform to
+certain semantics as follows:
+
+Most important, cleancache is "ephemeral". Pages which are copied into
+cleancache have an indefinite lifetime which is completely unknowable
+by the kernel and so may or may not still be in cleancache at any later time.
+Thus, as its name implies, cleancache is not suitable for dirty pages.
+Cleancache has complete discretion over what pages to preserve and what
+pages to discard and when.
+
+Mounting a cleancache-enabled filesystem should call "init_fs" to obtain a
+pool id which, if positive, must be saved in the filesystem's superblock;
+a negative return value indicates failure. A "put_page" will copy a
+(presumably about-to-be-evicted) page into cleancache and associate it with
+the pool id, a file key, and a page index into the file. (The combination
+of a pool id, a file key, and an index is sometimes called a "handle".)
+A "get_page" will copy the page, if found, from cleancache into kernel memory.
+A "flush_page" will ensure the page no longer is present in cleancache;
+a "flush_inode" will flush all pages associated with the specified file;
+and, when a filesystem is unmounted, a "flush_fs" will flush all pages in
+all files specified by the given pool id and also surrender the pool id.
+
+An "init_shared_fs", like init_fs, obtains a pool id but tells cleancache
+to treat the pool as shared using a 128-bit UUID as a key. On systems
+that may run multiple kernels (such as hard partitioned or virtualized
+systems) that may share a clustered filesystem, and where cleancache
+may be shared among those kernels, calls to init_shared_fs that specify the
+same UUID will receive the same pool id, thus allowing the pages to
+be shared. Note that any security requirements must be imposed outside
+of the kernel (e.g. by "tools" that control cleancache). Or a
+cleancache implementation can simply disable shared_init by always
+returning a negative value.
+
+If a get_page is successful on a non-shared pool, the page is flushed (thus
+making cleancache an "exclusive" cache). On a shared pool, the page
+is NOT flushed on a successful get_page so that it remains accessible to
+other sharers. The kernel is responsible for ensuring coherency between
+cleancache (shared or not), the page cache, and the filesystem, using
+cleancache flush operations as required.
+
+Note that cleancache must enforce put-put-get coherency and get-get
+coherency. For the former, if two puts are made to the same handle but
+with different data, say AAA by the first put and BBB by the second, a
+subsequent get can never return the stale data (AAA). For get-get coherency,
+if a get for a given handle fails, subsequent gets for that handle will
+never succeed unless preceded by a successful put with that handle.
+
+Last, cleancache provides no SMP serialization guarantees; if two
+different Linux threads are simultaneously putting and flushing a page
+with the same handle, the results are indeterminate. Callers must
+lock the page to ensure serial behavior.
+
+CLEANCACHE PERFORMANCE METRICS
+
+Cleancache monitoring is done by sysfs files in the
+/sys/kernel/mm/cleancache directory. The effectiveness of cleancache
+can be measured (across all filesystems) with:
+
+succ_gets - number of gets that were successful
+failed_gets - number of gets that failed
+puts - number of puts attempted (all "succeed")
+flushes - number of flushes attempted
+
+A backend implementatation may provide additional metrics.
+
+FAQ
+
+1) Where's the value? (Andrew Morton)
+
+Cleancache provides a significant performance benefit to many workloads
+in many environments with negligible overhead by improving the
+effectiveness of the pagecache. Clean pagecache pages are
+saved in transcendent memory (RAM that is otherwise not directly
+addressable to the kernel); fetching those pages later avoids "refaults"
+and thus disk reads.
+
+Cleancache (and its sister code "frontswap") provide interfaces for
+this transcendent memory (aka "tmem"), which conceptually lies between
+fast kernel-directly-addressable RAM and slower DMA/asynchronous devices.
+Disallowing direct kernel or userland reads/writes to tmem
+is ideal when data is transformed to a different form and size (such
+as with compression) or secretly moved (as might be useful for write-
+balancing for some RAM-like devices). Evicted page-cache pages (and
+swap pages) are a great use for this kind of slower-than-RAM-but-much-
+faster-than-disk transcendent memory, and the cleancache (and frontswap)
+"page-object-oriented" specification provides a nice way to read and
+write -- and indirectly "name" -- the pages.
+
+In the virtual case, the whole point of virtualization is to statistically
+multiplex physical resources across the varying demands of multiple
+virtual machines. This is really hard to do with RAM and efforts to
+do it well with no kernel change have essentially failed (except in some
+well-publicized special-case workloads). Cleancache -- and frontswap --
+with a fairly small impact on the kernel, provide a huge amount
+of flexibility for more dynamic, flexible RAM multiplexing.
+Specifically, the Xen Transcendent Memory backend allows otherwise
+"fallow" hypervisor-owned RAM to not only be "time-shared" between multiple
+virtual machines, but the pages can be compressed and deduplicated to
+optimize RAM utilization. And when guest OS's are induced to surrender
+underutilized RAM (e.g. with "self-ballooning"), page cache pages
+are the first to go, and cleancache allows those pages to be
+saved and reclaimed if overall host system memory conditions allow.
+
+And the identical interface used for cleancache can be used in
+physical systems as well. The zcache driver acts as a memory-hungry
+device that stores pages of data in a compressed state. And
+the proposed "RAMster" driver shares RAM across multiple physical
+systems.
+
+2) Why does cleancache have its sticky fingers so deep inside the
+ filesystems and VFS? (Andrew Morton and Christoph Hellwig)
+
+The core hooks for cleancache in VFS are in most cases a single line
+and the minimum set are placed precisely where needed to maintain
+coherency (via cleancache_flush operations) between cleancache,
+the page cache, and disk. All hooks compile into nothingness if
+cleancache is config'ed off and turn into a function-pointer-
+compare-to-NULL if config'ed on but no backend claims the ops
+functions, or to a compare-struct-element-to-negative if a
+backend claims the ops functions but a filesystem doesn't enable
+cleancache.
+
+Some filesystems are built entirely on top of VFS and the hooks
+in VFS are sufficient, so don't require an "init_fs" hook; the
+initial implementation of cleancache didn't provide this hook.
+But for some filesystems (such as btrfs), the VFS hooks are
+incomplete and one or more hooks in fs-specific code are required.
+And for some other filesystems, such as tmpfs, cleancache may
+be counterproductive. So it seemed prudent to require a filesystem
+to "opt in" to use cleancache, which requires adding a hook in
+each filesystem. Not all filesystems are supported by cleancache
+only because they haven't been tested. The existing set should
+be sufficient to validate the concept, the opt-in approach means
+that untested filesystems are not affected, and the hooks in the
+existing filesystems should make it very easy to add more
+filesystems in the future.
+
+The total impact of the hooks to existing fs and mm files is only
+about 40 lines added (not counting comments and blank lines).
+
+3) Why not make cleancache asynchronous and batched so it can
+ more easily interface with real devices with DMA instead
+ of copying each individual page? (Minchan Kim)
+
+The one-page-at-a-time copy semantics simplifies the implementation
+on both the frontend and backend and also allows the backend to
+do fancy things on-the-fly like page compression and
+page deduplication. And since the data is "gone" (copied into/out
+of the pageframe) before the cleancache get/put call returns,
+a great deal of race conditions and potential coherency issues
+are avoided. While the interface seems odd for a "real device"
+or for real kernel-addressable RAM, it makes perfect sense for
+transcendent memory.
+
+4) Why is non-shared cleancache "exclusive"? And where is the
+ page "flushed" after a "get"? (Minchan Kim)
+
+The main reason is to free up space in transcendent memory and
+to avoid unnecessary cleancache_flush calls. If you want inclusive,
+the page can be "put" immediately following the "get". If
+put-after-get for inclusive becomes common, the interface could
+be easily extended to add a "get_no_flush" call.
+
+The flush is done by the cleancache backend implementation.
+
+5) What's the performance impact?
+
+Performance analysis has been presented at OLS'09 and LCA'10.
+Briefly, performance gains can be significant on most workloads,
+especially when memory pressure is high (e.g. when RAM is
+overcommitted in a virtual workload); and because the hooks are
+invoked primarily in place of or in addition to a disk read/write,
+overhead is negligible even in worst case workloads. Basically
+cleancache replaces I/O with memory-copy-CPU-overhead; on older
+single-core systems with slow memory-copy speeds, cleancache
+has little value, but in newer multicore machines, especially
+consolidated/virtualized machines, it has great value.
+
+6) How do I add cleancache support for filesystem X? (Boaz Harrash)
+
+Filesystems that are well-behaved and conform to certain
+restrictions can utilize cleancache simply by making a call to
+cleancache_init_fs at mount time. Unusual, misbehaving, or
+poorly layered filesystems must either add additional hooks
+and/or undergo extensive additional testing... or should just
+not enable the optional cleancache.
+
+Some points for a filesystem to consider:
+
+- The FS should be block-device-based (e.g. a ram-based FS such
+ as tmpfs should not enable cleancache)
+- To ensure coherency/correctness, the FS must ensure that all
+ file removal or truncation operations either go through VFS or
+ add hooks to do the equivalent cleancache "flush" operations
+- To ensure coherency/correctness, either inode numbers must
+ be unique across the lifetime of the on-disk file OR the
+ FS must provide an "encode_fh" function.
+- The FS must call the VFS superblock alloc and deactivate routines
+ or add hooks to do the equivalent cleancache calls done there.
+- To maximize performance, all pages fetched from the FS should
+ go through the do_mpag_readpage routine or the FS should add
+ hooks to do the equivalent (cf. btrfs)
+- Currently, the FS blocksize must be the same as PAGESIZE. This
+ is not an architectural restriction, but no backends currently
+ support anything different.
+- A clustered FS should invoke the "shared_init_fs" cleancache
+ hook to get best performance for some backends.
+
+7) Why not use the KVA of the inode as the key? (Christoph Hellwig)
+
+If cleancache would use the inode virtual address instead of
+inode/filehandle, the pool id could be eliminated. But, this
+won't work because cleancache retains pagecache data pages
+persistently even when the inode has been pruned from the
+inode unused list, and only flushes the data page if the file
+gets removed/truncated. So if cleancache used the inode kva,
+there would be potential coherency issues if/when the inode
+kva is reused for a different file. Alternately, if cleancache
+flushed the pages when the inode kva was freed, much of the value
+of cleancache would be lost because the cache of pages in cleanache
+is potentially much larger than the kernel pagecache and is most
+useful if the pages survive inode cache removal.
+
+8) Why is a global variable required?
+
+The cleancache_enabled flag is checked in all of the frequently-used
+cleancache hooks. The alternative is a function call to check a static
+variable. Since cleancache is enabled dynamically at runtime, systems
+that don't enable cleancache would suffer thousands (possibly
+tens-of-thousands) of unnecessary function calls per second. So the
+global variable allows cleancache to be enabled by default at compile
+time, but have insignificant performance impact when cleancache remains
+disabled at runtime.
+
+9) Does cleanache work with KVM?
+
+The memory model of KVM is sufficiently different that a cleancache
+backend may have less value for KVM. This remains to be tested,
+especially in an overcommitted system.
+
+10) Does cleancache work in userspace? It sounds useful for
+ memory hungry caches like web browsers. (Jamie Lokier)
+
+No plans yet, though we agree it sounds useful, at least for
+apps that bypass the page cache (e.g. O_DIRECT).
+
+Last updated: Dan Magenheimer, April 13 2011
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ab17de..572b5b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -931,6 +931,8 @@
F: drivers/tty/serial/msm_serial.h
F: drivers/tty/serial/msm_serial.c
F: drivers/platform/msm/
+F: drivers/*/pm8???-*
+F: include/linux/mfd/pm8xxx/
T: git git://codeaurora.org/quic/kernel/davidb/linux-msm.git
S: Maintained
@@ -2582,6 +2584,13 @@
F: drivers/hwmon/f75375s.c
F: include/linux/f75375s.h
+FIREWIRE AUDIO DRIVERS
+M: Clemens Ladisch <clemens@ladisch.de>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+T: git git://git.alsa-project.org/alsa-kernel.git
+S: Maintained
+F: sound/firewire/
+
FIREWIRE SUBSYSTEM
M: Stefan Richter <stefanr@s5r6.in-berlin.de>
L: linux1394-devel@lists.sourceforge.net
@@ -3572,9 +3581,16 @@
M: Jan Kara <jack@suse.cz>
L: linux-ext4@vger.kernel.org
S: Maintained
-F: fs/jbd*/
-F: include/linux/ext*jbd*.h
-F: include/linux/jbd*.h
+F: fs/jbd/
+F: include/linux/ext3_jbd.h
+F: include/linux/jbd.h
+
+JOURNALLING LAYER FOR BLOCK DEVICES (JBD2)
+M: "Theodore Ts'o" <tytso@mit.edu>
+L: linux-ext4@vger.kernel.org
+S: Maintained
+F: fs/jbd2/
+F: include/linux/jbd2.h
JSM Neo PCI based serial card
M: Breno Leitao <leitao@linux.vnet.ibm.com>
@@ -4138,6 +4154,7 @@
L: linux-mm@kvack.org
S: Maintained
F: mm/memcontrol.c
+F: mm/page_cgroup.c
MEMORY TECHNOLOGY DEVICES (MTD)
M: David Woodhouse <dwmw2@infradead.org>
@@ -5991,7 +6008,7 @@
F: arch/powerpc/platforms/cell/spufs/
SQUASHFS FILE SYSTEM
-M: Phillip Lougher <phillip@lougher.demon.co.uk>
+M: Phillip Lougher <phillip@squashfs.org.uk>
L: squashfs-devel@lists.sourceforge.net (subscribers-only)
W: http://squashfs.org.uk
S: Maintained
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index e3a8277..60219bf 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -41,10 +41,6 @@
bool
default n
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
config GENERIC_CALIBRATE_DELAY
bool
default y
diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig
index 076db52..d5f00d7 100644
--- a/arch/arm/configs/omap2plus_defconfig
+++ b/arch/arm/configs/omap2plus_defconfig
@@ -21,58 +21,22 @@
CONFIG_MODULE_SRCVERSION_ALL=y
# CONFIG_BLK_DEV_BSG is not set
CONFIG_ARCH_OMAP=y
-CONFIG_ARCH_OMAP2=y
-CONFIG_ARCH_OMAP3=y
-CONFIG_ARCH_OMAP4=y
CONFIG_OMAP_RESET_CLOCKS=y
CONFIG_OMAP_MUX_DEBUG=y
-CONFIG_OMAP_32K_TIMER=y
-CONFIG_MACH_OMAP_GENERIC=y
-CONFIG_ARCH_OMAP2420=y
-CONFIG_ARCH_OMAP2430=y
-CONFIG_ARCH_OMAP3430=y
-CONFIG_MACH_OMAP_H4=y
-CONFIG_MACH_OMAP_APOLLON=y
-CONFIG_MACH_OMAP_2430SDP=y
-CONFIG_MACH_OMAP3_BEAGLE=y
-CONFIG_MACH_DEVKIT8000=y
-CONFIG_MACH_OMAP_LDP=y
-CONFIG_MACH_OVERO=y
-CONFIG_MACH_OMAP3EVM=y
-CONFIG_MACH_OMAP3517EVM=y
-CONFIG_MACH_OMAP3_PANDORA=y
-CONFIG_MACH_OMAP3_TOUCHBOOK=y
-CONFIG_MACH_OMAP_3430SDP=y
-CONFIG_MACH_NOKIA_N8X0=y
-CONFIG_MACH_NOKIA_RX51=y
-CONFIG_MACH_OMAP_ZOOM2=y
-CONFIG_MACH_OMAP_ZOOM3=y
-CONFIG_MACH_CM_T35=y
-CONFIG_MACH_IGEP0020=y
-CONFIG_MACH_SBC3530=y
-CONFIG_MACH_OMAP_3630SDP=y
-CONFIG_MACH_OMAP_4430SDP=y
CONFIG_ARM_THUMBEE=y
-CONFIG_ARM_L1_CACHE_SHIFT=5
CONFIG_ARM_ERRATA_411920=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2
-# CONFIG_LOCAL_TIMERS is not set
-CONFIG_AEABI=y
CONFIG_LEDS=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200"
CONFIG_KEXEC=y
CONFIG_FPE_NWFPE=y
-CONFIG_VFP=y
-CONFIG_NEON=y
CONFIG_BINFMT_MISC=y
-CONFIG_PM=y
CONFIG_PM_DEBUG=y
-CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -89,14 +53,6 @@
# CONFIG_IPV6 is not set
CONFIG_NETFILTER=y
CONFIG_BT=m
-CONFIG_BT_L2CAP=m
-CONFIG_BT_SCO=m
-CONFIG_BT_RFCOMM=y
-CONFIG_BT_RFCOMM_TTY=y
-CONFIG_BT_BNEP=m
-CONFIG_BT_BNEP_MC_FILTER=y
-CONFIG_BT_BNEP_PROTO_FILTER=y
-CONFIG_BT_HIDP=m
CONFIG_BT_HCIUART=m
CONFIG_BT_HCIUART_H4=y
CONFIG_BT_HCIUART_BCSP=y
@@ -107,11 +63,9 @@
CONFIG_MAC80211=m
CONFIG_MAC80211_RC_PID=y
CONFIG_MAC80211_RC_DEFAULT_PID=y
-CONFIG_MAC80211_LEDS=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_CONNECTOR=y
CONFIG_MTD=y
-CONFIG_MTD_CONCAT=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
@@ -127,7 +81,6 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=16384
-CONFIG_EEPROM_LEGACY=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SCSI_MULTI_LUN=y
@@ -158,19 +111,15 @@
CONFIG_INPUT_MISC=y
CONFIG_INPUT_TWL4030_PWRBUTTON=y
CONFIG_VT_HW_CONSOLE_BINDING=y
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250_NR_UARTS=32
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
-# CONFIG_LEGACY_PTYS is not set
CONFIG_HW_RANDOM=y
-CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
-CONFIG_I2C_OMAP=y
CONFIG_SPI=y
CONFIG_SPI_OMAP24XX=y
CONFIG_DEBUG_GPIO=y
@@ -181,10 +130,6 @@
CONFIG_WATCHDOG=y
CONFIG_OMAP_WATCHDOG=y
CONFIG_TWL4030_WATCHDOG=y
-CONFIG_MENELAUS=y
-CONFIG_TWL4030_CORE=y
-CONFIG_TWL4030_POWER=y
-CONFIG_REGULATOR=y
CONFIG_REGULATOR_TWL4030=y
CONFIG_REGULATOR_TPS65023=y
CONFIG_REGULATOR_TPS6507X=y
@@ -208,7 +153,6 @@
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=y
CONFIG_DISPLAY_SUPPORT=y
-# CONFIG_VGA_CONSOLE is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_FONTS=y
@@ -217,25 +161,20 @@
CONFIG_LOGO=y
CONFIG_SOUND=m
CONFIG_SND=m
-CONFIG_SND_MIXER_OSS=y
-CONFIG_SND_PCM_OSS=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
CONFIG_SND_VERBOSE_PRINTK=y
CONFIG_SND_DEBUG=y
-CONFIG_SND_USB_AUDIO=y
-CONFIG_SND_SOC=y
-CONFIG_SND_OMAP_SOC=y
-CONFIG_SND_OMAP_SOC_OMAP3_PANDORA=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_SOC=m
+CONFIG_SND_OMAP_SOC=m
+CONFIG_SND_OMAP_SOC_OMAP3_PANDORA=m
CONFIG_USB=y
CONFIG_USB_DEBUG=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_DEVICEFS=y
CONFIG_USB_SUSPEND=y
-# CONFIG_USB_OTG_WHITELIST is not set
CONFIG_USB_MON=y
-# CONFIG_USB_MUSB_HDRC is not set
-# CONFIG_USB_MUSB_OTG is not set
-# CONFIG_USB_GADGET_MUSB_HDRC is not set
-CONFIG_USB_MUSB_DEBUG=y
CONFIG_USB_WDM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_LIBUSUAL=y
@@ -250,18 +189,12 @@
CONFIG_SDIO_UART=y
CONFIG_MMC_OMAP=y
CONFIG_MMC_OMAP_HS=y
-CONFIG_LEDS_CLASS=y
-CONFIG_LEDS_GPIO=y
-CONFIG_LEDS_TRIGGER_TIMER=y
-CONFIG_LEDS_TRIGGER_HEARTBEAT=y
-CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_TWL92330=y
CONFIG_RTC_DRV_TWL4030=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_FS_XATTR is not set
-CONFIG_INOTIFY=y
CONFIG_QUOTA=y
CONFIG_QFMT_V2=y
CONFIG_MSDOS_FS=y
@@ -285,12 +218,10 @@
CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_PROVE_LOCKING=y
-# CONFIG_LOCK_STAT is not set
CONFIG_DEBUG_SPINLOCK_SLEEP=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_DEBUG_INFO=y
diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h
index 6b7403f..b4892a0 100644
--- a/arch/arm/include/asm/bitops.h
+++ b/arch/arm/include/asm/bitops.h
@@ -203,8 +203,6 @@
#define find_first_bit(p,sz) _find_first_bit_le(p,sz)
#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off)
-#define WORD_BITOFF_TO_LE(x) ((x))
-
#else
/*
* These are the big endian, atomic definitions.
@@ -214,8 +212,6 @@
#define find_first_bit(p,sz) _find_first_bit_be(p,sz)
#define find_next_bit(p,sz,off) _find_next_bit_be(p,sz,off)
-#define WORD_BITOFF_TO_LE(x) ((x) ^ 0x18)
-
#endif
#if __LINUX_ARM_ARCH__ < 5
@@ -287,55 +283,29 @@
#include <asm-generic/bitops/hweight.h>
#include <asm-generic/bitops/lock.h>
-static inline void __set_bit_le(int nr, void *addr)
-{
- __set_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
-
-static inline void __clear_bit_le(int nr, void *addr)
-{
- __clear_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
-
-static inline int __test_and_set_bit_le(int nr, void *addr)
-{
- return __test_and_set_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
-
-static inline int test_and_set_bit_le(int nr, void *addr)
-{
- return test_and_set_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
-
-static inline int __test_and_clear_bit_le(int nr, void *addr)
-{
- return __test_and_clear_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
-
-static inline int test_and_clear_bit_le(int nr, void *addr)
-{
- return test_and_clear_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
-
-static inline int test_bit_le(int nr, const void *addr)
-{
- return test_bit(WORD_BITOFF_TO_LE(nr), addr);
-}
+#ifdef __ARMEB__
static inline int find_first_zero_bit_le(const void *p, unsigned size)
{
return _find_first_zero_bit_le(p, size);
}
+#define find_first_zero_bit_le find_first_zero_bit_le
static inline int find_next_zero_bit_le(const void *p, int size, int offset)
{
return _find_next_zero_bit_le(p, size, offset);
}
+#define find_next_zero_bit_le find_next_zero_bit_le
static inline int find_next_bit_le(const void *p, int size, int offset)
{
return _find_next_bit_le(p, size, offset);
}
+#define find_next_bit_le find_next_bit_le
+
+#endif
+
+#include <asm-generic/bitops/le.h>
/*
* Ext2 is defined to use little-endian byte ordering.
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index b997a358..19d5891 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -288,6 +288,7 @@
depends on ARCH_OMAP3
default y
select OMAP_PACKAGE_CBB
+ select MACH_IGEP0020
config MACH_SBC3530
bool "OMAP3 SBC STALKER board"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 66dfbcc..b148077 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -229,8 +229,6 @@
obj-$(CONFIG_MACH_CM_T3517) += board-cm-t3517.o
obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o \
hsmmc.o
-obj-$(CONFIG_MACH_IGEP0030) += board-igep0030.o \
- hsmmc.o
obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o \
hsmmc.o
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \
@@ -270,3 +268,5 @@
disp-$(CONFIG_OMAP2_DSS) := display.o
obj-y += $(disp-m) $(disp-y)
+
+obj-y += common-board-devices.o
diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c
index 1fa6bb8..d54969b 100644
--- a/arch/arm/mach-omap2/board-2430sdp.c
+++ b/arch/arm/mach-omap2/board-2430sdp.c
@@ -41,6 +41,7 @@
#include "mux.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define SDP2430_CS0_BASE 0x04000000
#define SECONDARY_LCD_GPIO 147
@@ -180,15 +181,6 @@
.vmmc1 = &sdp2430_vmmc1,
};
-static struct i2c_board_info __initdata sdp2430_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_24XX_SYS_NIRQ,
- .platform_data = &sdp2430_twldata,
- },
-};
-
static struct i2c_board_info __initdata sdp2430_i2c1_boardinfo[] = {
{
I2C_BOARD_INFO("isp1301_omap", 0x2D),
@@ -201,8 +193,7 @@
{
omap_register_i2c_bus(1, 100, sdp2430_i2c1_boardinfo,
ARRAY_SIZE(sdp2430_i2c1_boardinfo));
- omap_register_i2c_bus(2, 2600, sdp2430_i2c_boardinfo,
- ARRAY_SIZE(sdp2430_i2c_boardinfo));
+ omap2_pmic_init("twl4030", &sdp2430_twldata);
return 0;
}
@@ -217,11 +208,6 @@
{} /* Terminator */
};
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
static struct omap_usb_config sdp2430_usb_config __initdata = {
.otg = 1,
#ifdef CONFIG_USB_GADGET_OMAP
@@ -240,8 +226,6 @@
static void __init omap_2430sdp_init(void)
{
- int ret;
-
omap2430_mux_init(board_mux, OMAP_PACKAGE_ZAC);
omap_board_config = sdp2430_config;
@@ -255,14 +239,13 @@
omap2_usbfs_init(&sdp2430_usb_config);
omap_mux_init_signal("usb0hs_stp", OMAP_PULL_ENA | OMAP_PULL_UP);
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
board_smc91x_init();
/* Turn off secondary LCD backlight */
- ret = gpio_request(SECONDARY_LCD_GPIO, "Secondary LCD backlight");
- if (ret == 0)
- gpio_direction_output(SECONDARY_LCD_GPIO, 0);
+ gpio_request_one(SECONDARY_LCD_GPIO, GPIOF_OUT_INIT_LOW,
+ "Secondary LCD backlight");
}
static void __init omap_2430sdp_map_io(void)
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index 23244cd..ae2963a 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -19,7 +19,6 @@
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/spi/spi.h>
-#include <linux/spi/ads7846.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/machine.h>
#include <linux/io.h>
@@ -48,6 +47,7 @@
#include "hsmmc.h"
#include "pm.h"
#include "control.h"
+#include "common-board-devices.h"
#define CONFIG_DISABLE_HFCLK 1
@@ -59,24 +59,6 @@
#define TWL4030_MSECURE_GPIO 22
-/* FIXME: These values need to be updated based on more profiling on 3430sdp*/
-static struct cpuidle_params omap3_cpuidle_params_table[] = {
- /* C1 */
- {1, 2, 2, 5},
- /* C2 */
- {1, 10, 10, 30},
- /* C3 */
- {1, 50, 50, 300},
- /* C4 */
- {1, 1500, 1800, 4000},
- /* C5 */
- {1, 2500, 7500, 12000},
- /* C6 */
- {1, 3000, 8500, 15000},
- /* C7 */
- {1, 10000, 30000, 300000},
-};
-
static uint32_t board_keymap[] = {
KEY(0, 0, KEY_LEFT),
KEY(0, 1, KEY_RIGHT),
@@ -123,63 +105,14 @@
.rep = 1,
};
-static int ts_gpio; /* Needed for ads7846_get_pendown_state */
-
-/**
- * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
- *
- * @return - void. If request gpio fails then Flag KERN_ERR.
- */
-static void ads7846_dev_init(void)
-{
- if (gpio_request(ts_gpio, "ADS7846 pendown") < 0) {
- printk(KERN_ERR "can't get ads746 pen down GPIO\n");
- return;
- }
-
- gpio_direction_input(ts_gpio);
- gpio_set_debounce(ts_gpio, 310);
-}
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(ts_gpio);
-}
-
-static struct ads7846_platform_data tsc2046_config __initdata = {
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
- .wakeup = true,
-};
-
-
-static struct omap2_mcspi_device_config tsc2046_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static struct spi_board_info sdp3430_spi_board_info[] __initdata = {
- [0] = {
- /*
- * TSC2046 operates at a max freqency of 2MHz, so
- * operate slightly below at 1.5MHz
- */
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &tsc2046_mcspi_config,
- .irq = 0,
- .platform_data = &tsc2046_config,
- },
-};
-
-
#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 8
#define SDP3430_LCD_PANEL_ENABLE_GPIO 5
-static unsigned backlight_gpio;
-static unsigned enable_gpio;
+static struct gpio sdp3430_dss_gpios[] __initdata = {
+ {SDP3430_LCD_PANEL_ENABLE_GPIO, GPIOF_OUT_INIT_LOW, "LCD reset" },
+ {SDP3430_LCD_PANEL_BACKLIGHT_GPIO, GPIOF_OUT_INIT_LOW, "LCD Backlight"},
+};
+
static int lcd_enabled;
static int dvi_enabled;
@@ -187,29 +120,11 @@
{
int r;
- enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO;
- backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO;
+ r = gpio_request_array(sdp3430_dss_gpios,
+ ARRAY_SIZE(sdp3430_dss_gpios));
+ if (r)
+ printk(KERN_ERR "failed to get LCD control GPIOs\n");
- r = gpio_request(enable_gpio, "LCD reset");
- if (r) {
- printk(KERN_ERR "failed to get LCD reset GPIO\n");
- goto err0;
- }
-
- r = gpio_request(backlight_gpio, "LCD Backlight");
- if (r) {
- printk(KERN_ERR "failed to get LCD backlight GPIO\n");
- goto err1;
- }
-
- gpio_direction_output(enable_gpio, 0);
- gpio_direction_output(backlight_gpio, 0);
-
- return;
-err1:
- gpio_free(enable_gpio);
-err0:
- return;
}
static int sdp3430_panel_enable_lcd(struct omap_dss_device *dssdev)
@@ -219,8 +134,8 @@
return -EINVAL;
}
- gpio_direction_output(enable_gpio, 1);
- gpio_direction_output(backlight_gpio, 1);
+ gpio_direction_output(SDP3430_LCD_PANEL_ENABLE_GPIO, 1);
+ gpio_direction_output(SDP3430_LCD_PANEL_BACKLIGHT_GPIO, 1);
lcd_enabled = 1;
@@ -231,8 +146,8 @@
{
lcd_enabled = 0;
- gpio_direction_output(enable_gpio, 0);
- gpio_direction_output(backlight_gpio, 0);
+ gpio_direction_output(SDP3430_LCD_PANEL_ENABLE_GPIO, 0);
+ gpio_direction_output(SDP3430_LCD_PANEL_BACKLIGHT_GPIO, 0);
}
static int sdp3430_panel_enable_dvi(struct omap_dss_device *dssdev)
@@ -360,12 +275,10 @@
omap2_hsmmc_init(mmc);
/* gpio + 7 is "sub_lcd_en_bkl" (output/PWM1) */
- gpio_request(gpio + 7, "sub_lcd_en_bkl");
- gpio_direction_output(gpio + 7, 0);
+ gpio_request_one(gpio + 7, GPIOF_OUT_INIT_LOW, "sub_lcd_en_bkl");
/* gpio + 15 is "sub_lcd_nRST" (output) */
- gpio_request(gpio + 15, "sub_lcd_nRST");
- gpio_direction_output(gpio + 15, 0);
+ gpio_request_one(gpio + 15, GPIOF_OUT_INIT_LOW, "sub_lcd_nRST");
return 0;
}
@@ -580,20 +493,10 @@
.vpll2 = &sdp3430_vpll2,
};
-static struct i2c_board_info __initdata sdp3430_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &sdp3430_twldata,
- },
-};
-
static int __init omap3430_i2c_init(void)
{
/* i2c1 for PMIC only */
- omap_register_i2c_bus(1, 2600, sdp3430_i2c_boardinfo,
- ARRAY_SIZE(sdp3430_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &sdp3430_twldata);
/* i2c2 on camera connector (for sensor control) and optional isp1301 */
omap_register_i2c_bus(2, 400, NULL, 0);
/* i2c3 on display connector (for DVI, tfp410) */
@@ -872,30 +775,22 @@
},
};
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static void __init omap_3430sdp_init(void)
{
+ int gpio_pendown;
+
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
omap_board_config = sdp3430_config;
omap_board_config_size = ARRAY_SIZE(sdp3430_config);
- omap3_pm_init_cpuidle(omap3_cpuidle_params_table);
omap3430_i2c_init();
omap_display_init(&sdp3430_dss_data);
if (omap_rev() > OMAP3430_REV_ES1_0)
- ts_gpio = SDP3430_TS_GPIO_IRQ_SDPV2;
+ gpio_pendown = SDP3430_TS_GPIO_IRQ_SDPV2;
else
- ts_gpio = SDP3430_TS_GPIO_IRQ_SDPV1;
- sdp3430_spi_board_info[0].irq = gpio_to_irq(ts_gpio);
- spi_register_board_info(sdp3430_spi_board_info,
- ARRAY_SIZE(sdp3430_spi_board_info));
- ads7846_dev_init();
+ gpio_pendown = SDP3430_TS_GPIO_IRQ_SDPV1;
+ omap_ads7846_init(1, gpio_pendown, 310, NULL);
board_serial_init();
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
board_smc91x_init();
board_flash_init(sdp_flash_partitions, chip_sel_3430, 0);
sdp3430_display_init();
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 93edd7f..73fa90b 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -42,6 +42,7 @@
#include "hsmmc.h"
#include "timer-gp.h"
#include "control.h"
+#include "common-board-devices.h"
#define ETH_KS8851_IRQ 34
#define ETH_KS8851_POWER_ON 48
@@ -251,58 +252,22 @@
},
};
+static struct gpio sdp4430_eth_gpios[] __initdata = {
+ { ETH_KS8851_POWER_ON, GPIOF_OUT_INIT_HIGH, "eth_power" },
+ { ETH_KS8851_QUART, GPIOF_OUT_INIT_HIGH, "quart" },
+ { ETH_KS8851_IRQ, GPIOF_IN, "eth_irq" },
+};
+
static int omap_ethernet_init(void)
{
int status;
/* Request of GPIO lines */
+ status = gpio_request_array(sdp4430_eth_gpios,
+ ARRAY_SIZE(sdp4430_eth_gpios));
+ if (status)
+ pr_err("Cannot request ETH GPIOs\n");
- status = gpio_request(ETH_KS8851_POWER_ON, "eth_power");
- if (status) {
- pr_err("Cannot request GPIO %d\n", ETH_KS8851_POWER_ON);
- return status;
- }
-
- status = gpio_request(ETH_KS8851_QUART, "quart");
- if (status) {
- pr_err("Cannot request GPIO %d\n", ETH_KS8851_QUART);
- goto error1;
- }
-
- status = gpio_request(ETH_KS8851_IRQ, "eth_irq");
- if (status) {
- pr_err("Cannot request GPIO %d\n", ETH_KS8851_IRQ);
- goto error2;
- }
-
- /* Configuration of requested GPIO lines */
-
- status = gpio_direction_output(ETH_KS8851_POWER_ON, 1);
- if (status) {
- pr_err("Cannot set output GPIO %d\n", ETH_KS8851_IRQ);
- goto error3;
- }
-
- status = gpio_direction_output(ETH_KS8851_QUART, 1);
- if (status) {
- pr_err("Cannot set output GPIO %d\n", ETH_KS8851_QUART);
- goto error3;
- }
-
- status = gpio_direction_input(ETH_KS8851_IRQ);
- if (status) {
- pr_err("Cannot set input GPIO %d\n", ETH_KS8851_IRQ);
- goto error3;
- }
-
- return 0;
-
-error3:
- gpio_free(ETH_KS8851_IRQ);
-error2:
- gpio_free(ETH_KS8851_QUART);
-error1:
- gpio_free(ETH_KS8851_POWER_ON);
return status;
}
@@ -575,14 +540,6 @@
.usb = &omap4_usbphy_data
};
-static struct i2c_board_info __initdata sdp4430_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl6030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = OMAP44XX_IRQ_SYS_1N,
- .platform_data = &sdp4430_twldata,
- },
-};
static struct i2c_board_info __initdata sdp4430_i2c_3_boardinfo[] = {
{
I2C_BOARD_INFO("tmp105", 0x48),
@@ -598,12 +555,7 @@
};
static int __init omap4_i2c_init(void)
{
- /*
- * Phoenix Audio IC needs I2C1 to
- * start with 400 KHz or less
- */
- omap_register_i2c_bus(1, 400, sdp4430_i2c_boardinfo,
- ARRAY_SIZE(sdp4430_i2c_boardinfo));
+ omap4_pmic_init("twl6030", &sdp4430_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, sdp4430_i2c_3_boardinfo,
ARRAY_SIZE(sdp4430_i2c_3_boardinfo));
@@ -614,21 +566,13 @@
static void __init omap_sfh7741prox_init(void)
{
- int error;
+ int error;
- error = gpio_request(OMAP4_SFH7741_ENABLE_GPIO, "sfh7741");
- if (error < 0) {
+ error = gpio_request_one(OMAP4_SFH7741_ENABLE_GPIO,
+ GPIOF_OUT_INIT_LOW, "sfh7741");
+ if (error < 0)
pr_err("%s:failed to request GPIO %d, error %d\n",
__func__, OMAP4_SFH7741_ENABLE_GPIO, error);
- return;
- }
-
- error = gpio_direction_output(OMAP4_SFH7741_ENABLE_GPIO , 0);
- if (error < 0) {
- pr_err("%s: GPIO configuration failed: GPIO %d,error %d\n",
- __func__, OMAP4_SFH7741_ENABLE_GPIO, error);
- gpio_free(OMAP4_SFH7741_ENABLE_GPIO);
- }
}
static void sdp4430_hdmi_mux_init(void)
@@ -645,27 +589,19 @@
OMAP_PIN_INPUT_PULLUP);
}
+static struct gpio sdp4430_hdmi_gpios[] = {
+ { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" },
+ { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" },
+};
+
static int sdp4430_panel_enable_hdmi(struct omap_dss_device *dssdev)
{
int status;
- status = gpio_request_one(HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH,
- "hdmi_gpio_hpd");
- if (status) {
- pr_err("Cannot request GPIO %d\n", HDMI_GPIO_HPD);
- return status;
- }
- status = gpio_request_one(HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH,
- "hdmi_gpio_ls_oe");
- if (status) {
- pr_err("Cannot request GPIO %d\n", HDMI_GPIO_LS_OE);
- goto error1;
- }
-
- return 0;
-
-error1:
- gpio_free(HDMI_GPIO_HPD);
+ status = gpio_request_array(sdp4430_hdmi_gpios,
+ ARRAY_SIZE(sdp4430_hdmi_gpios));
+ if (status)
+ pr_err("%s: Cannot request HDMI GPIOs\n", __func__);
return status;
}
diff --git a/arch/arm/mach-omap2/board-am3517crane.c b/arch/arm/mach-omap2/board-am3517crane.c
index a890d24..5e438a7 100644
--- a/arch/arm/mach-omap2/board-am3517crane.c
+++ b/arch/arm/mach-omap2/board-am3517crane.c
@@ -89,19 +89,13 @@
return;
}
- ret = gpio_request(GPIO_USB_POWER, "usb_ehci_enable");
+ ret = gpio_request_one(GPIO_USB_POWER, GPIOF_OUT_INIT_HIGH,
+ "usb_ehci_enable");
if (ret < 0) {
pr_err("Can not request GPIO %d\n", GPIO_USB_POWER);
return;
}
- ret = gpio_direction_output(GPIO_USB_POWER, 1);
- if (ret < 0) {
- gpio_free(GPIO_USB_POWER);
- pr_err("Unable to initialize EHCI power\n");
- return;
- }
-
usbhs_init(&usbhs_bdata);
}
diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c
index ff8c59b..63af417 100644
--- a/arch/arm/mach-omap2/board-am3517evm.c
+++ b/arch/arm/mach-omap2/board-am3517evm.c
@@ -174,19 +174,14 @@
int r;
omap_mux_init_gpio(GPIO_RTCS35390A_IRQ, OMAP_PIN_INPUT_PULLUP);
- r = gpio_request(GPIO_RTCS35390A_IRQ, "rtcs35390a-irq");
+
+ r = gpio_request_one(GPIO_RTCS35390A_IRQ, GPIOF_IN, "rtcs35390a-irq");
if (r < 0) {
printk(KERN_WARNING "failed to request GPIO#%d\n",
GPIO_RTCS35390A_IRQ);
return;
}
- r = gpio_direction_input(GPIO_RTCS35390A_IRQ);
- if (r < 0) {
- printk(KERN_WARNING "GPIO#%d cannot be configured as input\n",
- GPIO_RTCS35390A_IRQ);
- gpio_free(GPIO_RTCS35390A_IRQ);
- return;
- }
+
am3517evm_i2c1_boardinfo[0].irq = gpio_to_irq(GPIO_RTCS35390A_IRQ);
}
@@ -242,6 +237,15 @@
#if defined(CONFIG_PANEL_SHARP_LQ043T1DG01) || \
defined(CONFIG_PANEL_SHARP_LQ043T1DG01_MODULE)
+static struct gpio am3517_evm_dss_gpios[] __initdata = {
+ /* GPIO 182 = LCD Backlight Power */
+ { LCD_PANEL_BKLIGHT_PWR, GPIOF_OUT_INIT_HIGH, "lcd_backlight_pwr" },
+ /* GPIO 181 = LCD Panel PWM */
+ { LCD_PANEL_PWM, GPIOF_OUT_INIT_HIGH, "lcd bl enable" },
+ /* GPIO 176 = LCD Panel Power enable pin */
+ { LCD_PANEL_PWR, GPIOF_OUT_INIT_HIGH, "dvi enable" },
+};
+
static void __init am3517_evm_display_init(void)
{
int r;
@@ -249,41 +253,15 @@
omap_mux_init_gpio(LCD_PANEL_PWR, OMAP_PIN_INPUT_PULLUP);
omap_mux_init_gpio(LCD_PANEL_BKLIGHT_PWR, OMAP_PIN_INPUT_PULLDOWN);
omap_mux_init_gpio(LCD_PANEL_PWM, OMAP_PIN_INPUT_PULLDOWN);
- /*
- * Enable GPIO 182 = LCD Backlight Power
- */
- r = gpio_request(LCD_PANEL_BKLIGHT_PWR, "lcd_backlight_pwr");
+
+ r = gpio_request_array(am3517_evm_dss_gpios,
+ ARRAY_SIZE(am3517_evm_dss_gpios));
if (r) {
- printk(KERN_ERR "failed to get lcd_backlight_pwr\n");
+ printk(KERN_ERR "failed to get DSS panel control GPIOs\n");
return;
}
- gpio_direction_output(LCD_PANEL_BKLIGHT_PWR, 1);
- /*
- * Enable GPIO 181 = LCD Panel PWM
- */
- r = gpio_request(LCD_PANEL_PWM, "lcd_pwm");
- if (r) {
- printk(KERN_ERR "failed to get lcd_pwm\n");
- goto err_1;
- }
- gpio_direction_output(LCD_PANEL_PWM, 1);
- /*
- * Enable GPIO 176 = LCD Panel Power enable pin
- */
- r = gpio_request(LCD_PANEL_PWR, "lcd_panel_pwr");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_pwr\n");
- goto err_2;
- }
- gpio_direction_output(LCD_PANEL_PWR, 1);
printk(KERN_INFO "Display initialized successfully\n");
- return;
-
-err_2:
- gpio_free(LCD_PANEL_PWM);
-err_1:
- gpio_free(LCD_PANEL_BKLIGHT_PWR);
}
#else
static void __init am3517_evm_display_init(void) {}
@@ -396,7 +374,7 @@
.power = 500,
.set_phy_power = am35x_musb_phy_power,
.clear_irq = am35x_musb_clear_irq,
- .set_mode = am35x_musb_set_mode,
+ .set_mode = am35x_set_mode,
.reset = am35x_musb_reset,
};
diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c
index f4f8374..f3beb8e 100644
--- a/arch/arm/mach-omap2/board-apollon.c
+++ b/arch/arm/mach-omap2/board-apollon.c
@@ -202,6 +202,7 @@
unsigned int rate;
struct clk *gpmc_fck;
int eth_cs;
+ int err;
gpmc_fck = clk_get(NULL, "gpmc_fck"); /* Always on ENABLE_ON_INIT */
if (IS_ERR(gpmc_fck)) {
@@ -245,15 +246,13 @@
apollon_smc91x_resources[0].end = base + 0x30f;
udelay(100);
- omap_mux_init_gpio(74, 0);
- if (gpio_request(APOLLON_ETHR_GPIO_IRQ, "SMC91x irq") < 0) {
+ omap_mux_init_gpio(APOLLON_ETHR_GPIO_IRQ, 0);
+ err = gpio_request_one(APOLLON_ETHR_GPIO_IRQ, GPIOF_IN, "SMC91x irq");
+ if (err) {
printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n",
APOLLON_ETHR_GPIO_IRQ);
gpmc_cs_free(APOLLON_ETH_CS);
- goto out;
}
- gpio_direction_input(APOLLON_ETHR_GPIO_IRQ);
-
out:
clk_disable(gpmc_fck);
clk_put(gpmc_fck);
@@ -280,20 +279,19 @@
omap2_init_common_devices(NULL, NULL);
}
+static struct gpio apollon_gpio_leds[] __initdata = {
+ { LED0_GPIO13, GPIOF_OUT_INIT_LOW, "LED0" }, /* LED0 - AA10 */
+ { LED1_GPIO14, GPIOF_OUT_INIT_LOW, "LED1" }, /* LED1 - AA6 */
+ { LED2_GPIO15, GPIOF_OUT_INIT_LOW, "LED2" }, /* LED2 - AA4 */
+};
+
static void __init apollon_led_init(void)
{
- /* LED0 - AA10 */
omap_mux_init_signal("vlynq_clk.gpio_13", 0);
- gpio_request(LED0_GPIO13, "LED0");
- gpio_direction_output(LED0_GPIO13, 0);
- /* LED1 - AA6 */
omap_mux_init_signal("vlynq_rx1.gpio_14", 0);
- gpio_request(LED1_GPIO14, "LED1");
- gpio_direction_output(LED1_GPIO14, 0);
- /* LED2 - AA4 */
omap_mux_init_signal("vlynq_rx0.gpio_15", 0);
- gpio_request(LED2_GPIO15, "LED2");
- gpio_direction_output(LED2_GPIO15, 0);
+
+ gpio_request_array(apollon_gpio_leds, ARRAY_SIZE(apollon_gpio_leds));
}
static void __init apollon_usb_init(void)
@@ -301,8 +299,7 @@
/* USB device */
/* DEVICE_SUSPEND */
omap_mux_init_signal("mcbsp2_clkx.gpio_12", 0);
- gpio_request(12, "USB suspend");
- gpio_direction_output(12, 0);
+ gpio_request_one(12, GPIOF_OUT_INIT_LOW, "USB suspend");
omap2_usbfs_init(&apollon_usb_config);
}
diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c
index 9340f6a..c63115b 100644
--- a/arch/arm/mach-omap2/board-cm-t35.c
+++ b/arch/arm/mach-omap2/board-cm-t35.c
@@ -54,6 +54,7 @@
#include "mux.h"
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define CM_T35_GPIO_PENDOWN 57
@@ -66,86 +67,28 @@
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
#include <linux/smsc911x.h>
+#include <plat/gpmc-smsc911x.h>
-static struct smsc911x_platform_config cm_t35_smsc911x_config = {
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
- .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS,
- .phy_interface = PHY_INTERFACE_MODE_MII,
-};
-
-static struct resource cm_t35_smsc911x_resources[] = {
- {
- .flags = IORESOURCE_MEM,
- },
- {
- .start = OMAP_GPIO_IRQ(CM_T35_SMSC911X_GPIO),
- .end = OMAP_GPIO_IRQ(CM_T35_SMSC911X_GPIO),
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct platform_device cm_t35_smsc911x_device = {
- .name = "smsc911x",
+static struct omap_smsc911x_platform_data cm_t35_smsc911x_cfg = {
.id = 0,
- .num_resources = ARRAY_SIZE(cm_t35_smsc911x_resources),
- .resource = cm_t35_smsc911x_resources,
- .dev = {
- .platform_data = &cm_t35_smsc911x_config,
- },
+ .cs = CM_T35_SMSC911X_CS,
+ .gpio_irq = CM_T35_SMSC911X_GPIO,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS,
};
-static struct resource sb_t35_smsc911x_resources[] = {
- {
- .flags = IORESOURCE_MEM,
- },
- {
- .start = OMAP_GPIO_IRQ(SB_T35_SMSC911X_GPIO),
- .end = OMAP_GPIO_IRQ(SB_T35_SMSC911X_GPIO),
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct platform_device sb_t35_smsc911x_device = {
- .name = "smsc911x",
+static struct omap_smsc911x_platform_data sb_t35_smsc911x_cfg = {
.id = 1,
- .num_resources = ARRAY_SIZE(sb_t35_smsc911x_resources),
- .resource = sb_t35_smsc911x_resources,
- .dev = {
- .platform_data = &cm_t35_smsc911x_config,
- },
+ .cs = SB_T35_SMSC911X_CS,
+ .gpio_irq = SB_T35_SMSC911X_GPIO,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS,
};
-static void __init cm_t35_init_smsc911x(struct platform_device *dev,
- int cs, int irq_gpio)
-{
- unsigned long cs_mem_base;
-
- if (gpmc_cs_request(cs, SZ_16M, &cs_mem_base) < 0) {
- pr_err("CM-T35: Failed request for GPMC mem for smsc911x\n");
- return;
- }
-
- dev->resource[0].start = cs_mem_base + 0x0;
- dev->resource[0].end = cs_mem_base + 0xff;
-
- if ((gpio_request(irq_gpio, "ETH IRQ") == 0) &&
- (gpio_direction_input(irq_gpio) == 0)) {
- gpio_export(irq_gpio, 0);
- } else {
- pr_err("CM-T35: could not obtain gpio for SMSC911X IRQ\n");
- return;
- }
-
- platform_device_register(dev);
-}
-
static void __init cm_t35_init_ethernet(void)
{
- cm_t35_init_smsc911x(&cm_t35_smsc911x_device,
- CM_T35_SMSC911X_CS, CM_T35_SMSC911X_GPIO);
- cm_t35_init_smsc911x(&sb_t35_smsc911x_device,
- SB_T35_SMSC911X_CS, SB_T35_SMSC911X_GPIO);
+ gpmc_smsc911x_init(&cm_t35_smsc911x_cfg);
+ gpmc_smsc911x_init(&sb_t35_smsc911x_cfg);
}
#else
static inline void __init cm_t35_init_ethernet(void) { return; }
@@ -235,69 +178,10 @@
static inline void cm_t35_init_nand(void) {}
#endif
-#if defined(CONFIG_TOUCHSCREEN_ADS7846) || \
- defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
-#include <linux/spi/ads7846.h>
-
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(CM_T35_GPIO_PENDOWN);
-}
-
-static struct ads7846_platform_data ads7846_config = {
- .x_max = 0x0fff,
- .y_max = 0x0fff,
- .x_plate_ohms = 180,
- .pressure_max = 255,
- .debounce_max = 10,
- .debounce_tol = 3,
- .debounce_rep = 1,
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
-};
-
-static struct spi_board_info cm_t35_spi_board_info[] __initdata = {
- {
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(CM_T35_GPIO_PENDOWN),
- .platform_data = &ads7846_config,
- },
-};
-
-static void __init cm_t35_init_ads7846(void)
-{
- if ((gpio_request(CM_T35_GPIO_PENDOWN, "ADS7846_PENDOWN") == 0) &&
- (gpio_direction_input(CM_T35_GPIO_PENDOWN) == 0)) {
- gpio_export(CM_T35_GPIO_PENDOWN, 0);
- } else {
- pr_err("CM-T35: could not obtain gpio for ADS7846_PENDOWN\n");
- return;
- }
-
- spi_register_board_info(cm_t35_spi_board_info,
- ARRAY_SIZE(cm_t35_spi_board_info));
-}
-#else
-static inline void cm_t35_init_ads7846(void) {}
-#endif
-
#define CM_T35_LCD_EN_GPIO 157
#define CM_T35_LCD_BL_GPIO 58
#define CM_T35_DVI_EN_GPIO 54
-static int lcd_bl_gpio;
-static int lcd_en_gpio;
-static int dvi_en_gpio;
-
static int lcd_enabled;
static int dvi_enabled;
@@ -308,8 +192,8 @@
return -EINVAL;
}
- gpio_set_value(lcd_en_gpio, 1);
- gpio_set_value(lcd_bl_gpio, 1);
+ gpio_set_value(CM_T35_LCD_EN_GPIO, 1);
+ gpio_set_value(CM_T35_LCD_BL_GPIO, 1);
lcd_enabled = 1;
@@ -320,8 +204,8 @@
{
lcd_enabled = 0;
- gpio_set_value(lcd_bl_gpio, 0);
- gpio_set_value(lcd_en_gpio, 0);
+ gpio_set_value(CM_T35_LCD_BL_GPIO, 0);
+ gpio_set_value(CM_T35_LCD_EN_GPIO, 0);
}
static int cm_t35_panel_enable_dvi(struct omap_dss_device *dssdev)
@@ -331,7 +215,7 @@
return -EINVAL;
}
- gpio_set_value(dvi_en_gpio, 0);
+ gpio_set_value(CM_T35_DVI_EN_GPIO, 0);
dvi_enabled = 1;
return 0;
@@ -339,7 +223,7 @@
static void cm_t35_panel_disable_dvi(struct omap_dss_device *dssdev)
{
- gpio_set_value(dvi_en_gpio, 1);
+ gpio_set_value(CM_T35_DVI_EN_GPIO, 1);
dvi_enabled = 0;
}
@@ -421,62 +305,38 @@
},
};
+static struct gpio cm_t35_dss_gpios[] __initdata = {
+ { CM_T35_LCD_EN_GPIO, GPIOF_OUT_INIT_LOW, "lcd enable" },
+ { CM_T35_LCD_BL_GPIO, GPIOF_OUT_INIT_LOW, "lcd bl enable" },
+ { CM_T35_DVI_EN_GPIO, GPIOF_OUT_INIT_HIGH, "dvi enable" },
+};
+
static void __init cm_t35_init_display(void)
{
int err;
- lcd_en_gpio = CM_T35_LCD_EN_GPIO;
- lcd_bl_gpio = CM_T35_LCD_BL_GPIO;
- dvi_en_gpio = CM_T35_DVI_EN_GPIO;
-
spi_register_board_info(cm_t35_lcd_spi_board_info,
ARRAY_SIZE(cm_t35_lcd_spi_board_info));
- err = gpio_request(lcd_en_gpio, "LCD RST");
+ err = gpio_request_array(cm_t35_dss_gpios,
+ ARRAY_SIZE(cm_t35_dss_gpios));
if (err) {
- pr_err("CM-T35: failed to get LCD reset GPIO\n");
- goto out;
+ pr_err("CM-T35: failed to request DSS control GPIOs\n");
+ return;
}
- err = gpio_request(lcd_bl_gpio, "LCD BL");
- if (err) {
- pr_err("CM-T35: failed to get LCD backlight control GPIO\n");
- goto err_lcd_bl;
- }
-
- err = gpio_request(dvi_en_gpio, "DVI EN");
- if (err) {
- pr_err("CM-T35: failed to get DVI reset GPIO\n");
- goto err_dvi_en;
- }
-
- gpio_export(lcd_en_gpio, 0);
- gpio_export(lcd_bl_gpio, 0);
- gpio_export(dvi_en_gpio, 0);
- gpio_direction_output(lcd_en_gpio, 0);
- gpio_direction_output(lcd_bl_gpio, 0);
- gpio_direction_output(dvi_en_gpio, 1);
+ gpio_export(CM_T35_LCD_EN_GPIO, 0);
+ gpio_export(CM_T35_LCD_BL_GPIO, 0);
+ gpio_export(CM_T35_DVI_EN_GPIO, 0);
msleep(50);
- gpio_set_value(lcd_en_gpio, 1);
+ gpio_set_value(CM_T35_LCD_EN_GPIO, 1);
err = omap_display_init(&cm_t35_dss_data);
if (err) {
pr_err("CM-T35: failed to register DSS device\n");
- goto err_dev_reg;
+ gpio_free_array(cm_t35_dss_gpios, ARRAY_SIZE(cm_t35_dss_gpios));
}
-
- return;
-
-err_dev_reg:
- gpio_free(dvi_en_gpio);
-err_dvi_en:
- gpio_free(lcd_bl_gpio);
-err_lcd_bl:
- gpio_free(lcd_en_gpio);
-out:
-
- return;
}
static struct regulator_consumer_supply cm_t35_vmmc1_supply = {
@@ -609,10 +469,8 @@
{
int wlan_rst = gpio + 2;
- if ((gpio_request(wlan_rst, "WLAN RST") == 0) &&
- (gpio_direction_output(wlan_rst, 1) == 0)) {
+ if (gpio_request_one(wlan_rst, GPIOF_OUT_INIT_HIGH, "WLAN RST") == 0) {
gpio_export(wlan_rst, 0);
-
udelay(10);
gpio_set_value(wlan_rst, 0);
udelay(10);
@@ -653,19 +511,9 @@
.vpll2 = &cm_t35_vpll2,
};
-static struct i2c_board_info __initdata cm_t35_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("tps65930", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &cm_t35_twldata,
- },
-};
-
static void __init cm_t35_init_i2c(void)
{
- omap_register_i2c_bus(1, 2600, cm_t35_i2c_boardinfo,
- ARRAY_SIZE(cm_t35_i2c_boardinfo));
+ omap3_pmic_init("tps65930", &cm_t35_twldata);
}
static void __init cm_t35_init_early(void)
@@ -775,12 +623,6 @@
};
#endif
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static struct omap_board_config_kernel cm_t35_config[] __initdata = {
};
@@ -792,12 +634,12 @@
omap_serial_init();
cm_t35_init_i2c();
cm_t35_init_nand();
- cm_t35_init_ads7846();
+ omap_ads7846_init(1, CM_T35_GPIO_PENDOWN, 0, NULL);
cm_t35_init_ethernet();
cm_t35_init_led();
cm_t35_init_display();
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
}
diff --git a/arch/arm/mach-omap2/board-cm-t3517.c b/arch/arm/mach-omap2/board-cm-t3517.c
index a27e3ee..08f08e8 100644
--- a/arch/arm/mach-omap2/board-cm-t3517.c
+++ b/arch/arm/mach-omap2/board-cm-t3517.c
@@ -148,14 +148,13 @@
{
int err;
- err = gpio_request(RTC_CS_EN_GPIO, "rtc cs en");
+ err = gpio_request_one(RTC_CS_EN_GPIO, GPIOF_OUT_INIT_HIGH,
+ "rtc cs en");
if (err) {
pr_err("CM-T3517: rtc cs en gpio request failed: %d\n", err);
return;
}
- gpio_direction_output(RTC_CS_EN_GPIO, 1);
-
platform_device_register(&cm_t3517_rtc_device);
}
#else
@@ -182,11 +181,11 @@
{
int err;
- err = gpio_request(USB_HUB_RESET_GPIO, "usb hub rst");
+ err = gpio_request_one(USB_HUB_RESET_GPIO, GPIOF_OUT_INIT_LOW,
+ "usb hub rst");
if (err) {
pr_err("CM-T3517: usb hub rst gpio request failed: %d\n", err);
} else {
- gpio_direction_output(USB_HUB_RESET_GPIO, 0);
udelay(10);
gpio_set_value(USB_HUB_RESET_GPIO, 1);
msleep(1);
diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c
index 1d1b56a..cf520d7 100644
--- a/arch/arm/mach-omap2/board-devkit8000.c
+++ b/arch/arm/mach-omap2/board-devkit8000.c
@@ -51,7 +51,6 @@
#include <plat/mcspi.h>
#include <linux/input/matrix_keypad.h>
#include <linux/spi/spi.h>
-#include <linux/spi/ads7846.h>
#include <linux/dm9000.h>
#include <linux/interrupt.h>
@@ -60,6 +59,7 @@
#include "mux.h"
#include "hsmmc.h"
#include "timer-gp.h"
+#include "common-board-devices.h"
#define NAND_BLOCK_SIZE SZ_128K
@@ -97,13 +97,6 @@
},
};
-static struct omap_nand_platform_data devkit8000_nand_data = {
- .options = NAND_BUSWIDTH_16,
- .parts = devkit8000_nand_partitions,
- .nr_parts = ARRAY_SIZE(devkit8000_nand_partitions),
- .dma_channel = -1, /* disable DMA in OMAP NAND driver */
-};
-
static struct omap2_hsmmc_info mmc[] = {
{
.mmc = 1,
@@ -249,7 +242,7 @@
/* TWL4030_GPIO_MAX + 0 is "LCD_PWREN" (out, active high) */
devkit8000_lcd_device.reset_gpio = gpio + TWL4030_GPIO_MAX + 0;
ret = gpio_request_one(devkit8000_lcd_device.reset_gpio,
- GPIOF_DIR_OUT | GPIOF_INIT_LOW, "LCD_PWREN");
+ GPIOF_OUT_INIT_LOW, "LCD_PWREN");
if (ret < 0) {
devkit8000_lcd_device.reset_gpio = -EINVAL;
printk(KERN_ERR "Failed to request GPIO for LCD_PWRN\n");
@@ -258,7 +251,7 @@
/* gpio + 7 is "DVI_PD" (out, active low) */
devkit8000_dvi_device.reset_gpio = gpio + 7;
ret = gpio_request_one(devkit8000_dvi_device.reset_gpio,
- GPIOF_DIR_OUT | GPIOF_INIT_LOW, "DVI PowerDown");
+ GPIOF_OUT_INIT_LOW, "DVI PowerDown");
if (ret < 0) {
devkit8000_dvi_device.reset_gpio = -EINVAL;
printk(KERN_ERR "Failed to request GPIO for DVI PowerDown\n");
@@ -366,19 +359,9 @@
.keypad = &devkit8000_kp_data,
};
-static struct i2c_board_info __initdata devkit8000_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("tps65930", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &devkit8000_twldata,
- },
-};
-
static int __init devkit8000_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, devkit8000_i2c_boardinfo,
- ARRAY_SIZE(devkit8000_i2c_boardinfo));
+ omap3_pmic_init("tps65930", &devkit8000_twldata);
/* Bus 3 is attached to the DVI port where devices like the pico DLP
* projector don't work reliably with 400kHz */
omap_register_i2c_bus(3, 400, NULL, 0);
@@ -463,56 +446,6 @@
#endif
}
-static void __init devkit8000_ads7846_init(void)
-{
- int gpio = OMAP3_DEVKIT_TS_GPIO;
- int ret;
-
- ret = gpio_request(gpio, "ads7846_pen_down");
- if (ret < 0) {
- printk(KERN_ERR "Failed to request GPIO %d for "
- "ads7846 pen down IRQ\n", gpio);
- return;
- }
-
- gpio_direction_input(gpio);
-}
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(OMAP3_DEVKIT_TS_GPIO);
-}
-
-static struct ads7846_platform_data ads7846_config = {
- .x_max = 0x0fff,
- .y_max = 0x0fff,
- .x_plate_ohms = 180,
- .pressure_max = 255,
- .debounce_max = 10,
- .debounce_tol = 5,
- .debounce_rep = 1,
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
- .settle_delay_usecs = 150,
-};
-
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static struct spi_board_info devkit8000_spi_board_info[] __initdata = {
- {
- .modalias = "ads7846",
- .bus_num = 2,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(OMAP3_DEVKIT_TS_GPIO),
- .platform_data = &ads7846_config,
- }
-};
-
#define OMAP_DM9000_BASE 0x2c000000
static struct resource omap_dm9000_resources[] = {
@@ -550,14 +483,14 @@
{
unsigned char *eth_addr = omap_dm9000_platdata.dev_addr;
struct omap_die_id odi;
+ int ret;
- if (gpio_request(OMAP_DM9000_GPIO_IRQ, "dm9000 irq") < 0) {
+ ret = gpio_request_one(OMAP_DM9000_GPIO_IRQ, GPIOF_IN, "dm9000 irq");
+ if (ret < 0) {
printk(KERN_ERR "Failed to request GPIO%d for dm9000 IRQ\n",
OMAP_DM9000_GPIO_IRQ);
return;
- }
-
- gpio_direction_input(OMAP_DM9000_GPIO_IRQ);
+ }
/* init the mac address using DIE id */
omap_get_die_id(&odi);
@@ -576,45 +509,6 @@
&omap_dm9000_dev,
};
-static void __init devkit8000_flash_init(void)
-{
- u8 cs = 0;
- u8 nandcs = GPMC_CS_NUM + 1;
-
- /* find out the chip-select on which NAND exists */
- while (cs < GPMC_CS_NUM) {
- u32 ret = 0;
- ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
-
- if ((ret & 0xC00) == 0x800) {
- printk(KERN_INFO "Found NAND on CS%d\n", cs);
- if (nandcs > GPMC_CS_NUM)
- nandcs = cs;
- }
- cs++;
- }
-
- if (nandcs > GPMC_CS_NUM) {
- printk(KERN_INFO "NAND: Unable to find configuration "
- "in GPMC\n ");
- return;
- }
-
- if (nandcs < GPMC_CS_NUM) {
- devkit8000_nand_data.cs = nandcs;
-
- printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
- if (gpmc_nand_init(&devkit8000_nand_data) < 0)
- printk(KERN_ERR "Unable to register NAND device\n");
- }
-}
-
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
@@ -795,14 +689,13 @@
ARRAY_SIZE(devkit8000_devices));
omap_display_init(&devkit8000_dss_data);
- spi_register_board_info(devkit8000_spi_board_info,
- ARRAY_SIZE(devkit8000_spi_board_info));
- devkit8000_ads7846_init();
+ omap_ads7846_init(2, OMAP3_DEVKIT_TS_GPIO, 0, NULL);
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
- devkit8000_flash_init();
+ omap_nand_flash_init(NAND_BUSWIDTH_16, devkit8000_nand_partitions,
+ ARRAY_SIZE(devkit8000_nand_partitions));
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c
index 3da64d3..0c1bfca 100644
--- a/arch/arm/mach-omap2/board-igep0020.c
+++ b/arch/arm/mach-omap2/board-igep0020.c
@@ -38,6 +38,7 @@
#include "mux.h"
#include "hsmmc.h"
#include "sdram-numonyx-m65kxxxxam.h"
+#include "common-board-devices.h"
#define IGEP2_SMSC911X_CS 5
#define IGEP2_SMSC911X_GPIO 176
@@ -54,6 +55,11 @@
#define IGEP2_RC_GPIO_WIFI_NRESET 139
#define IGEP2_RC_GPIO_BT_NRESET 137
+#define IGEP3_GPIO_LED0_GREEN 54
+#define IGEP3_GPIO_LED0_RED 53
+#define IGEP3_GPIO_LED1_RED 16
+#define IGEP3_GPIO_USBH_NRESET 183
+
/*
* IGEP2 Hardware Revision Table
*
@@ -68,6 +74,7 @@
#define IGEP2_BOARD_HWREV_B 0
#define IGEP2_BOARD_HWREV_C 1
+#define IGEP3_BOARD_HWREV 2
static u8 hwrev;
@@ -75,24 +82,29 @@
{
u8 ret;
+ if (machine_is_igep0030()) {
+ hwrev = IGEP3_BOARD_HWREV;
+ return;
+ }
+
omap_mux_init_gpio(IGEP2_GPIO_LED1_RED, OMAP_PIN_INPUT);
- if ((gpio_request(IGEP2_GPIO_LED1_RED, "GPIO_HW0_REV") == 0) &&
- (gpio_direction_input(IGEP2_GPIO_LED1_RED) == 0)) {
- ret = gpio_get_value(IGEP2_GPIO_LED1_RED);
- if (ret == 0) {
- pr_info("IGEP2: Hardware Revision C (B-NON compatible)\n");
- hwrev = IGEP2_BOARD_HWREV_C;
- } else if (ret == 1) {
- pr_info("IGEP2: Hardware Revision B/C (B compatible)\n");
- hwrev = IGEP2_BOARD_HWREV_B;
- } else {
- pr_err("IGEP2: Unknown Hardware Revision\n");
- hwrev = -1;
- }
- } else {
+ if (gpio_request_one(IGEP2_GPIO_LED1_RED, GPIOF_IN, "GPIO_HW0_REV")) {
pr_warning("IGEP2: Could not obtain gpio GPIO_HW0_REV\n");
pr_err("IGEP2: Unknown Hardware Revision\n");
+ return;
+ }
+
+ ret = gpio_get_value(IGEP2_GPIO_LED1_RED);
+ if (ret == 0) {
+ pr_info("IGEP2: Hardware Revision C (B-NON compatible)\n");
+ hwrev = IGEP2_BOARD_HWREV_C;
+ } else if (ret == 1) {
+ pr_info("IGEP2: Hardware Revision B/C (B compatible)\n");
+ hwrev = IGEP2_BOARD_HWREV_B;
+ } else {
+ pr_err("IGEP2: Unknown Hardware Revision\n");
+ hwrev = -1;
}
gpio_free(IGEP2_GPIO_LED1_RED);
@@ -111,7 +123,7 @@
* So MTD regards it as 4KiB page size and 256KiB block size 64*(2*2048)
*/
-static struct mtd_partition igep2_onenand_partitions[] = {
+static struct mtd_partition igep_onenand_partitions[] = {
{
.name = "X-Loader",
.offset = 0,
@@ -139,21 +151,21 @@
},
};
-static struct omap_onenand_platform_data igep2_onenand_data = {
- .parts = igep2_onenand_partitions,
- .nr_parts = ARRAY_SIZE(igep2_onenand_partitions),
+static struct omap_onenand_platform_data igep_onenand_data = {
+ .parts = igep_onenand_partitions,
+ .nr_parts = ARRAY_SIZE(igep_onenand_partitions),
.dma_channel = -1, /* disable DMA in OMAP OneNAND driver */
};
-static struct platform_device igep2_onenand_device = {
+static struct platform_device igep_onenand_device = {
.name = "omap2-onenand",
.id = -1,
.dev = {
- .platform_data = &igep2_onenand_data,
+ .platform_data = &igep_onenand_data,
},
};
-static void __init igep2_flash_init(void)
+static void __init igep_flash_init(void)
{
u8 cs = 0;
u8 onenandcs = GPMC_CS_NUM + 1;
@@ -165,7 +177,7 @@
/* Check if NAND/oneNAND is configured */
if ((ret & 0xC00) == 0x800)
/* NAND found */
- pr_err("IGEP2: Unsupported NAND found\n");
+ pr_err("IGEP: Unsupported NAND found\n");
else {
ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
if ((ret & 0x3F) == (ONENAND_MAP >> 24))
@@ -175,85 +187,46 @@
}
if (onenandcs > GPMC_CS_NUM) {
- pr_err("IGEP2: Unable to find configuration in GPMC\n");
+ pr_err("IGEP: Unable to find configuration in GPMC\n");
return;
}
- igep2_onenand_data.cs = onenandcs;
+ igep_onenand_data.cs = onenandcs;
- if (platform_device_register(&igep2_onenand_device) < 0)
- pr_err("IGEP2: Unable to register OneNAND device\n");
+ if (platform_device_register(&igep_onenand_device) < 0)
+ pr_err("IGEP: Unable to register OneNAND device\n");
}
#else
-static void __init igep2_flash_init(void) {}
+static void __init igep_flash_init(void) {}
#endif
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
#include <linux/smsc911x.h>
+#include <plat/gpmc-smsc911x.h>
-static struct smsc911x_platform_config igep2_smsc911x_config = {
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
- .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS ,
- .phy_interface = PHY_INTERFACE_MODE_MII,
-};
-
-static struct resource igep2_smsc911x_resources[] = {
- {
- .flags = IORESOURCE_MEM,
- },
- {
- .start = OMAP_GPIO_IRQ(IGEP2_SMSC911X_GPIO),
- .end = OMAP_GPIO_IRQ(IGEP2_SMSC911X_GPIO),
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct platform_device igep2_smsc911x_device = {
- .name = "smsc911x",
- .id = 0,
- .num_resources = ARRAY_SIZE(igep2_smsc911x_resources),
- .resource = igep2_smsc911x_resources,
- .dev = {
- .platform_data = &igep2_smsc911x_config,
- },
+static struct omap_smsc911x_platform_data smsc911x_cfg = {
+ .cs = IGEP2_SMSC911X_CS,
+ .gpio_irq = IGEP2_SMSC911X_GPIO,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS,
};
static inline void __init igep2_init_smsc911x(void)
{
- unsigned long cs_mem_base;
-
- if (gpmc_cs_request(IGEP2_SMSC911X_CS, SZ_16M, &cs_mem_base) < 0) {
- pr_err("IGEP v2: Failed request for GPMC mem for smsc911x\n");
- gpmc_cs_free(IGEP2_SMSC911X_CS);
- return;
- }
-
- igep2_smsc911x_resources[0].start = cs_mem_base + 0x0;
- igep2_smsc911x_resources[0].end = cs_mem_base + 0xff;
-
- if ((gpio_request(IGEP2_SMSC911X_GPIO, "SMSC911X IRQ") == 0) &&
- (gpio_direction_input(IGEP2_SMSC911X_GPIO) == 0)) {
- gpio_export(IGEP2_SMSC911X_GPIO, 0);
- } else {
- pr_err("IGEP v2: Could not obtain gpio for for SMSC911X IRQ\n");
- return;
- }
-
- platform_device_register(&igep2_smsc911x_device);
+ gpmc_smsc911x_init(&smsc911x_cfg);
}
#else
static inline void __init igep2_init_smsc911x(void) { }
#endif
-static struct regulator_consumer_supply igep2_vmmc1_supply =
+static struct regulator_consumer_supply igep_vmmc1_supply =
REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0");
/* VMMC1 for OMAP VDD_MMC1 (i/o) and MMC1 card */
-static struct regulator_init_data igep2_vmmc1 = {
+static struct regulator_init_data igep_vmmc1 = {
.constraints = {
.min_uV = 1850000,
.max_uV = 3150000,
@@ -264,13 +237,13 @@
| REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = 1,
- .consumer_supplies = &igep2_vmmc1_supply,
+ .consumer_supplies = &igep_vmmc1_supply,
};
-static struct regulator_consumer_supply igep2_vio_supply =
+static struct regulator_consumer_supply igep_vio_supply =
REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.1");
-static struct regulator_init_data igep2_vio = {
+static struct regulator_init_data igep_vio = {
.constraints = {
.min_uV = 1800000,
.max_uV = 1800000,
@@ -282,34 +255,34 @@
| REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = 1,
- .consumer_supplies = &igep2_vio_supply,
+ .consumer_supplies = &igep_vio_supply,
};
-static struct regulator_consumer_supply igep2_vmmc2_supply =
+static struct regulator_consumer_supply igep_vmmc2_supply =
REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1");
-static struct regulator_init_data igep2_vmmc2 = {
+static struct regulator_init_data igep_vmmc2 = {
.constraints = {
.valid_modes_mask = REGULATOR_MODE_NORMAL,
.always_on = 1,
},
.num_consumer_supplies = 1,
- .consumer_supplies = &igep2_vmmc2_supply,
+ .consumer_supplies = &igep_vmmc2_supply,
};
-static struct fixed_voltage_config igep2_vwlan = {
+static struct fixed_voltage_config igep_vwlan = {
.supply_name = "vwlan",
.microvolts = 3300000,
.gpio = -EINVAL,
.enabled_at_boot = 1,
- .init_data = &igep2_vmmc2,
+ .init_data = &igep_vmmc2,
};
-static struct platform_device igep2_vwlan_device = {
+static struct platform_device igep_vwlan_device = {
.name = "reg-fixed-voltage",
.id = 0,
.dev = {
- .platform_data = &igep2_vwlan,
+ .platform_data = &igep_vwlan,
},
};
@@ -334,20 +307,17 @@
#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
#include <linux/leds.h>
-static struct gpio_led igep2_gpio_leds[] = {
+static struct gpio_led igep_gpio_leds[] = {
[0] = {
.name = "gpio-led:red:d0",
- .gpio = IGEP2_GPIO_LED0_RED,
.default_trigger = "default-off"
},
[1] = {
.name = "gpio-led:green:d0",
- .gpio = IGEP2_GPIO_LED0_GREEN,
.default_trigger = "default-off",
},
[2] = {
.name = "gpio-led:red:d1",
- .gpio = IGEP2_GPIO_LED1_RED,
.default_trigger = "default-off",
},
[3] = {
@@ -358,94 +328,119 @@
},
};
-static struct gpio_led_platform_data igep2_led_pdata = {
- .leds = igep2_gpio_leds,
- .num_leds = ARRAY_SIZE(igep2_gpio_leds),
+static struct gpio_led_platform_data igep_led_pdata = {
+ .leds = igep_gpio_leds,
+ .num_leds = ARRAY_SIZE(igep_gpio_leds),
};
-static struct platform_device igep2_led_device = {
+static struct platform_device igep_led_device = {
.name = "leds-gpio",
.id = -1,
.dev = {
- .platform_data = &igep2_led_pdata,
+ .platform_data = &igep_led_pdata,
},
};
-static void __init igep2_leds_init(void)
+static void __init igep_leds_init(void)
{
- platform_device_register(&igep2_led_device);
+ if (machine_is_igep0020()) {
+ igep_gpio_leds[0].gpio = IGEP2_GPIO_LED0_RED;
+ igep_gpio_leds[1].gpio = IGEP2_GPIO_LED0_GREEN;
+ igep_gpio_leds[2].gpio = IGEP2_GPIO_LED1_RED;
+ } else {
+ igep_gpio_leds[0].gpio = IGEP3_GPIO_LED0_RED;
+ igep_gpio_leds[1].gpio = IGEP3_GPIO_LED0_GREEN;
+ igep_gpio_leds[2].gpio = IGEP3_GPIO_LED1_RED;
+ }
+
+ platform_device_register(&igep_led_device);
}
#else
-static inline void igep2_leds_init(void)
+static struct gpio igep_gpio_leds[] __initdata = {
+ { -EINVAL, GPIOF_OUT_INIT_LOW, "gpio-led:red:d0" },
+ { -EINVAL, GPIOF_OUT_INIT_LOW, "gpio-led:green:d0" },
+ { -EINVAL, GPIOF_OUT_INIT_LOW, "gpio-led:red:d1" },
+};
+
+static inline void igep_leds_init(void)
{
- if ((gpio_request(IGEP2_GPIO_LED0_RED, "gpio-led:red:d0") == 0) &&
- (gpio_direction_output(IGEP2_GPIO_LED0_RED, 0) == 0))
- gpio_export(IGEP2_GPIO_LED0_RED, 0);
- else
- pr_warning("IGEP v2: Could not obtain gpio GPIO_LED0_RED\n");
+ int i;
- if ((gpio_request(IGEP2_GPIO_LED0_GREEN, "gpio-led:green:d0") == 0) &&
- (gpio_direction_output(IGEP2_GPIO_LED0_GREEN, 0) == 0))
- gpio_export(IGEP2_GPIO_LED0_GREEN, 0);
- else
- pr_warning("IGEP v2: Could not obtain gpio GPIO_LED0_GREEN\n");
+ if (machine_is_igep0020()) {
+ igep_gpio_leds[0].gpio = IGEP2_GPIO_LED0_RED;
+ igep_gpio_leds[1].gpio = IGEP2_GPIO_LED0_GREEN;
+ igep_gpio_leds[2].gpio = IGEP2_GPIO_LED1_RED;
+ } else {
+ igep_gpio_leds[0].gpio = IGEP3_GPIO_LED0_RED;
+ igep_gpio_leds[1].gpio = IGEP3_GPIO_LED0_GREEN;
+ igep_gpio_leds[2].gpio = IGEP3_GPIO_LED1_RED;
+ }
- if ((gpio_request(IGEP2_GPIO_LED1_RED, "gpio-led:red:d1") == 0) &&
- (gpio_direction_output(IGEP2_GPIO_LED1_RED, 0) == 0))
- gpio_export(IGEP2_GPIO_LED1_RED, 0);
- else
- pr_warning("IGEP v2: Could not obtain gpio GPIO_LED1_RED\n");
+ if (gpio_request_array(igep_gpio_leds, ARRAY_SIZE(igep_gpio_leds))) {
+ pr_warning("IGEP v2: Could not obtain leds gpios\n");
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(igep_gpio_leds); i++)
+ gpio_export(igep_gpio_leds[i].gpio, 0);
}
#endif
-static int igep2_twl_gpio_setup(struct device *dev,
+static struct gpio igep2_twl_gpios[] = {
+ { -EINVAL, GPIOF_IN, "GPIO_EHCI_NOC" },
+ { -EINVAL, GPIOF_OUT_INIT_LOW, "GPIO_USBH_CPEN" },
+};
+
+static int igep_twl_gpio_setup(struct device *dev,
unsigned gpio, unsigned ngpio)
{
+ int ret;
+
/* gpio + 0 is "mmc0_cd" (input/IRQ) */
mmc[0].gpio_cd = gpio + 0;
omap2_hsmmc_init(mmc);
+ /* TWL4030_GPIO_MAX + 1 == ledB (out, active low LED) */
+#if !defined(CONFIG_LEDS_GPIO) && !defined(CONFIG_LEDS_GPIO_MODULE)
+ ret = gpio_request_one(gpio + TWL4030_GPIO_MAX + 1, GPIOF_OUT_INIT_HIGH,
+ "gpio-led:green:d1");
+ if (ret == 0)
+ gpio_export(gpio + TWL4030_GPIO_MAX + 1, 0);
+ else
+ pr_warning("IGEP: Could not obtain gpio GPIO_LED1_GREEN\n");
+#else
+ igep_gpio_leds[3].gpio = gpio + TWL4030_GPIO_MAX + 1;
+#endif
+
+ if (machine_is_igep0030())
+ return 0;
+
/*
* REVISIT: need ehci-omap hooks for external VBUS
* power switch and overcurrent detect
*/
- if ((gpio_request(gpio + 1, "GPIO_EHCI_NOC") < 0) ||
- (gpio_direction_input(gpio + 1) < 0))
- pr_err("IGEP2: Could not obtain gpio for EHCI NOC");
+ igep2_twl_gpios[0].gpio = gpio + 1;
- /*
- * TWL4030_GPIO_MAX + 0 == ledA, GPIO_USBH_CPEN
- * (out, active low)
- */
- if ((gpio_request(gpio + TWL4030_GPIO_MAX, "GPIO_USBH_CPEN") < 0) ||
- (gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0) < 0))
+ /* TWL4030_GPIO_MAX + 0 == ledA, GPIO_USBH_CPEN (out, active low) */
+ igep2_twl_gpios[1].gpio = gpio + TWL4030_GPIO_MAX;
+
+ ret = gpio_request_array(igep2_twl_gpios, ARRAY_SIZE(igep2_twl_gpios));
+ if (ret < 0)
pr_err("IGEP2: Could not obtain gpio for USBH_CPEN");
- /* TWL4030_GPIO_MAX + 1 == ledB (out, active low LED) */
-#if !defined(CONFIG_LEDS_GPIO) && !defined(CONFIG_LEDS_GPIO_MODULE)
- if ((gpio_request(gpio+TWL4030_GPIO_MAX+1, "gpio-led:green:d1") == 0)
- && (gpio_direction_output(gpio + TWL4030_GPIO_MAX + 1, 1) == 0))
- gpio_export(gpio + TWL4030_GPIO_MAX + 1, 0);
- else
- pr_warning("IGEP v2: Could not obtain gpio GPIO_LED1_GREEN\n");
-#else
- igep2_gpio_leds[3].gpio = gpio + TWL4030_GPIO_MAX + 1;
-#endif
-
return 0;
};
-static struct twl4030_gpio_platform_data igep2_twl4030_gpio_pdata = {
+static struct twl4030_gpio_platform_data igep_twl4030_gpio_pdata = {
.gpio_base = OMAP_MAX_GPIO_LINES,
.irq_base = TWL4030_GPIO_IRQ_BASE,
.irq_end = TWL4030_GPIO_IRQ_END,
.use_leds = true,
- .setup = igep2_twl_gpio_setup,
+ .setup = igep_twl_gpio_setup,
};
-static struct twl4030_usb_data igep2_usb_data = {
+static struct twl4030_usb_data igep_usb_data = {
.usb_mode = T2_USB_MODE_ULPI,
};
@@ -507,16 +502,17 @@
static void __init igep2_display_init(void)
{
- if (gpio_request(IGEP2_GPIO_DVI_PUP, "GPIO_DVI_PUP") &&
- gpio_direction_output(IGEP2_GPIO_DVI_PUP, 1))
+ int err = gpio_request_one(IGEP2_GPIO_DVI_PUP, GPIOF_OUT_INIT_HIGH,
+ "GPIO_DVI_PUP");
+ if (err)
pr_err("IGEP v2: Could not obtain gpio GPIO_DVI_PUP\n");
}
-static struct platform_device *igep2_devices[] __initdata = {
- &igep2_vwlan_device,
+static struct platform_device *igep_devices[] __initdata = {
+ &igep_vwlan_device,
};
-static void __init igep2_init_early(void)
+static void __init igep_init_early(void)
{
omap2_init_common_infrastructure();
omap2_init_common_devices(m65kxxxxam_sdrc_params,
@@ -561,27 +557,15 @@
.rep = 1,
};
-static struct twl4030_platform_data igep2_twldata = {
+static struct twl4030_platform_data igep_twldata = {
.irq_base = TWL4030_IRQ_BASE,
.irq_end = TWL4030_IRQ_END,
/* platform_data for children goes here */
- .usb = &igep2_usb_data,
- .codec = &igep2_codec_data,
- .gpio = &igep2_twl4030_gpio_pdata,
- .keypad = &igep2_keypad_pdata,
- .vmmc1 = &igep2_vmmc1,
- .vpll2 = &igep2_vpll2,
- .vio = &igep2_vio,
-};
-
-static struct i2c_board_info __initdata igep2_i2c1_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &igep2_twldata,
- },
+ .usb = &igep_usb_data,
+ .gpio = &igep_twl4030_gpio_pdata,
+ .vmmc1 = &igep_vmmc1,
+ .vio = &igep_vio,
};
static struct i2c_board_info __initdata igep2_i2c3_boardinfo[] = {
@@ -590,32 +574,29 @@
},
};
-static void __init igep2_i2c_init(void)
+static void __init igep_i2c_init(void)
{
int ret;
- ret = omap_register_i2c_bus(1, 2600, igep2_i2c1_boardinfo,
- ARRAY_SIZE(igep2_i2c1_boardinfo));
- if (ret)
- pr_warning("IGEP2: Could not register I2C1 bus (%d)\n", ret);
+ if (machine_is_igep0020()) {
+ /*
+ * Bus 3 is attached to the DVI port where devices like the
+ * pico DLP projector don't work reliably with 400kHz
+ */
+ ret = omap_register_i2c_bus(3, 100, igep2_i2c3_boardinfo,
+ ARRAY_SIZE(igep2_i2c3_boardinfo));
+ if (ret)
+ pr_warning("IGEP2: Could not register I2C3 bus (%d)\n", ret);
- /*
- * Bus 3 is attached to the DVI port where devices like the pico DLP
- * projector don't work reliably with 400kHz
- */
- ret = omap_register_i2c_bus(3, 100, igep2_i2c3_boardinfo,
- ARRAY_SIZE(igep2_i2c3_boardinfo));
- if (ret)
- pr_warning("IGEP2: Could not register I2C3 bus (%d)\n", ret);
+ igep_twldata.codec = &igep2_codec_data;
+ igep_twldata.keypad = &igep2_keypad_pdata;
+ igep_twldata.vpll2 = &igep2_vpll2;
+ }
+
+ omap3_pmic_init("twl4030", &igep_twldata);
}
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
+static const struct usbhs_omap_board_data igep2_usbhs_bdata __initconst = {
.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
.port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
.port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
@@ -626,6 +607,17 @@
.reset_gpio_port[2] = -EINVAL,
};
+static const struct usbhs_omap_board_data igep3_usbhs_bdata __initconst = {
+ .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
+ .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
+ .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+
+ .phy_reset = true,
+ .reset_gpio_port[0] = -EINVAL,
+ .reset_gpio_port[1] = IGEP3_GPIO_USBH_NRESET,
+ .reset_gpio_port[2] = -EINVAL,
+};
+
#ifdef CONFIG_OMAP_MUX
static struct omap_board_mux board_mux[] __initdata = {
{ .reg_offset = OMAP_MUX_TERMINATOR },
@@ -633,82 +625,95 @@
#endif
#if defined(CONFIG_LIBERTAS_SDIO) || defined(CONFIG_LIBERTAS_SDIO_MODULE)
+static struct gpio igep_wlan_bt_gpios[] __initdata = {
+ { -EINVAL, GPIOF_OUT_INIT_HIGH, "GPIO_WIFI_NPD" },
+ { -EINVAL, GPIOF_OUT_INIT_HIGH, "GPIO_WIFI_NRESET" },
+ { -EINVAL, GPIOF_OUT_INIT_HIGH, "GPIO_BT_NRESET" },
+};
-static void __init igep2_wlan_bt_init(void)
+static void __init igep_wlan_bt_init(void)
{
- unsigned npd, wreset, btreset;
+ int err;
/* GPIO's for WLAN-BT combo depends on hardware revision */
if (hwrev == IGEP2_BOARD_HWREV_B) {
- npd = IGEP2_RB_GPIO_WIFI_NPD;
- wreset = IGEP2_RB_GPIO_WIFI_NRESET;
- btreset = IGEP2_RB_GPIO_BT_NRESET;
- } else if (hwrev == IGEP2_BOARD_HWREV_C) {
- npd = IGEP2_RC_GPIO_WIFI_NPD;
- wreset = IGEP2_RC_GPIO_WIFI_NRESET;
- btreset = IGEP2_RC_GPIO_BT_NRESET;
+ igep_wlan_bt_gpios[0].gpio = IGEP2_RB_GPIO_WIFI_NPD;
+ igep_wlan_bt_gpios[1].gpio = IGEP2_RB_GPIO_WIFI_NRESET;
+ igep_wlan_bt_gpios[2].gpio = IGEP2_RB_GPIO_BT_NRESET;
+ } else if (hwrev == IGEP2_BOARD_HWREV_C || machine_is_igep0030()) {
+ igep_wlan_bt_gpios[0].gpio = IGEP2_RC_GPIO_WIFI_NPD;
+ igep_wlan_bt_gpios[1].gpio = IGEP2_RC_GPIO_WIFI_NRESET;
+ igep_wlan_bt_gpios[2].gpio = IGEP2_RC_GPIO_BT_NRESET;
} else
return;
- /* Set GPIO's for WLAN-BT combo module */
- if ((gpio_request(npd, "GPIO_WIFI_NPD") == 0) &&
- (gpio_direction_output(npd, 1) == 0)) {
- gpio_export(npd, 0);
- } else
- pr_warning("IGEP2: Could not obtain gpio GPIO_WIFI_NPD\n");
+ err = gpio_request_array(igep_wlan_bt_gpios,
+ ARRAY_SIZE(igep_wlan_bt_gpios));
+ if (err) {
+ pr_warning("IGEP2: Could not obtain WIFI/BT gpios\n");
+ return;
+ }
- if ((gpio_request(wreset, "GPIO_WIFI_NRESET") == 0) &&
- (gpio_direction_output(wreset, 1) == 0)) {
- gpio_export(wreset, 0);
- gpio_set_value(wreset, 0);
- udelay(10);
- gpio_set_value(wreset, 1);
- } else
- pr_warning("IGEP2: Could not obtain gpio GPIO_WIFI_NRESET\n");
+ gpio_export(igep_wlan_bt_gpios[0].gpio, 0);
+ gpio_export(igep_wlan_bt_gpios[1].gpio, 0);
+ gpio_export(igep_wlan_bt_gpios[2].gpio, 0);
- if ((gpio_request(btreset, "GPIO_BT_NRESET") == 0) &&
- (gpio_direction_output(btreset, 1) == 0)) {
- gpio_export(btreset, 0);
- } else
- pr_warning("IGEP2: Could not obtain gpio GPIO_BT_NRESET\n");
+ gpio_set_value(igep_wlan_bt_gpios[1].gpio, 0);
+ udelay(10);
+ gpio_set_value(igep_wlan_bt_gpios[1].gpio, 1);
+
}
#else
-static inline void __init igep2_wlan_bt_init(void) { }
+static inline void __init igep_wlan_bt_init(void) { }
#endif
-static void __init igep2_init(void)
+static void __init igep_init(void)
{
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
/* Get IGEP2 hardware revision */
igep2_get_revision();
/* Register I2C busses and drivers */
- igep2_i2c_init();
- platform_add_devices(igep2_devices, ARRAY_SIZE(igep2_devices));
- omap_display_init(&igep2_dss_data);
+ igep_i2c_init();
+ platform_add_devices(igep_devices, ARRAY_SIZE(igep_devices));
omap_serial_init();
- usb_musb_init(&musb_board_data);
- usbhs_init(&usbhs_bdata);
+ usb_musb_init(NULL);
- igep2_flash_init();
- igep2_leds_init();
- igep2_display_init();
- igep2_init_smsc911x();
+ igep_flash_init();
+ igep_leds_init();
/*
* WLAN-BT combo module from MuRata which has a Marvell WLAN
* (88W8686) + CSR Bluetooth chipset. Uses SDIO interface.
*/
- igep2_wlan_bt_init();
+ igep_wlan_bt_init();
+ if (machine_is_igep0020()) {
+ omap_display_init(&igep2_dss_data);
+ igep2_display_init();
+ igep2_init_smsc911x();
+ usbhs_init(&igep2_usbhs_bdata);
+ } else {
+ usbhs_init(&igep3_usbhs_bdata);
+ }
}
MACHINE_START(IGEP0020, "IGEP v2 board")
.boot_params = 0x80000100,
.reserve = omap_reserve,
.map_io = omap3_map_io,
- .init_early = igep2_init_early,
+ .init_early = igep_init_early,
.init_irq = omap_init_irq,
- .init_machine = igep2_init,
+ .init_machine = igep_init,
+ .timer = &omap_timer,
+MACHINE_END
+
+MACHINE_START(IGEP0030, "IGEP OMAP3 module")
+ .boot_params = 0x80000100,
+ .reserve = omap_reserve,
+ .map_io = omap3_map_io,
+ .init_early = igep_init_early,
+ .init_irq = omap_init_irq,
+ .init_machine = igep_init,
.timer = &omap_timer,
MACHINE_END
diff --git a/arch/arm/mach-omap2/board-igep0030.c b/arch/arm/mach-omap2/board-igep0030.c
deleted file mode 100644
index 2cf86c3..0000000
--- a/arch/arm/mach-omap2/board-igep0030.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright (C) 2010 - ISEE 2007 SL
- *
- * Modified from mach-omap2/board-generic.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-
-#include <linux/regulator/machine.h>
-#include <linux/regulator/fixed.h>
-#include <linux/i2c/twl.h>
-#include <linux/mmc/host.h>
-
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-
-#include <plat/board.h>
-#include <plat/common.h>
-#include <plat/gpmc.h>
-#include <plat/usb.h>
-#include <plat/onenand.h>
-
-#include "mux.h"
-#include "hsmmc.h"
-#include "sdram-numonyx-m65kxxxxam.h"
-
-#define IGEP3_GPIO_LED0_GREEN 54
-#define IGEP3_GPIO_LED0_RED 53
-#define IGEP3_GPIO_LED1_RED 16
-
-#define IGEP3_GPIO_WIFI_NPD 138
-#define IGEP3_GPIO_WIFI_NRESET 139
-#define IGEP3_GPIO_BT_NRESET 137
-
-#define IGEP3_GPIO_USBH_NRESET 183
-
-
-#if defined(CONFIG_MTD_ONENAND_OMAP2) || \
- defined(CONFIG_MTD_ONENAND_OMAP2_MODULE)
-
-#define ONENAND_MAP 0x20000000
-
-/*
- * x2 Flash built-in COMBO POP MEMORY
- * Since the device is equipped with two DataRAMs, and two-plane NAND
- * Flash memory array, these two component enables simultaneous program
- * of 4KiB. Plane1 has only even blocks such as block0, block2, block4
- * while Plane2 has only odd blocks such as block1, block3, block5.
- * So MTD regards it as 4KiB page size and 256KiB block size 64*(2*2048)
- */
-
-static struct mtd_partition igep3_onenand_partitions[] = {
- {
- .name = "X-Loader",
- .offset = 0,
- .size = 2 * (64*(2*2048))
- },
- {
- .name = "U-Boot",
- .offset = MTDPART_OFS_APPEND,
- .size = 6 * (64*(2*2048)),
- },
- {
- .name = "Environment",
- .offset = MTDPART_OFS_APPEND,
- .size = 2 * (64*(2*2048)),
- },
- {
- .name = "Kernel",
- .offset = MTDPART_OFS_APPEND,
- .size = 12 * (64*(2*2048)),
- },
- {
- .name = "File System",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- },
-};
-
-static struct omap_onenand_platform_data igep3_onenand_pdata = {
- .parts = igep3_onenand_partitions,
- .nr_parts = ARRAY_SIZE(igep3_onenand_partitions),
- .onenand_setup = NULL,
- .dma_channel = -1, /* disable DMA in OMAP OneNAND driver */
-};
-
-static struct platform_device igep3_onenand_device = {
- .name = "omap2-onenand",
- .id = -1,
- .dev = {
- .platform_data = &igep3_onenand_pdata,
- },
-};
-
-static void __init igep3_flash_init(void)
-{
- u8 cs = 0;
- u8 onenandcs = GPMC_CS_NUM + 1;
-
- for (cs = 0; cs < GPMC_CS_NUM; cs++) {
- u32 ret;
- ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
-
- /* Check if NAND/oneNAND is configured */
- if ((ret & 0xC00) == 0x800)
- /* NAND found */
- pr_err("IGEP3: Unsupported NAND found\n");
- else {
- ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
-
- if ((ret & 0x3F) == (ONENAND_MAP >> 24))
- /* OneNAND found */
- onenandcs = cs;
- }
- }
-
- if (onenandcs > GPMC_CS_NUM) {
- pr_err("IGEP3: Unable to find configuration in GPMC\n");
- return;
- }
-
- igep3_onenand_pdata.cs = onenandcs;
-
- if (platform_device_register(&igep3_onenand_device) < 0)
- pr_err("IGEP3: Unable to register OneNAND device\n");
-}
-
-#else
-static void __init igep3_flash_init(void) {}
-#endif
-
-static struct regulator_consumer_supply igep3_vmmc1_supply =
- REGULATOR_SUPPLY("vmmc", "omap_hsmmc.0");
-
-/* VMMC1 for OMAP VDD_MMC1 (i/o) and MMC1 card */
-static struct regulator_init_data igep3_vmmc1 = {
- .constraints = {
- .min_uV = 1850000,
- .max_uV = 3150000,
- .valid_modes_mask = REGULATOR_MODE_NORMAL
- | REGULATOR_MODE_STANDBY,
- .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
- | REGULATOR_CHANGE_MODE
- | REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &igep3_vmmc1_supply,
-};
-
-static struct regulator_consumer_supply igep3_vio_supply =
- REGULATOR_SUPPLY("vmmc_aux", "omap_hsmmc.1");
-
-static struct regulator_init_data igep3_vio = {
- .constraints = {
- .min_uV = 1800000,
- .max_uV = 1800000,
- .apply_uV = 1,
- .valid_modes_mask = REGULATOR_MODE_NORMAL
- | REGULATOR_MODE_STANDBY,
- .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
- | REGULATOR_CHANGE_MODE
- | REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &igep3_vio_supply,
-};
-
-static struct regulator_consumer_supply igep3_vmmc2_supply =
- REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1");
-
-static struct regulator_init_data igep3_vmmc2 = {
- .constraints = {
- .valid_modes_mask = REGULATOR_MODE_NORMAL,
- .always_on = 1,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &igep3_vmmc2_supply,
-};
-
-static struct fixed_voltage_config igep3_vwlan = {
- .supply_name = "vwlan",
- .microvolts = 3300000,
- .gpio = -EINVAL,
- .enabled_at_boot = 1,
- .init_data = &igep3_vmmc2,
-};
-
-static struct platform_device igep3_vwlan_device = {
- .name = "reg-fixed-voltage",
- .id = 0,
- .dev = {
- .platform_data = &igep3_vwlan,
- },
-};
-
-static struct omap2_hsmmc_info mmc[] = {
- [0] = {
- .mmc = 1,
- .caps = MMC_CAP_4_BIT_DATA,
- .gpio_cd = -EINVAL,
- .gpio_wp = -EINVAL,
- },
-#if defined(CONFIG_LIBERTAS_SDIO) || defined(CONFIG_LIBERTAS_SDIO_MODULE)
- [1] = {
- .mmc = 2,
- .caps = MMC_CAP_4_BIT_DATA,
- .gpio_cd = -EINVAL,
- .gpio_wp = -EINVAL,
- },
-#endif
- {} /* Terminator */
-};
-
-#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
-#include <linux/leds.h>
-
-static struct gpio_led igep3_gpio_leds[] = {
- [0] = {
- .name = "gpio-led:red:d0",
- .gpio = IGEP3_GPIO_LED0_RED,
- .default_trigger = "default-off"
- },
- [1] = {
- .name = "gpio-led:green:d0",
- .gpio = IGEP3_GPIO_LED0_GREEN,
- .default_trigger = "default-off",
- },
- [2] = {
- .name = "gpio-led:red:d1",
- .gpio = IGEP3_GPIO_LED1_RED,
- .default_trigger = "default-off",
- },
- [3] = {
- .name = "gpio-led:green:d1",
- .default_trigger = "heartbeat",
- .gpio = -EINVAL, /* gets replaced */
- },
-};
-
-static struct gpio_led_platform_data igep3_led_pdata = {
- .leds = igep3_gpio_leds,
- .num_leds = ARRAY_SIZE(igep3_gpio_leds),
-};
-
-static struct platform_device igep3_led_device = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &igep3_led_pdata,
- },
-};
-
-static void __init igep3_leds_init(void)
-{
- platform_device_register(&igep3_led_device);
-}
-
-#else
-static inline void igep3_leds_init(void)
-{
- if ((gpio_request(IGEP3_GPIO_LED0_RED, "gpio-led:red:d0") == 0) &&
- (gpio_direction_output(IGEP3_GPIO_LED0_RED, 1) == 0)) {
- gpio_export(IGEP3_GPIO_LED0_RED, 0);
- gpio_set_value(IGEP3_GPIO_LED0_RED, 1);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_LED0_RED\n");
-
- if ((gpio_request(IGEP3_GPIO_LED0_GREEN, "gpio-led:green:d0") == 0) &&
- (gpio_direction_output(IGEP3_GPIO_LED0_GREEN, 1) == 0)) {
- gpio_export(IGEP3_GPIO_LED0_GREEN, 0);
- gpio_set_value(IGEP3_GPIO_LED0_GREEN, 1);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_LED0_GREEN\n");
-
- if ((gpio_request(IGEP3_GPIO_LED1_RED, "gpio-led:red:d1") == 0) &&
- (gpio_direction_output(IGEP3_GPIO_LED1_RED, 1) == 0)) {
- gpio_export(IGEP3_GPIO_LED1_RED, 0);
- gpio_set_value(IGEP3_GPIO_LED1_RED, 1);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_LED1_RED\n");
-}
-#endif
-
-static int igep3_twl4030_gpio_setup(struct device *dev,
- unsigned gpio, unsigned ngpio)
-{
- /* gpio + 0 is "mmc0_cd" (input/IRQ) */
- mmc[0].gpio_cd = gpio + 0;
- omap2_hsmmc_init(mmc);
-
- /* TWL4030_GPIO_MAX + 1 == ledB (out, active low LED) */
-#if !defined(CONFIG_LEDS_GPIO) && !defined(CONFIG_LEDS_GPIO_MODULE)
- if ((gpio_request(gpio+TWL4030_GPIO_MAX+1, "gpio-led:green:d1") == 0)
- && (gpio_direction_output(gpio + TWL4030_GPIO_MAX + 1, 1) == 0)) {
- gpio_export(gpio + TWL4030_GPIO_MAX + 1, 0);
- gpio_set_value(gpio + TWL4030_GPIO_MAX + 1, 0);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_LED1_GREEN\n");
-#else
- igep3_gpio_leds[3].gpio = gpio + TWL4030_GPIO_MAX + 1;
-#endif
-
- return 0;
-};
-
-static struct twl4030_gpio_platform_data igep3_twl4030_gpio_pdata = {
- .gpio_base = OMAP_MAX_GPIO_LINES,
- .irq_base = TWL4030_GPIO_IRQ_BASE,
- .irq_end = TWL4030_GPIO_IRQ_END,
- .use_leds = true,
- .setup = igep3_twl4030_gpio_setup,
-};
-
-static struct twl4030_usb_data igep3_twl4030_usb_data = {
- .usb_mode = T2_USB_MODE_ULPI,
-};
-
-static struct platform_device *igep3_devices[] __initdata = {
- &igep3_vwlan_device,
-};
-
-static void __init igep3_init_early(void)
-{
- omap2_init_common_infrastructure();
- omap2_init_common_devices(m65kxxxxam_sdrc_params,
- m65kxxxxam_sdrc_params);
-}
-
-static struct twl4030_platform_data igep3_twl4030_pdata = {
- .irq_base = TWL4030_IRQ_BASE,
- .irq_end = TWL4030_IRQ_END,
-
- /* platform_data for children goes here */
- .usb = &igep3_twl4030_usb_data,
- .gpio = &igep3_twl4030_gpio_pdata,
- .vmmc1 = &igep3_vmmc1,
- .vio = &igep3_vio,
-};
-
-static struct i2c_board_info __initdata igep3_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &igep3_twl4030_pdata,
- },
-};
-
-static int __init igep3_i2c_init(void)
-{
- omap_register_i2c_bus(1, 2600, igep3_i2c_boardinfo,
- ARRAY_SIZE(igep3_i2c_boardinfo));
-
- return 0;
-}
-
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
-#if defined(CONFIG_LIBERTAS_SDIO) || defined(CONFIG_LIBERTAS_SDIO_MODULE)
-
-static void __init igep3_wifi_bt_init(void)
-{
- /* Configure MUX values for W-LAN + Bluetooth GPIO's */
- omap_mux_init_gpio(IGEP3_GPIO_WIFI_NPD, OMAP_PIN_OUTPUT);
- omap_mux_init_gpio(IGEP3_GPIO_WIFI_NRESET, OMAP_PIN_OUTPUT);
- omap_mux_init_gpio(IGEP3_GPIO_BT_NRESET, OMAP_PIN_OUTPUT);
-
- /* Set GPIO's for W-LAN + Bluetooth combo module */
- if ((gpio_request(IGEP3_GPIO_WIFI_NPD, "GPIO_WIFI_NPD") == 0) &&
- (gpio_direction_output(IGEP3_GPIO_WIFI_NPD, 1) == 0)) {
- gpio_export(IGEP3_GPIO_WIFI_NPD, 0);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_WIFI_NPD\n");
-
- if ((gpio_request(IGEP3_GPIO_WIFI_NRESET, "GPIO_WIFI_NRESET") == 0) &&
- (gpio_direction_output(IGEP3_GPIO_WIFI_NRESET, 1) == 0)) {
- gpio_export(IGEP3_GPIO_WIFI_NRESET, 0);
- gpio_set_value(IGEP3_GPIO_WIFI_NRESET, 0);
- udelay(10);
- gpio_set_value(IGEP3_GPIO_WIFI_NRESET, 1);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_WIFI_NRESET\n");
-
- if ((gpio_request(IGEP3_GPIO_BT_NRESET, "GPIO_BT_NRESET") == 0) &&
- (gpio_direction_output(IGEP3_GPIO_BT_NRESET, 1) == 0)) {
- gpio_export(IGEP3_GPIO_BT_NRESET, 0);
- } else
- pr_warning("IGEP3: Could not obtain gpio GPIO_BT_NRESET\n");
-}
-#else
-void __init igep3_wifi_bt_init(void) {}
-#endif
-
-static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
- .port_mode[0] = OMAP_USBHS_PORT_MODE_UNUSED,
- .port_mode[1] = OMAP_EHCI_PORT_MODE_PHY,
- .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
-
- .phy_reset = true,
- .reset_gpio_port[0] = -EINVAL,
- .reset_gpio_port[1] = IGEP3_GPIO_USBH_NRESET,
- .reset_gpio_port[2] = -EINVAL,
-};
-
-#ifdef CONFIG_OMAP_MUX
-static struct omap_board_mux board_mux[] __initdata = {
- OMAP3_MUX(I2C2_SDA, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT),
- { .reg_offset = OMAP_MUX_TERMINATOR },
-};
-#endif
-
-static void __init igep3_init(void)
-{
- omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
-
- /* Register I2C busses and drivers */
- igep3_i2c_init();
- platform_add_devices(igep3_devices, ARRAY_SIZE(igep3_devices));
- omap_serial_init();
- usb_musb_init(&musb_board_data);
- usbhs_init(&usbhs_bdata);
-
- igep3_flash_init();
- igep3_leds_init();
-
- /*
- * WLAN-BT combo module from MuRata which has a Marvell WLAN
- * (88W8686) + CSR Bluetooth chipset. Uses SDIO interface.
- */
- igep3_wifi_bt_init();
-
-}
-
-MACHINE_START(IGEP0030, "IGEP OMAP3 module")
- .boot_params = 0x80000100,
- .reserve = omap_reserve,
- .map_io = omap3_map_io,
- .init_early = igep3_init_early,
- .init_irq = omap_init_irq,
- .init_machine = igep3_init,
- .timer = &omap_timer,
-MACHINE_END
diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c
index e2ba779..f7d6038 100644
--- a/arch/arm/mach-omap2/board-ldp.c
+++ b/arch/arm/mach-omap2/board-ldp.c
@@ -22,7 +22,6 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
-#include <linux/spi/ads7846.h>
#include <linux/regulator/machine.h>
#include <linux/i2c/twl.h>
#include <linux/io.h>
@@ -43,47 +42,19 @@
#include <asm/delay.h>
#include <plat/usb.h>
+#include <plat/gpmc-smsc911x.h>
#include "board-flash.h"
#include "mux.h"
#include "hsmmc.h"
#include "control.h"
+#include "common-board-devices.h"
#define LDP_SMSC911X_CS 1
#define LDP_SMSC911X_GPIO 152
#define DEBUG_BASE 0x08000000
#define LDP_ETHR_START DEBUG_BASE
-static struct resource ldp_smsc911x_resources[] = {
- [0] = {
- .start = LDP_ETHR_START,
- .end = LDP_ETHR_START + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 0,
- .end = 0,
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct smsc911x_platform_config ldp_smsc911x_config = {
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
- .flags = SMSC911X_USE_32BIT,
- .phy_interface = PHY_INTERFACE_MODE_MII,
-};
-
-static struct platform_device ldp_smsc911x_device = {
- .name = "smsc911x",
- .id = -1,
- .num_resources = ARRAY_SIZE(ldp_smsc911x_resources),
- .resource = ldp_smsc911x_resources,
- .dev = {
- .platform_data = &ldp_smsc911x_config,
- },
-};
-
static uint32_t board_keymap[] = {
KEY(0, 0, KEY_1),
KEY(1, 0, KEY_2),
@@ -197,82 +168,16 @@
},
};
-static int ts_gpio;
-
-/**
- * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
- *
- * @return - void. If request gpio fails then Flag KERN_ERR.
- */
-static void ads7846_dev_init(void)
-{
- if (gpio_request(ts_gpio, "ads7846 irq") < 0) {
- printk(KERN_ERR "can't get ads746 pen down GPIO\n");
- return;
- }
-
- gpio_direction_input(ts_gpio);
- gpio_set_debounce(ts_gpio, 310);
-}
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(ts_gpio);
-}
-
-static struct ads7846_platform_data tsc2046_config __initdata = {
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
-};
-
-static struct omap2_mcspi_device_config tsc2046_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static struct spi_board_info ldp_spi_board_info[] __initdata = {
- [0] = {
- /*
- * TSC2046 operates at a max freqency of 2MHz, so
- * operate slightly below at 1.5MHz
- */
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &tsc2046_mcspi_config,
- .irq = 0,
- .platform_data = &tsc2046_config,
- },
+static struct omap_smsc911x_platform_data smsc911x_cfg = {
+ .cs = LDP_SMSC911X_CS,
+ .gpio_irq = LDP_SMSC911X_GPIO,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT,
};
static inline void __init ldp_init_smsc911x(void)
{
- int eth_cs;
- unsigned long cs_mem_base;
- int eth_gpio = 0;
-
- eth_cs = LDP_SMSC911X_CS;
-
- if (gpmc_cs_request(eth_cs, SZ_16M, &cs_mem_base) < 0) {
- printk(KERN_ERR "Failed to request GPMC mem for smsc911x\n");
- return;
- }
-
- ldp_smsc911x_resources[0].start = cs_mem_base + 0x0;
- ldp_smsc911x_resources[0].end = cs_mem_base + 0xff;
- udelay(100);
-
- eth_gpio = LDP_SMSC911X_GPIO;
-
- ldp_smsc911x_resources[1].start = OMAP_GPIO_IRQ(eth_gpio);
-
- if (gpio_request(eth_gpio, "smsc911x irq") < 0) {
- printk(KERN_ERR "Failed to request GPIO%d for smsc911x IRQ\n",
- eth_gpio);
- return;
- }
- gpio_direction_input(eth_gpio);
+ gpmc_smsc911x_init(&smsc911x_cfg);
}
static struct platform_device ldp_lcd_device = {
@@ -360,19 +265,9 @@
.keypad = &ldp_kp_twl4030_data,
};
-static struct i2c_board_info __initdata ldp_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &ldp_twldata,
- },
-};
-
static int __init omap_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, ldp_i2c_boardinfo,
- ARRAY_SIZE(ldp_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &ldp_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
@@ -389,7 +284,6 @@
};
static struct platform_device *ldp_devices[] __initdata = {
- &ldp_smsc911x_device,
&ldp_lcd_device,
&ldp_gpio_keys_device,
};
@@ -400,12 +294,6 @@
};
#endif
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static struct mtd_partition ldp_nand_partitions[] = {
/* All the partition sizes are listed in terms of NAND block size */
{
@@ -446,13 +334,9 @@
ldp_init_smsc911x();
omap_i2c_init();
platform_add_devices(ldp_devices, ARRAY_SIZE(ldp_devices));
- ts_gpio = 54;
- ldp_spi_board_info[0].irq = gpio_to_irq(ts_gpio);
- spi_register_board_info(ldp_spi_board_info,
- ARRAY_SIZE(ldp_spi_board_info));
- ads7846_dev_init();
+ omap_ads7846_init(1, 54, 310, NULL);
omap_serial_init();
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
board_nand_init(ldp_nand_partitions,
ARRAY_SIZE(ldp_nand_partitions), ZOOM_NAND_CS, 0);
diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c
index e710cd9..8d74318 100644
--- a/arch/arm/mach-omap2/board-n8x0.c
+++ b/arch/arm/mach-omap2/board-n8x0.c
@@ -106,14 +106,13 @@
static char announce[] __initdata = KERN_INFO "TUSB 6010\n";
/* PM companion chip power control pin */
- ret = gpio_request(TUSB6010_GPIO_ENABLE, "TUSB6010 enable");
+ ret = gpio_request_one(TUSB6010_GPIO_ENABLE, GPIOF_OUT_INIT_LOW,
+ "TUSB6010 enable");
if (ret != 0) {
printk(KERN_ERR "Could not get TUSB power GPIO%i\n",
TUSB6010_GPIO_ENABLE);
return;
}
- gpio_direction_output(TUSB6010_GPIO_ENABLE, 0);
-
tusb_set_power(0);
ret = tusb6010_setup_interface(&tusb_data, TUSB6010_REFCLK_19, 2,
@@ -494,8 +493,12 @@
static struct omap_mmc_platform_data *mmc_data[OMAP24XX_NR_MMC];
-static void __init n8x0_mmc_init(void)
+static struct gpio n810_emmc_gpios[] __initdata = {
+ { N810_EMMC_VSD_GPIO, GPIOF_OUT_INIT_LOW, "MMC slot 2 Vddf" },
+ { N810_EMMC_VIO_GPIO, GPIOF_OUT_INIT_LOW, "MMC slot 2 Vdd" },
+};
+static void __init n8x0_mmc_init(void)
{
int err;
@@ -512,27 +515,18 @@
mmc1_data.slots[1].ban_openended = 1;
}
- err = gpio_request(N8X0_SLOT_SWITCH_GPIO, "MMC slot switch");
+ err = gpio_request_one(N8X0_SLOT_SWITCH_GPIO, GPIOF_OUT_INIT_LOW,
+ "MMC slot switch");
if (err)
return;
- gpio_direction_output(N8X0_SLOT_SWITCH_GPIO, 0);
-
if (machine_is_nokia_n810()) {
- err = gpio_request(N810_EMMC_VSD_GPIO, "MMC slot 2 Vddf");
+ err = gpio_request_array(n810_emmc_gpios,
+ ARRAY_SIZE(n810_emmc_gpios));
if (err) {
gpio_free(N8X0_SLOT_SWITCH_GPIO);
return;
}
- gpio_direction_output(N810_EMMC_VSD_GPIO, 0);
-
- err = gpio_request(N810_EMMC_VIO_GPIO, "MMC slot 2 Vdd");
- if (err) {
- gpio_free(N8X0_SLOT_SWITCH_GPIO);
- gpio_free(N810_EMMC_VSD_GPIO);
- return;
- }
- gpio_direction_output(N810_EMMC_VIO_GPIO, 0);
}
mmc_data[0] = &mmc1_data;
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 97750d4..be71426 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -52,6 +52,7 @@
#include "hsmmc.h"
#include "timer-gp.h"
#include "pm.h"
+#include "common-board-devices.h"
#define NAND_BLOCK_SIZE SZ_128K
@@ -79,6 +80,12 @@
return omap3_beagle_version;
}
+static struct gpio omap3_beagle_rev_gpios[] __initdata = {
+ { 171, GPIOF_IN, "rev_id_0" },
+ { 172, GPIOF_IN, "rev_id_1" },
+ { 173, GPIOF_IN, "rev_id_2" },
+};
+
static void __init omap3_beagle_init_rev(void)
{
int ret;
@@ -88,21 +95,13 @@
omap_mux_init_gpio(172, OMAP_PIN_INPUT_PULLUP);
omap_mux_init_gpio(173, OMAP_PIN_INPUT_PULLUP);
- ret = gpio_request(171, "rev_id_0");
- if (ret < 0)
- goto fail0;
-
- ret = gpio_request(172, "rev_id_1");
- if (ret < 0)
- goto fail1;
-
- ret = gpio_request(173, "rev_id_2");
- if (ret < 0)
- goto fail2;
-
- gpio_direction_input(171);
- gpio_direction_input(172);
- gpio_direction_input(173);
+ ret = gpio_request_array(omap3_beagle_rev_gpios,
+ ARRAY_SIZE(omap3_beagle_rev_gpios));
+ if (ret < 0) {
+ printk(KERN_ERR "Unable to get revision detection GPIO pins\n");
+ omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN;
+ return;
+ }
beagle_rev = gpio_get_value(171) | (gpio_get_value(172) << 1)
| (gpio_get_value(173) << 2);
@@ -128,18 +127,6 @@
printk(KERN_INFO "OMAP3 Beagle Rev: unknown %hd\n", beagle_rev);
omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN;
}
-
- return;
-
-fail2:
- gpio_free(172);
-fail1:
- gpio_free(171);
-fail0:
- printk(KERN_ERR "Unable to get revision detection GPIO pins\n");
- omap3_beagle_version = OMAP3BEAGLE_BOARD_UNKN;
-
- return;
}
static struct mtd_partition omap3beagle_nand_partitions[] = {
@@ -173,15 +160,6 @@
},
};
-static struct omap_nand_platform_data omap3beagle_nand_data = {
- .options = NAND_BUSWIDTH_16,
- .parts = omap3beagle_nand_partitions,
- .nr_parts = ARRAY_SIZE(omap3beagle_nand_partitions),
- .dma_channel = -1, /* disable DMA in OMAP NAND driver */
- .nand_setup = NULL,
- .dev_ready = NULL,
-};
-
/* DSS */
static int beagle_enable_dvi(struct omap_dss_device *dssdev)
@@ -243,13 +221,10 @@
{
int r;
- r = gpio_request(beagle_dvi_device.reset_gpio, "DVI reset");
- if (r < 0) {
+ r = gpio_request_one(beagle_dvi_device.reset_gpio, GPIOF_OUT_INIT_LOW,
+ "DVI reset");
+ if (r < 0)
printk(KERN_ERR "Unable to get DVI reset GPIO\n");
- return;
- }
-
- gpio_direction_output(beagle_dvi_device.reset_gpio, 0);
}
#include "sdram-micron-mt46h32m32lf-6.h"
@@ -276,7 +251,7 @@
static int beagle_twl_gpio_setup(struct device *dev,
unsigned gpio, unsigned ngpio)
{
- int r;
+ int r, usb_pwr_level;
if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) {
mmc[0].gpio_wp = -EINVAL;
@@ -295,66 +270,46 @@
beagle_vmmc1_supply.dev = mmc[0].dev;
beagle_vsim_supply.dev = mmc[0].dev;
- /* REVISIT: need ehci-omap hooks for external VBUS
- * power switch and overcurrent detect
- */
- if (omap3_beagle_get_rev() != OMAP3BEAGLE_BOARD_XM) {
- r = gpio_request(gpio + 1, "EHCI_nOC");
- if (!r) {
- r = gpio_direction_input(gpio + 1);
- if (r)
- gpio_free(gpio + 1);
- }
- if (r)
- pr_err("%s: unable to configure EHCI_nOC\n", __func__);
- }
-
/*
* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, XM active
* high / others active low)
- */
- gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR");
- if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM)
- gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1);
- else
- gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0);
-
- /* DVI reset GPIO is different between beagle revisions */
- if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM)
- beagle_dvi_device.reset_gpio = 129;
- else
- beagle_dvi_device.reset_gpio = 170;
-
- /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
- gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
-
- /*
- * gpio + 1 on Xm controls the TFP410's enable line (active low)
- * gpio + 2 control varies depending on the board rev as follows:
- * P7/P8 revisions(prototype): Camera EN
- * A2+ revisions (production): LDO (supplies DVI, serial, led blocks)
+ * DVI reset GPIO is different between beagle revisions
*/
if (omap3_beagle_get_rev() == OMAP3BEAGLE_BOARD_XM) {
- r = gpio_request(gpio + 1, "nDVI_PWR_EN");
- if (!r) {
- r = gpio_direction_output(gpio + 1, 0);
- if (r)
- gpio_free(gpio + 1);
- }
+ usb_pwr_level = GPIOF_OUT_INIT_HIGH;
+ beagle_dvi_device.reset_gpio = 129;
+ /*
+ * gpio + 1 on Xm controls the TFP410's enable line (active low)
+ * gpio + 2 control varies depending on the board rev as below:
+ * P7/P8 revisions(prototype): Camera EN
+ * A2+ revisions (production): LDO (DVI, serial, led blocks)
+ */
+ r = gpio_request_one(gpio + 1, GPIOF_OUT_INIT_LOW,
+ "nDVI_PWR_EN");
if (r)
pr_err("%s: unable to configure nDVI_PWR_EN\n",
__func__);
- r = gpio_request(gpio + 2, "DVI_LDO_EN");
- if (!r) {
- r = gpio_direction_output(gpio + 2, 1);
- if (r)
- gpio_free(gpio + 2);
- }
+ r = gpio_request_one(gpio + 2, GPIOF_OUT_INIT_HIGH,
+ "DVI_LDO_EN");
if (r)
pr_err("%s: unable to configure DVI_LDO_EN\n",
__func__);
+ } else {
+ usb_pwr_level = GPIOF_OUT_INIT_LOW;
+ beagle_dvi_device.reset_gpio = 170;
+ /*
+ * REVISIT: need ehci-omap hooks for external VBUS
+ * power switch and overcurrent detect
+ */
+ if (gpio_request_one(gpio + 1, GPIOF_IN, "EHCI_nOC"))
+ pr_err("%s: unable to configure EHCI_nOC\n", __func__);
}
+ gpio_request_one(gpio + TWL4030_GPIO_MAX, usb_pwr_level, "nEN_USB_PWR");
+
+ /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
+ gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
+
return 0;
}
@@ -453,15 +408,6 @@
.vpll2 = &beagle_vpll2,
};
-static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &beagle_twldata,
- },
-};
-
static struct i2c_board_info __initdata beagle_i2c_eeprom[] = {
{
I2C_BOARD_INFO("eeprom", 0x50),
@@ -470,8 +416,7 @@
static int __init omap3_beagle_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo,
- ARRAY_SIZE(beagle_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &beagle_twldata);
/* Bus 3 is attached to the DVI port where devices like the pico DLP
* projector don't work reliably with 400kHz */
omap_register_i2c_bus(3, 100, beagle_i2c_eeprom, ARRAY_SIZE(beagle_i2c_eeprom));
@@ -551,39 +496,6 @@
&keys_gpio,
};
-static void __init omap3beagle_flash_init(void)
-{
- u8 cs = 0;
- u8 nandcs = GPMC_CS_NUM + 1;
-
- /* find out the chip-select on which NAND exists */
- while (cs < GPMC_CS_NUM) {
- u32 ret = 0;
- ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
-
- if ((ret & 0xC00) == 0x800) {
- printk(KERN_INFO "Found NAND on CS%d\n", cs);
- if (nandcs > GPMC_CS_NUM)
- nandcs = cs;
- }
- cs++;
- }
-
- if (nandcs > GPMC_CS_NUM) {
- printk(KERN_INFO "NAND: Unable to find configuration "
- "in GPMC\n ");
- return;
- }
-
- if (nandcs < GPMC_CS_NUM) {
- omap3beagle_nand_data.cs = nandcs;
-
- printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
- if (gpmc_nand_init(&omap3beagle_nand_data) < 0)
- printk(KERN_ERR "Unable to register NAND device\n");
- }
-}
-
static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
@@ -602,12 +514,6 @@
};
#endif
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static void __init beagle_opp_init(void)
{
int r = 0;
@@ -665,13 +571,13 @@
omap_serial_init();
omap_mux_init_gpio(170, OMAP_PIN_INPUT);
- gpio_request(170, "DVI_nPD");
/* REVISIT leave DVI powered down until it's needed ... */
- gpio_direction_output(170, true);
+ gpio_request_one(170, GPIOF_OUT_INIT_HIGH, "DVI_nPD");
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
- omap3beagle_flash_init();
+ omap_nand_flash_init(NAND_BUSWIDTH_16, omap3beagle_nand_partitions,
+ ARRAY_SIZE(omap3beagle_nand_partitions));
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index 7f94ccc..b4d4346 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -50,6 +50,7 @@
#include "mux.h"
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define OMAP3_EVM_TS_GPIO 175
#define OMAP3_EVM_EHCI_VBUS 22
@@ -101,49 +102,20 @@
}
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
-static struct resource omap3evm_smsc911x_resources[] = {
- [0] = {
- .start = OMAP3EVM_ETHR_START,
- .end = (OMAP3EVM_ETHR_START + OMAP3EVM_ETHR_SIZE - 1),
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = OMAP_GPIO_IRQ(OMAP3EVM_ETHR_GPIO_IRQ),
- .end = OMAP_GPIO_IRQ(OMAP3EVM_ETHR_GPIO_IRQ),
- .flags = (IORESOURCE_IRQ | IRQF_TRIGGER_LOW),
- },
-};
+#include <plat/gpmc-smsc911x.h>
-static struct smsc911x_platform_config smsc911x_config = {
- .phy_interface = PHY_INTERFACE_MODE_MII,
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
- .flags = (SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS),
-};
-
-static struct platform_device omap3evm_smsc911x_device = {
- .name = "smsc911x",
- .id = -1,
- .num_resources = ARRAY_SIZE(omap3evm_smsc911x_resources),
- .resource = &omap3evm_smsc911x_resources[0],
- .dev = {
- .platform_data = &smsc911x_config,
- },
+static struct omap_smsc911x_platform_data smsc911x_cfg = {
+ .cs = OMAP3EVM_SMSC911X_CS,
+ .gpio_irq = OMAP3EVM_ETHR_GPIO_IRQ,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS,
};
static inline void __init omap3evm_init_smsc911x(void)
{
- int eth_cs, eth_rst;
struct clk *l3ck;
unsigned int rate;
- if (get_omap3_evm_rev() == OMAP3EVM_BOARD_GEN_1)
- eth_rst = OMAP3EVM_GEN1_ETHR_GPIO_RST;
- else
- eth_rst = OMAP3EVM_GEN2_ETHR_GPIO_RST;
-
- eth_cs = OMAP3EVM_SMSC911X_CS;
-
l3ck = clk_get(NULL, "l3_ck");
if (IS_ERR(l3ck))
rate = 100000000;
@@ -152,33 +124,13 @@
/* Configure ethernet controller reset gpio */
if (cpu_is_omap3430()) {
- if (gpio_request(eth_rst, "SMSC911x gpio") < 0) {
- pr_err(KERN_ERR "Failed to request %d for smsc911x\n",
- eth_rst);
- return;
- }
-
- if (gpio_direction_output(eth_rst, 1) < 0) {
- pr_err(KERN_ERR "Failed to set direction of %d for" \
- " smsc911x\n", eth_rst);
- return;
- }
- /* reset pulse to ethernet controller*/
- usleep_range(150, 220);
- gpio_set_value(eth_rst, 0);
- usleep_range(150, 220);
- gpio_set_value(eth_rst, 1);
- usleep_range(1, 2);
+ if (get_omap3_evm_rev() == OMAP3EVM_BOARD_GEN_1)
+ smsc911x_cfg.gpio_reset = OMAP3EVM_GEN1_ETHR_GPIO_RST;
+ else
+ smsc911x_cfg.gpio_reset = OMAP3EVM_GEN2_ETHR_GPIO_RST;
}
- if (gpio_request(OMAP3EVM_ETHR_GPIO_IRQ, "SMSC911x irq") < 0) {
- printk(KERN_ERR "Failed to request GPIO%d for smsc911x IRQ\n",
- OMAP3EVM_ETHR_GPIO_IRQ);
- return;
- }
-
- gpio_direction_input(OMAP3EVM_ETHR_GPIO_IRQ);
- platform_device_register(&omap3evm_smsc911x_device);
+ gpmc_smsc911x_init(&smsc911x_cfg);
}
#else
@@ -197,6 +149,15 @@
#define OMAP3EVM_LCD_PANEL_BKLIGHT_GPIO 210
#define OMAP3EVM_DVI_PANEL_EN_GPIO 199
+static struct gpio omap3_evm_dss_gpios[] __initdata = {
+ { OMAP3EVM_LCD_PANEL_RESB, GPIOF_OUT_INIT_HIGH, "lcd_panel_resb" },
+ { OMAP3EVM_LCD_PANEL_INI, GPIOF_OUT_INIT_HIGH, "lcd_panel_ini" },
+ { OMAP3EVM_LCD_PANEL_QVGA, GPIOF_OUT_INIT_LOW, "lcd_panel_qvga" },
+ { OMAP3EVM_LCD_PANEL_LR, GPIOF_OUT_INIT_HIGH, "lcd_panel_lr" },
+ { OMAP3EVM_LCD_PANEL_UD, GPIOF_OUT_INIT_HIGH, "lcd_panel_ud" },
+ { OMAP3EVM_LCD_PANEL_ENVDD, GPIOF_OUT_INIT_LOW, "lcd_panel_envdd" },
+};
+
static int lcd_enabled;
static int dvi_enabled;
@@ -204,61 +165,10 @@
{
int r;
- r = gpio_request(OMAP3EVM_LCD_PANEL_RESB, "lcd_panel_resb");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_resb\n");
- return;
- }
- gpio_direction_output(OMAP3EVM_LCD_PANEL_RESB, 1);
-
- r = gpio_request(OMAP3EVM_LCD_PANEL_INI, "lcd_panel_ini");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_ini\n");
- goto err_1;
- }
- gpio_direction_output(OMAP3EVM_LCD_PANEL_INI, 1);
-
- r = gpio_request(OMAP3EVM_LCD_PANEL_QVGA, "lcd_panel_qvga");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_qvga\n");
- goto err_2;
- }
- gpio_direction_output(OMAP3EVM_LCD_PANEL_QVGA, 0);
-
- r = gpio_request(OMAP3EVM_LCD_PANEL_LR, "lcd_panel_lr");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_lr\n");
- goto err_3;
- }
- gpio_direction_output(OMAP3EVM_LCD_PANEL_LR, 1);
-
- r = gpio_request(OMAP3EVM_LCD_PANEL_UD, "lcd_panel_ud");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_ud\n");
- goto err_4;
- }
- gpio_direction_output(OMAP3EVM_LCD_PANEL_UD, 1);
-
- r = gpio_request(OMAP3EVM_LCD_PANEL_ENVDD, "lcd_panel_envdd");
- if (r) {
- printk(KERN_ERR "failed to get lcd_panel_envdd\n");
- goto err_5;
- }
- gpio_direction_output(OMAP3EVM_LCD_PANEL_ENVDD, 0);
-
- return;
-
-err_5:
- gpio_free(OMAP3EVM_LCD_PANEL_UD);
-err_4:
- gpio_free(OMAP3EVM_LCD_PANEL_LR);
-err_3:
- gpio_free(OMAP3EVM_LCD_PANEL_QVGA);
-err_2:
- gpio_free(OMAP3EVM_LCD_PANEL_INI);
-err_1:
- gpio_free(OMAP3EVM_LCD_PANEL_RESB);
-
+ r = gpio_request_array(omap3_evm_dss_gpios,
+ ARRAY_SIZE(omap3_evm_dss_gpios));
+ if (r)
+ printk(KERN_ERR "failed to get lcd_panel_* gpios\n");
}
static int omap3_evm_enable_lcd(struct omap_dss_device *dssdev)
@@ -448,7 +358,7 @@
static int omap3evm_twl_gpio_setup(struct device *dev,
unsigned gpio, unsigned ngpio)
{
- int r;
+ int r, lcd_bl_en;
/* gpio + 0 is "mmc0_cd" (input/IRQ) */
omap_mux_init_gpio(63, OMAP_PIN_INPUT);
@@ -465,16 +375,14 @@
*/
/* TWL4030_GPIO_MAX + 0 == ledA, LCD Backlight control */
- r = gpio_request(gpio + TWL4030_GPIO_MAX, "EN_LCD_BKL");
- if (!r)
- r = gpio_direction_output(gpio + TWL4030_GPIO_MAX,
- (get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2) ? 1 : 0);
+ lcd_bl_en = get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2 ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ r = gpio_request_one(gpio + TWL4030_GPIO_MAX, lcd_bl_en, "EN_LCD_BKL");
if (r)
printk(KERN_ERR "failed to get/set lcd_bkl gpio\n");
/* gpio + 7 == DVI Enable */
- gpio_request(gpio + 7, "EN_DVI");
- gpio_direction_output(gpio + 7, 0);
+ gpio_request_one(gpio + 7, GPIOF_OUT_INIT_LOW, "EN_DVI");
/* TWL4030_GPIO_MAX + 1 == ledB (out, active low LED) */
gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
@@ -652,78 +560,18 @@
.vdac = &omap3_evm_vdac,
.vpll2 = &omap3_evm_vpll2,
.vio = &omap3evm_vio,
-};
-
-static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &omap3evm_twldata,
- },
+ .vmmc1 = &omap3evm_vmmc1,
+ .vsim = &omap3evm_vsim,
};
static int __init omap3_evm_i2c_init(void)
{
- /*
- * REVISIT: These entries can be set in omap3evm_twl_data
- * after a merge with MFD tree
- */
- omap3evm_twldata.vmmc1 = &omap3evm_vmmc1;
- omap3evm_twldata.vsim = &omap3evm_vsim;
-
- omap_register_i2c_bus(1, 2600, omap3evm_i2c_boardinfo,
- ARRAY_SIZE(omap3evm_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &omap3evm_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
}
-static void ads7846_dev_init(void)
-{
- if (gpio_request(OMAP3_EVM_TS_GPIO, "ADS7846 pendown") < 0)
- printk(KERN_ERR "can't get ads7846 pen down GPIO\n");
-
- gpio_direction_input(OMAP3_EVM_TS_GPIO);
- gpio_set_debounce(OMAP3_EVM_TS_GPIO, 310);
-}
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(OMAP3_EVM_TS_GPIO);
-}
-
-static struct ads7846_platform_data ads7846_config = {
- .x_max = 0x0fff,
- .y_max = 0x0fff,
- .x_plate_ohms = 180,
- .pressure_max = 255,
- .debounce_max = 10,
- .debounce_tol = 3,
- .debounce_rep = 1,
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
- .settle_delay_usecs = 150,
- .wakeup = true,
-};
-
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static struct spi_board_info omap3evm_spi_board_info[] = {
- [0] = {
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(OMAP3_EVM_TS_GPIO),
- .platform_data = &ads7846_config,
- },
-};
-
static struct omap_board_config_kernel omap3_evm_config[] __initdata = {
};
@@ -825,6 +673,11 @@
.power = 100,
};
+static struct gpio omap3_evm_ehci_gpios[] __initdata = {
+ { OMAP3_EVM_EHCI_VBUS, GPIOF_OUT_INIT_HIGH, "enable EHCI VBUS" },
+ { OMAP3_EVM_EHCI_SELECT, GPIOF_OUT_INIT_LOW, "select EHCI port" },
+};
+
static void __init omap3_evm_init(void)
{
omap3_evm_get_revision();
@@ -841,9 +694,6 @@
omap_display_init(&omap3_evm_dss_data);
- spi_register_board_info(omap3evm_spi_board_info,
- ARRAY_SIZE(omap3evm_spi_board_info));
-
omap_serial_init();
/* OMAP3EVM uses ISP1504 phy and so register nop transceiver */
@@ -851,16 +701,12 @@
if (get_omap3_evm_rev() >= OMAP3EVM_BOARD_GEN_2) {
/* enable EHCI VBUS using GPIO22 */
- omap_mux_init_gpio(22, OMAP_PIN_INPUT_PULLUP);
- gpio_request(OMAP3_EVM_EHCI_VBUS, "enable EHCI VBUS");
- gpio_direction_output(OMAP3_EVM_EHCI_VBUS, 0);
- gpio_set_value(OMAP3_EVM_EHCI_VBUS, 1);
-
+ omap_mux_init_gpio(OMAP3_EVM_EHCI_VBUS, OMAP_PIN_INPUT_PULLUP);
/* Select EHCI port on main board */
- omap_mux_init_gpio(61, OMAP_PIN_INPUT_PULLUP);
- gpio_request(OMAP3_EVM_EHCI_SELECT, "select EHCI port");
- gpio_direction_output(OMAP3_EVM_EHCI_SELECT, 0);
- gpio_set_value(OMAP3_EVM_EHCI_SELECT, 0);
+ omap_mux_init_gpio(OMAP3_EVM_EHCI_SELECT,
+ OMAP_PIN_INPUT_PULLUP);
+ gpio_request_array(omap3_evm_ehci_gpios,
+ ARRAY_SIZE(omap3_evm_ehci_gpios));
/* setup EHCI phy reset config */
omap_mux_init_gpio(21, OMAP_PIN_INPUT_PULLUP);
@@ -876,7 +722,7 @@
}
usb_musb_init(&musb_board_data);
usbhs_init(&usbhs_bdata);
- ads7846_dev_init();
+ omap_ads7846_init(1, OMAP3_EVM_TS_GPIO, 310, NULL);
omap3evm_init_smsc911x();
omap3_evm_display_init();
diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c
index b726943..60d9be4 100644
--- a/arch/arm/mach-omap2/board-omap3logic.c
+++ b/arch/arm/mach-omap2/board-omap3logic.c
@@ -37,6 +37,7 @@
#include "hsmmc.h"
#include "timer-gp.h"
#include "control.h"
+#include "common-board-devices.h"
#include <plat/mux.h>
#include <plat/board.h>
@@ -93,19 +94,9 @@
.vmmc1 = &omap3logic_vmmc1,
};
-static struct i2c_board_info __initdata omap3logic_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &omap3logic_twldata,
- },
-};
-
static int __init omap3logic_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, omap3logic_i2c_boardinfo,
- ARRAY_SIZE(omap3logic_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &omap3logic_twldata);
return 0;
}
@@ -147,7 +138,6 @@
.cs = OMAP3LOGIC_SMSC911X_CS,
.gpio_irq = -EINVAL,
.gpio_reset = -EINVAL,
- .flags = IORESOURCE_IRQ_LOWLEVEL,
};
/* TODO/FIXME (comment by Peter Barada, LogicPD):
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index 1db1549..1d10736 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -22,7 +22,6 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/spi/ads7846.h>
#include <linux/regulator/machine.h>
#include <linux/i2c/twl.h>
#include <linux/wl12xx.h>
@@ -52,6 +51,7 @@
#include "mux.h"
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define PANDORA_WIFI_IRQ_GPIO 21
#define PANDORA_WIFI_NRESET_GPIO 23
@@ -305,24 +305,13 @@
/* gpio + 13 drives 32kHz buffer for wifi module */
gpio_32khz = gpio + 13;
- ret = gpio_request(gpio_32khz, "wifi 32kHz");
+ ret = gpio_request_one(gpio_32khz, GPIOF_OUT_INIT_HIGH, "wifi 32kHz");
if (ret < 0) {
pr_err("Cannot get GPIO line %d, ret=%d\n", gpio_32khz, ret);
- goto fail;
- }
-
- ret = gpio_direction_output(gpio_32khz, 1);
- if (ret < 0) {
- pr_err("Cannot set GPIO line %d, ret=%d\n", gpio_32khz, ret);
- goto fail_direction;
+ return -ENODEV;
}
return 0;
-
-fail_direction:
- gpio_free(gpio_32khz);
-fail:
- return -ENODEV;
}
static struct twl4030_gpio_platform_data omap3pandora_gpio_data = {
@@ -544,15 +533,6 @@
.bci = &pandora_bci_data,
};
-static struct i2c_board_info __initdata omap3pandora_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("tps65950", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &omap3pandora_twldata,
- },
-};
-
static struct i2c_board_info __initdata omap3pandora_i2c3_boardinfo[] = {
{
I2C_BOARD_INFO("bq27500", 0x55),
@@ -562,61 +542,15 @@
static int __init omap3pandora_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, omap3pandora_i2c_boardinfo,
- ARRAY_SIZE(omap3pandora_i2c_boardinfo));
+ omap3_pmic_init("tps65950", &omap3pandora_twldata);
/* i2c2 pins are not connected */
omap_register_i2c_bus(3, 100, omap3pandora_i2c3_boardinfo,
ARRAY_SIZE(omap3pandora_i2c3_boardinfo));
return 0;
}
-static void __init omap3pandora_ads7846_init(void)
-{
- int gpio = OMAP3_PANDORA_TS_GPIO;
- int ret;
-
- ret = gpio_request(gpio, "ads7846_pen_down");
- if (ret < 0) {
- printk(KERN_ERR "Failed to request GPIO %d for "
- "ads7846 pen down IRQ\n", gpio);
- return;
- }
-
- gpio_direction_input(gpio);
-}
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(OMAP3_PANDORA_TS_GPIO);
-}
-
-static struct ads7846_platform_data ads7846_config = {
- .x_max = 0x0fff,
- .y_max = 0x0fff,
- .x_plate_ohms = 180,
- .pressure_max = 255,
- .debounce_max = 10,
- .debounce_tol = 3,
- .debounce_rep = 1,
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
-};
-
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
static struct spi_board_info omap3pandora_spi_board_info[] __initdata = {
{
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(OMAP3_PANDORA_TS_GPIO),
- .platform_data = &ads7846_config,
- }, {
.modalias = "tpo_td043mtea1_panel_spi",
.bus_num = 1,
.chip_select = 1,
@@ -639,14 +573,10 @@
memset(&pandora_wl1251_pdata, 0, sizeof(pandora_wl1251_pdata));
- ret = gpio_request(PANDORA_WIFI_IRQ_GPIO, "wl1251 irq");
+ ret = gpio_request_one(PANDORA_WIFI_IRQ_GPIO, GPIOF_IN, "wl1251 irq");
if (ret < 0)
goto fail;
- ret = gpio_direction_input(PANDORA_WIFI_IRQ_GPIO);
- if (ret < 0)
- goto fail_irq;
-
pandora_wl1251_pdata.irq = gpio_to_irq(PANDORA_WIFI_IRQ_GPIO);
if (pandora_wl1251_pdata.irq < 0)
goto fail_irq;
@@ -688,12 +618,6 @@
};
#endif
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static void __init omap3pandora_init(void)
{
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
@@ -705,9 +629,9 @@
omap_serial_init();
spi_register_board_info(omap3pandora_spi_board_info,
ARRAY_SIZE(omap3pandora_spi_board_info));
- omap3pandora_ads7846_init();
+ omap_ads7846_init(1, OMAP3_PANDORA_TS_GPIO, 0, NULL);
usbhs_init(&usbhs_bdata);
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
gpmc_nand_init(&pandora_nand_data);
/* Ensure SDRC pins are mux'd for self-refresh */
diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c
index a72c90a..0c108a2 100644
--- a/arch/arm/mach-omap2/board-omap3stalker.c
+++ b/arch/arm/mach-omap2/board-omap3stalker.c
@@ -45,7 +45,6 @@
#include <plat/mcspi.h>
#include <linux/input/matrix_keypad.h>
#include <linux/spi/spi.h>
-#include <linux/spi/ads7846.h>
#include <linux/interrupt.h>
#include <linux/smsc911x.h>
#include <linux/i2c/at24.h>
@@ -54,52 +53,28 @@
#include "mux.h"
#include "hsmmc.h"
#include "timer-gp.h"
+#include "common-board-devices.h"
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+#include <plat/gpmc-smsc911x.h>
+
#define OMAP3STALKER_ETHR_START 0x2c000000
#define OMAP3STALKER_ETHR_SIZE 1024
#define OMAP3STALKER_ETHR_GPIO_IRQ 19
#define OMAP3STALKER_SMC911X_CS 5
-static struct resource omap3stalker_smsc911x_resources[] = {
- [0] = {
- .start = OMAP3STALKER_ETHR_START,
- .end =
- (OMAP3STALKER_ETHR_START + OMAP3STALKER_ETHR_SIZE - 1),
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = OMAP_GPIO_IRQ(OMAP3STALKER_ETHR_GPIO_IRQ),
- .end = OMAP_GPIO_IRQ(OMAP3STALKER_ETHR_GPIO_IRQ),
- .flags = (IORESOURCE_IRQ | IRQF_TRIGGER_LOW),
- },
-};
-
-static struct smsc911x_platform_config smsc911x_config = {
- .phy_interface = PHY_INTERFACE_MODE_MII,
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
+static struct omap_smsc911x_platform_data smsc911x_cfg = {
+ .cs = OMAP3STALKER_SMC911X_CS,
+ .gpio_irq = OMAP3STALKER_ETHR_GPIO_IRQ,
+ .gpio_reset = -EINVAL,
.flags = (SMSC911X_USE_32BIT | SMSC911X_SAVE_MAC_ADDRESS),
};
-static struct platform_device omap3stalker_smsc911x_device = {
- .name = "smsc911x",
- .id = -1,
- .num_resources = ARRAY_SIZE(omap3stalker_smsc911x_resources),
- .resource = &omap3stalker_smsc911x_resources[0],
- .dev = {
- .platform_data = &smsc911x_config,
- },
-};
-
static inline void __init omap3stalker_init_eth(void)
{
- int eth_cs;
struct clk *l3ck;
unsigned int rate;
- eth_cs = OMAP3STALKER_SMC911X_CS;
-
l3ck = clk_get(NULL, "l3_ck");
if (IS_ERR(l3ck))
rate = 100000000;
@@ -107,16 +82,7 @@
rate = clk_get_rate(l3ck);
omap_mux_init_gpio(19, OMAP_PIN_INPUT_PULLUP);
- if (gpio_request(OMAP3STALKER_ETHR_GPIO_IRQ, "SMC911x irq") < 0) {
- printk(KERN_ERR
- "Failed to request GPIO%d for smc911x IRQ\n",
- OMAP3STALKER_ETHR_GPIO_IRQ);
- return;
- }
-
- gpio_direction_input(OMAP3STALKER_ETHR_GPIO_IRQ);
-
- platform_device_register(&omap3stalker_smsc911x_device);
+ gpmc_smsc911x_init(&smsc911x_cfg);
}
#else
@@ -365,12 +331,11 @@
*/
/* TWL4030_GPIO_MAX + 0 == ledA, LCD Backlight control */
- gpio_request(gpio + TWL4030_GPIO_MAX, "EN_LCD_BKL");
- gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0);
+ gpio_request_one(gpio + TWL4030_GPIO_MAX, GPIOF_OUT_INIT_LOW,
+ "EN_LCD_BKL");
/* gpio + 7 == DVI Enable */
- gpio_request(gpio + 7, "EN_DVI");
- gpio_direction_output(gpio + 7, 0);
+ gpio_request_one(gpio + 7, GPIOF_OUT_INIT_LOW, "EN_DVI");
/* TWL4030_GPIO_MAX + 1 == ledB (out, mmc0) */
gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
@@ -489,15 +454,8 @@
.codec = &omap3stalker_codec_data,
.vdac = &omap3_stalker_vdac,
.vpll2 = &omap3_stalker_vpll2,
-};
-
-static struct i2c_board_info __initdata omap3stalker_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &omap3stalker_twldata,
- },
+ .vmmc1 = &omap3stalker_vmmc1,
+ .vsim = &omap3stalker_vsim,
};
static struct at24_platform_data fram_info = {
@@ -516,15 +474,7 @@
static int __init omap3_stalker_i2c_init(void)
{
- /*
- * REVISIT: These entries can be set in omap3evm_twl_data
- * after a merge with MFD tree
- */
- omap3stalker_twldata.vmmc1 = &omap3stalker_vmmc1;
- omap3stalker_twldata.vsim = &omap3stalker_vsim;
-
- omap_register_i2c_bus(1, 2600, omap3stalker_i2c_boardinfo,
- ARRAY_SIZE(omap3stalker_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &omap3stalker_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, omap3stalker_i2c_boardinfo3,
ARRAY_SIZE(omap3stalker_i2c_boardinfo3));
@@ -532,49 +482,6 @@
}
#define OMAP3_STALKER_TS_GPIO 175
-static void ads7846_dev_init(void)
-{
- if (gpio_request(OMAP3_STALKER_TS_GPIO, "ADS7846 pendown") < 0)
- printk(KERN_ERR "can't get ads7846 pen down GPIO\n");
-
- gpio_direction_input(OMAP3_STALKER_TS_GPIO);
- gpio_set_debounce(OMAP3_STALKER_TS_GPIO, 310);
-}
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(OMAP3_STALKER_TS_GPIO);
-}
-
-static struct ads7846_platform_data ads7846_config = {
- .x_max = 0x0fff,
- .y_max = 0x0fff,
- .x_plate_ohms = 180,
- .pressure_max = 255,
- .debounce_max = 10,
- .debounce_tol = 3,
- .debounce_rep = 1,
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
- .settle_delay_usecs = 150,
-};
-
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static struct spi_board_info omap3stalker_spi_board_info[] = {
- [0] = {
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(OMAP3_STALKER_TS_GPIO),
- .platform_data = &ads7846_config,
- },
-};
static struct omap_board_config_kernel omap3_stalker_config[] __initdata = {
};
@@ -618,12 +525,6 @@
};
#endif
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static void __init omap3_stalker_init(void)
{
omap3_mux_init(board_mux, OMAP_PACKAGE_CUS);
@@ -636,13 +537,11 @@
ARRAY_SIZE(omap3_stalker_devices));
omap_display_init(&omap3_stalker_dss_data);
- spi_register_board_info(omap3stalker_spi_board_info,
- ARRAY_SIZE(omap3stalker_spi_board_info));
omap_serial_init();
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
- ads7846_dev_init();
+ omap_ads7846_init(1, OMAP3_STALKER_TS_GPIO, 310, NULL);
omap_mux_init_gpio(21, OMAP_PIN_OUTPUT);
omap_mux_init_gpio(18, OMAP_PIN_INPUT_PULLUP);
diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c
index 127cb17..82872d7 100644
--- a/arch/arm/mach-omap2/board-omap3touchbook.c
+++ b/arch/arm/mach-omap2/board-omap3touchbook.c
@@ -52,6 +52,7 @@
#include "mux.h"
#include "hsmmc.h"
#include "timer-gp.h"
+#include "common-board-devices.h"
#include <asm/setup.h>
@@ -95,15 +96,6 @@
},
};
-static struct omap_nand_platform_data omap3touchbook_nand_data = {
- .options = NAND_BUSWIDTH_16,
- .parts = omap3touchbook_nand_partitions,
- .nr_parts = ARRAY_SIZE(omap3touchbook_nand_partitions),
- .dma_channel = -1, /* disable DMA in OMAP NAND driver */
- .nand_setup = NULL,
- .dev_ready = NULL,
-};
-
#include "sdram-micron-mt46h32m32lf-6.h"
static struct omap2_hsmmc_info mmc[] = {
@@ -154,13 +146,11 @@
/* REVISIT: need ehci-omap hooks for external VBUS
* power switch and overcurrent detect
*/
-
- gpio_request(gpio + 1, "EHCI_nOC");
- gpio_direction_input(gpio + 1);
+ gpio_request_one(gpio + 1, GPIOF_IN, "EHCI_nOC");
/* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */
- gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR");
- gpio_direction_output(gpio + TWL4030_GPIO_MAX, 0);
+ gpio_request_one(gpio + TWL4030_GPIO_MAX, GPIOF_OUT_INIT_LOW,
+ "nEN_USB_PWR");
/* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
@@ -273,15 +263,6 @@
.vpll2 = &touchbook_vpll2,
};
-static struct i2c_board_info __initdata touchbook_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl4030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &touchbook_twldata,
- },
-};
-
static struct i2c_board_info __initdata touchBook_i2c_boardinfo[] = {
{
I2C_BOARD_INFO("bq27200", 0x55),
@@ -291,8 +272,7 @@
static int __init omap3_touchbook_i2c_init(void)
{
/* Standard TouchBook bus */
- omap_register_i2c_bus(1, 2600, touchbook_i2c_boardinfo,
- ARRAY_SIZE(touchbook_i2c_boardinfo));
+ omap3_pmic_init("twl4030", &touchbook_twldata);
/* Additional TouchBook bus */
omap_register_i2c_bus(3, 100, touchBook_i2c_boardinfo,
@@ -301,19 +281,7 @@
return 0;
}
-static void __init omap3_ads7846_init(void)
-{
- if (gpio_request(OMAP3_TS_GPIO, "ads7846_pen_down")) {
- printk(KERN_ERR "Failed to request GPIO %d for "
- "ads7846 pen down IRQ\n", OMAP3_TS_GPIO);
- return;
- }
-
- gpio_direction_input(OMAP3_TS_GPIO);
- gpio_set_debounce(OMAP3_TS_GPIO, 310);
-}
-
-static struct ads7846_platform_data ads7846_config = {
+static struct ads7846_platform_data ads7846_pdata = {
.x_min = 100,
.y_min = 265,
.x_max = 3950,
@@ -327,23 +295,6 @@
.keep_vref_on = 1,
};
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static struct spi_board_info omap3_ads7846_spi_board_info[] __initdata = {
- {
- .modalias = "ads7846",
- .bus_num = 4,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(OMAP3_TS_GPIO),
- .platform_data = &ads7846_config,
- }
-};
-
static struct gpio_led gpio_leds[] = {
{
.name = "touchbook::usr0",
@@ -434,39 +385,6 @@
&keys_gpio,
};
-static void __init omap3touchbook_flash_init(void)
-{
- u8 cs = 0;
- u8 nandcs = GPMC_CS_NUM + 1;
-
- /* find out the chip-select on which NAND exists */
- while (cs < GPMC_CS_NUM) {
- u32 ret = 0;
- ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
-
- if ((ret & 0xC00) == 0x800) {
- printk(KERN_INFO "Found NAND on CS%d\n", cs);
- if (nandcs > GPMC_CS_NUM)
- nandcs = cs;
- }
- cs++;
- }
-
- if (nandcs > GPMC_CS_NUM) {
- printk(KERN_INFO "NAND: Unable to find configuration "
- "in GPMC\n ");
- return;
- }
-
- if (nandcs < GPMC_CS_NUM) {
- omap3touchbook_nand_data.cs = nandcs;
-
- printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
- if (gpmc_nand_init(&omap3touchbook_nand_data) < 0)
- printk(KERN_ERR "Unable to register NAND device\n");
- }
-}
-
static const struct usbhs_omap_board_data usbhs_bdata __initconst = {
.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
@@ -481,15 +399,10 @@
static void omap3_touchbook_poweroff(void)
{
- int r;
+ int pwr_off = TB_KILL_POWER_GPIO;
- r = gpio_request(TB_KILL_POWER_GPIO, "DVI reset");
- if (r < 0) {
+ if (gpio_request_one(pwr_off, GPIOF_OUT_INIT_LOW, "DVI reset") < 0)
printk(KERN_ERR "Unable to get kill power GPIO\n");
- return;
- }
-
- gpio_direction_output(TB_KILL_POWER_GPIO, 0);
}
static int __init early_touchbook_revision(char *p)
@@ -501,12 +414,6 @@
}
early_param("tbr", early_touchbook_revision);
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static void __init omap3_touchbook_init(void)
{
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
@@ -521,17 +428,15 @@
omap_serial_init();
omap_mux_init_gpio(170, OMAP_PIN_INPUT);
- gpio_request(176, "DVI_nPD");
/* REVISIT leave DVI powered down until it's needed ... */
- gpio_direction_output(176, true);
+ gpio_request_one(176, GPIOF_OUT_INIT_HIGH, "DVI_nPD");
/* Touchscreen and accelerometer */
- spi_register_board_info(omap3_ads7846_spi_board_info,
- ARRAY_SIZE(omap3_ads7846_spi_board_info));
- omap3_ads7846_init();
- usb_musb_init(&musb_board_data);
+ omap_ads7846_init(4, OMAP3_TS_GPIO, 310, &ads7846_pdata);
+ usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
- omap3touchbook_flash_init();
+ omap_nand_flash_init(NAND_BUSWIDTH_16, omap3touchbook_nand_partitions,
+ ARRAY_SIZE(omap3touchbook_nand_partitions));
/* Ensure SDRC pins are mux'd for self-refresh */
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index e4973ac..90485fc 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -46,6 +46,7 @@
#include "hsmmc.h"
#include "control.h"
#include "mux.h"
+#include "common-board-devices.h"
#define GPIO_HUB_POWER 1
#define GPIO_HUB_NRESET 62
@@ -111,6 +112,11 @@
.reset_gpio_port[2] = -EINVAL
};
+static struct gpio panda_ehci_gpios[] __initdata = {
+ { GPIO_HUB_POWER, GPIOF_OUT_INIT_LOW, "hub_power" },
+ { GPIO_HUB_NRESET, GPIOF_OUT_INIT_LOW, "hub_nreset" },
+};
+
static void __init omap4_ehci_init(void)
{
int ret;
@@ -120,44 +126,27 @@
phy_ref_clk = clk_get(NULL, "auxclk3_ck");
if (IS_ERR(phy_ref_clk)) {
pr_err("Cannot request auxclk3\n");
- goto error1;
+ return;
}
clk_set_rate(phy_ref_clk, 19200000);
clk_enable(phy_ref_clk);
- /* disable the power to the usb hub prior to init */
- ret = gpio_request(GPIO_HUB_POWER, "hub_power");
+ /* disable the power to the usb hub prior to init and reset phy+hub */
+ ret = gpio_request_array(panda_ehci_gpios,
+ ARRAY_SIZE(panda_ehci_gpios));
if (ret) {
- pr_err("Cannot request GPIO %d\n", GPIO_HUB_POWER);
- goto error1;
+ pr_err("Unable to initialize EHCI power/reset\n");
+ return;
}
- gpio_export(GPIO_HUB_POWER, 0);
- gpio_direction_output(GPIO_HUB_POWER, 0);
- gpio_set_value(GPIO_HUB_POWER, 0);
- /* reset phy+hub */
- ret = gpio_request(GPIO_HUB_NRESET, "hub_nreset");
- if (ret) {
- pr_err("Cannot request GPIO %d\n", GPIO_HUB_NRESET);
- goto error2;
- }
+ gpio_export(GPIO_HUB_POWER, 0);
gpio_export(GPIO_HUB_NRESET, 0);
- gpio_direction_output(GPIO_HUB_NRESET, 0);
- gpio_set_value(GPIO_HUB_NRESET, 0);
gpio_set_value(GPIO_HUB_NRESET, 1);
usbhs_init(&usbhs_bdata);
/* enable power to hub */
gpio_set_value(GPIO_HUB_POWER, 1);
- return;
-
-error2:
- gpio_free(GPIO_HUB_POWER);
-error1:
- pr_err("Unable to initialize EHCI power/reset\n");
- return;
-
}
static struct omap_musb_board_data musb_board_data = {
@@ -408,15 +397,6 @@
.usb = &omap4_usbphy_data,
};
-static struct i2c_board_info __initdata omap4_panda_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl6030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = OMAP44XX_IRQ_SYS_1N,
- .platform_data = &omap4_panda_twldata,
- },
-};
-
/*
* Display monitor features are burnt in their EEPROM as EDID data. The EEPROM
* is connected as I2C slave device, and can be accessed at address 0x50
@@ -429,12 +409,7 @@
static int __init omap4_panda_i2c_init(void)
{
- /*
- * Phoenix Audio IC needs I2C1 to
- * start with 400 KHz or less
- */
- omap_register_i2c_bus(1, 400, omap4_panda_i2c_boardinfo,
- ARRAY_SIZE(omap4_panda_i2c_boardinfo));
+ omap4_pmic_init("twl6030", &omap4_panda_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
/*
* Bus 3 is attached to the DVI port where devices like the pico DLP
@@ -651,27 +626,19 @@
OMAP_PIN_INPUT_PULLUP);
}
+static struct gpio panda_hdmi_gpios[] = {
+ { HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_hpd" },
+ { HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH, "hdmi_gpio_ls_oe" },
+};
+
static int omap4_panda_panel_enable_hdmi(struct omap_dss_device *dssdev)
{
int status;
- status = gpio_request_one(HDMI_GPIO_HPD, GPIOF_OUT_INIT_HIGH,
- "hdmi_gpio_hpd");
- if (status) {
- pr_err("Cannot request GPIO %d\n", HDMI_GPIO_HPD);
- return status;
- }
- status = gpio_request_one(HDMI_GPIO_LS_OE, GPIOF_OUT_INIT_HIGH,
- "hdmi_gpio_ls_oe");
- if (status) {
- pr_err("Cannot request GPIO %d\n", HDMI_GPIO_LS_OE);
- goto error1;
- }
-
- return 0;
-
-error1:
- gpio_free(HDMI_GPIO_HPD);
+ status = gpio_request_array(panda_hdmi_gpios,
+ ARRAY_SIZE(panda_hdmi_gpios));
+ if (status)
+ pr_err("Cannot request HDMI GPIOs\n");
return status;
}
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index 9d192ff..1555918 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -56,6 +56,7 @@
#include "mux.h"
#include "sdram-micron-mt46h32m32lf-6.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define OVERO_GPIO_BT_XGATE 15
#define OVERO_GPIO_W2W_NRESET 16
@@ -74,30 +75,6 @@
#if defined(CONFIG_TOUCHSCREEN_ADS7846) || \
defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
-#include <linux/spi/ads7846.h>
-
-static struct omap2_mcspi_device_config ads7846_mcspi_config = {
- .turbo_mode = 0,
- .single_channel = 1, /* 0: slave, 1: master */
-};
-
-static int ads7846_get_pendown_state(void)
-{
- return !gpio_get_value(OVERO_GPIO_PENDOWN);
-}
-
-static struct ads7846_platform_data ads7846_config = {
- .x_max = 0x0fff,
- .y_max = 0x0fff,
- .x_plate_ohms = 180,
- .pressure_max = 255,
- .debounce_max = 10,
- .debounce_tol = 3,
- .debounce_rep = 1,
- .get_pendown_state = ads7846_get_pendown_state,
- .keep_vref_on = 1,
-};
-
/* fixed regulator for ads7846 */
static struct regulator_consumer_supply ads7846_supply =
REGULATOR_SUPPLY("vcc", "spi1.0");
@@ -128,14 +105,7 @@
static void __init overo_ads7846_init(void)
{
- if ((gpio_request(OVERO_GPIO_PENDOWN, "ADS7846_PENDOWN") == 0) &&
- (gpio_direction_input(OVERO_GPIO_PENDOWN) == 0)) {
- gpio_export(OVERO_GPIO_PENDOWN, 0);
- } else {
- printk(KERN_ERR "could not obtain gpio for ADS7846_PENDOWN\n");
- return;
- }
-
+ omap_ads7846_init(1, OVERO_GPIO_PENDOWN, 0, NULL);
platform_device_register(&vads7846_device);
}
@@ -146,106 +116,28 @@
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
#include <linux/smsc911x.h>
+#include <plat/gpmc-smsc911x.h>
-static struct resource overo_smsc911x_resources[] = {
- {
- .name = "smsc911x-memory",
- .flags = IORESOURCE_MEM,
- },
- {
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct resource overo_smsc911x2_resources[] = {
- {
- .name = "smsc911x2-memory",
- .flags = IORESOURCE_MEM,
- },
- {
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct smsc911x_platform_config overo_smsc911x_config = {
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
- .flags = SMSC911X_USE_32BIT ,
- .phy_interface = PHY_INTERFACE_MODE_MII,
-};
-
-static struct platform_device overo_smsc911x_device = {
- .name = "smsc911x",
+static struct omap_smsc911x_platform_data smsc911x_cfg = {
.id = 0,
- .num_resources = ARRAY_SIZE(overo_smsc911x_resources),
- .resource = overo_smsc911x_resources,
- .dev = {
- .platform_data = &overo_smsc911x_config,
- },
+ .cs = OVERO_SMSC911X_CS,
+ .gpio_irq = OVERO_SMSC911X_GPIO,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT,
};
-static struct platform_device overo_smsc911x2_device = {
- .name = "smsc911x",
+static struct omap_smsc911x_platform_data smsc911x2_cfg = {
.id = 1,
- .num_resources = ARRAY_SIZE(overo_smsc911x2_resources),
- .resource = overo_smsc911x2_resources,
- .dev = {
- .platform_data = &overo_smsc911x_config,
- },
+ .cs = OVERO_SMSC911X2_CS,
+ .gpio_irq = OVERO_SMSC911X2_GPIO,
+ .gpio_reset = -EINVAL,
+ .flags = SMSC911X_USE_32BIT,
};
-static struct platform_device *smsc911x_devices[] = {
- &overo_smsc911x_device,
- &overo_smsc911x2_device,
-};
-
-static inline void __init overo_init_smsc911x(void)
+static void __init overo_init_smsc911x(void)
{
- unsigned long cs_mem_base, cs_mem_base2;
-
- /* set up first smsc911x chip */
-
- if (gpmc_cs_request(OVERO_SMSC911X_CS, SZ_16M, &cs_mem_base) < 0) {
- printk(KERN_ERR "Failed request for GPMC mem for smsc911x\n");
- return;
- }
-
- overo_smsc911x_resources[0].start = cs_mem_base + 0x0;
- overo_smsc911x_resources[0].end = cs_mem_base + 0xff;
-
- if ((gpio_request(OVERO_SMSC911X_GPIO, "SMSC911X IRQ") == 0) &&
- (gpio_direction_input(OVERO_SMSC911X_GPIO) == 0)) {
- gpio_export(OVERO_SMSC911X_GPIO, 0);
- } else {
- printk(KERN_ERR "could not obtain gpio for SMSC911X IRQ\n");
- return;
- }
-
- overo_smsc911x_resources[1].start = OMAP_GPIO_IRQ(OVERO_SMSC911X_GPIO);
- overo_smsc911x_resources[1].end = 0;
-
- /* set up second smsc911x chip */
-
- if (gpmc_cs_request(OVERO_SMSC911X2_CS, SZ_16M, &cs_mem_base2) < 0) {
- printk(KERN_ERR "Failed request for GPMC mem for smsc911x2\n");
- return;
- }
-
- overo_smsc911x2_resources[0].start = cs_mem_base2 + 0x0;
- overo_smsc911x2_resources[0].end = cs_mem_base2 + 0xff;
-
- if ((gpio_request(OVERO_SMSC911X2_GPIO, "SMSC911X2 IRQ") == 0) &&
- (gpio_direction_input(OVERO_SMSC911X2_GPIO) == 0)) {
- gpio_export(OVERO_SMSC911X2_GPIO, 0);
- } else {
- printk(KERN_ERR "could not obtain gpio for SMSC911X2 IRQ\n");
- return;
- }
-
- overo_smsc911x2_resources[1].start = OMAP_GPIO_IRQ(OVERO_SMSC911X2_GPIO);
- overo_smsc911x2_resources[1].end = 0;
-
- platform_add_devices(smsc911x_devices, ARRAY_SIZE(smsc911x_devices));
+ gpmc_smsc911x_init(&smsc911x_cfg);
+ gpmc_smsc911x_init(&smsc911x2_cfg);
}
#else
@@ -259,21 +151,20 @@
#define OVERO_GPIO_LCD_EN 144
#define OVERO_GPIO_LCD_BL 145
+static struct gpio overo_dss_gpios[] __initdata = {
+ { OVERO_GPIO_LCD_EN, GPIOF_OUT_INIT_HIGH, "OVERO_GPIO_LCD_EN" },
+ { OVERO_GPIO_LCD_BL, GPIOF_OUT_INIT_HIGH, "OVERO_GPIO_LCD_BL" },
+};
+
static void __init overo_display_init(void)
{
- if ((gpio_request(OVERO_GPIO_LCD_EN, "OVERO_GPIO_LCD_EN") == 0) &&
- (gpio_direction_output(OVERO_GPIO_LCD_EN, 1) == 0))
- gpio_export(OVERO_GPIO_LCD_EN, 0);
- else
- printk(KERN_ERR "could not obtain gpio for "
- "OVERO_GPIO_LCD_EN\n");
+ if (gpio_request_array(overo_dss_gpios, ARRAY_SIZE(overo_dss_gpios))) {
+ printk(KERN_ERR "could not obtain DSS control GPIOs\n");
+ return;
+ }
- if ((gpio_request(OVERO_GPIO_LCD_BL, "OVERO_GPIO_LCD_BL") == 0) &&
- (gpio_direction_output(OVERO_GPIO_LCD_BL, 1) == 0))
- gpio_export(OVERO_GPIO_LCD_BL, 0);
- else
- printk(KERN_ERR "could not obtain gpio for "
- "OVERO_GPIO_LCD_BL\n");
+ gpio_export(OVERO_GPIO_LCD_EN, 0);
+ gpio_export(OVERO_GPIO_LCD_BL, 0);
}
static int overo_panel_enable_dvi(struct omap_dss_device *dssdev)
@@ -412,45 +303,6 @@
},
};
-static struct omap_nand_platform_data overo_nand_data = {
- .parts = overo_nand_partitions,
- .nr_parts = ARRAY_SIZE(overo_nand_partitions),
- .dma_channel = -1, /* disable DMA in OMAP NAND driver */
-};
-
-static void __init overo_flash_init(void)
-{
- u8 cs = 0;
- u8 nandcs = GPMC_CS_NUM + 1;
-
- /* find out the chip-select on which NAND exists */
- while (cs < GPMC_CS_NUM) {
- u32 ret = 0;
- ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
-
- if ((ret & 0xC00) == 0x800) {
- printk(KERN_INFO "Found NAND on CS%d\n", cs);
- if (nandcs > GPMC_CS_NUM)
- nandcs = cs;
- }
- cs++;
- }
-
- if (nandcs > GPMC_CS_NUM) {
- printk(KERN_INFO "NAND: Unable to find configuration "
- "in GPMC\n ");
- return;
- }
-
- if (nandcs < GPMC_CS_NUM) {
- overo_nand_data.cs = nandcs;
-
- printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
- if (gpmc_nand_init(&overo_nand_data) < 0)
- printk(KERN_ERR "Unable to register NAND device\n");
- }
-}
-
static struct omap2_hsmmc_info mmc[] = {
{
.mmc = 1,
@@ -648,37 +500,15 @@
.vpll2 = &overo_vpll2,
};
-static struct i2c_board_info __initdata overo_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("tps65950", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &overo_twldata,
- },
-};
-
static int __init overo_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, overo_i2c_boardinfo,
- ARRAY_SIZE(overo_i2c_boardinfo));
+ omap3_pmic_init("tps65950", &overo_twldata);
/* i2c2 pins are used for gpio */
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
}
static struct spi_board_info overo_spi_board_info[] __initdata = {
-#if defined(CONFIG_TOUCHSCREEN_ADS7846) || \
- defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
- {
- .modalias = "ads7846",
- .bus_num = 1,
- .chip_select = 0,
- .max_speed_hz = 1500000,
- .controller_data = &ads7846_mcspi_config,
- .irq = OMAP_GPIO_IRQ(OVERO_GPIO_PENDOWN),
- .platform_data = &ads7846_config,
- },
-#endif
#if defined(CONFIG_PANEL_LGPHILIPS_LB035Q02) || \
defined(CONFIG_PANEL_LGPHILIPS_LB035Q02_MODULE)
{
@@ -722,20 +552,22 @@
};
#endif
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
+static struct gpio overo_bt_gpios[] __initdata = {
+ { OVERO_GPIO_BT_XGATE, GPIOF_OUT_INIT_LOW, "lcd enable" },
+ { OVERO_GPIO_BT_NRESET, GPIOF_OUT_INIT_HIGH, "lcd bl enable" },
};
static void __init overo_init(void)
{
+ int ret;
+
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
overo_i2c_init();
omap_display_init(&overo_dss_data);
omap_serial_init();
- overo_flash_init();
- usb_musb_init(&musb_board_data);
+ omap_nand_flash_init(0, overo_nand_partitions,
+ ARRAY_SIZE(overo_nand_partitions));
+ usb_musb_init(NULL);
usbhs_init(&usbhs_bdata);
overo_spi_init();
overo_ads7846_init();
@@ -748,9 +580,9 @@
omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT);
- if ((gpio_request(OVERO_GPIO_W2W_NRESET,
- "OVERO_GPIO_W2W_NRESET") == 0) &&
- (gpio_direction_output(OVERO_GPIO_W2W_NRESET, 1) == 0)) {
+ ret = gpio_request_one(OVERO_GPIO_W2W_NRESET, GPIOF_OUT_INIT_HIGH,
+ "OVERO_GPIO_W2W_NRESET");
+ if (ret == 0) {
gpio_export(OVERO_GPIO_W2W_NRESET, 0);
gpio_set_value(OVERO_GPIO_W2W_NRESET, 0);
udelay(10);
@@ -760,25 +592,20 @@
"OVERO_GPIO_W2W_NRESET\n");
}
- if ((gpio_request(OVERO_GPIO_BT_XGATE, "OVERO_GPIO_BT_XGATE") == 0) &&
- (gpio_direction_output(OVERO_GPIO_BT_XGATE, 0) == 0))
+ ret = gpio_request_array(overo_bt_gpios, ARRAY_SIZE(overo_bt_gpios));
+ if (ret) {
+ pr_err("%s: could not obtain BT gpios\n", __func__);
+ } else {
gpio_export(OVERO_GPIO_BT_XGATE, 0);
- else
- printk(KERN_ERR "could not obtain gpio for OVERO_GPIO_BT_XGATE\n");
-
- if ((gpio_request(OVERO_GPIO_BT_NRESET, "OVERO_GPIO_BT_NRESET") == 0) &&
- (gpio_direction_output(OVERO_GPIO_BT_NRESET, 1) == 0)) {
gpio_export(OVERO_GPIO_BT_NRESET, 0);
gpio_set_value(OVERO_GPIO_BT_NRESET, 0);
mdelay(6);
gpio_set_value(OVERO_GPIO_BT_NRESET, 1);
- } else {
- printk(KERN_ERR "could not obtain gpio for "
- "OVERO_GPIO_BT_NRESET\n");
}
- if ((gpio_request(OVERO_GPIO_USBH_CPEN, "OVERO_GPIO_USBH_CPEN") == 0) &&
- (gpio_direction_output(OVERO_GPIO_USBH_CPEN, 1) == 0))
+ ret = gpio_request_one(OVERO_GPIO_USBH_CPEN, GPIOF_OUT_INIT_HIGH,
+ "OVERO_GPIO_USBH_CPEN");
+ if (ret == 0)
gpio_export(OVERO_GPIO_USBH_CPEN, 0);
else
printk(KERN_ERR "could not obtain gpio for "
diff --git a/arch/arm/mach-omap2/board-rm680.c b/arch/arm/mach-omap2/board-rm680.c
index 2af8b05..42d10b1 100644
--- a/arch/arm/mach-omap2/board-rm680.c
+++ b/arch/arm/mach-omap2/board-rm680.c
@@ -31,6 +31,7 @@
#include "mux.h"
#include "hsmmc.h"
#include "sdram-nokia.h"
+#include "common-board-devices.h"
static struct regulator_consumer_supply rm680_vemmc_consumers[] = {
REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"),
@@ -90,19 +91,9 @@
/* add rest of the children here */
};
-static struct i2c_board_info __initdata rm680_twl_i2c_board_info[] = {
- {
- I2C_BOARD_INFO("twl5031", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &rm680_twl_data,
- },
-};
-
static void __init rm680_i2c_init(void)
{
- omap_register_i2c_bus(1, 2900, rm680_twl_i2c_board_info,
- ARRAY_SIZE(rm680_twl_i2c_board_info));
+ omap_pmic_init(1, 2900, "twl5031", INT_34XX_SYS_NIRQ, &rm680_twl_data);
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, NULL, 0);
}
@@ -153,17 +144,11 @@
};
#endif
-static struct omap_musb_board_data rm680_musb_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_PERIPHERAL,
- .power = 100,
-};
-
static void __init rm680_init(void)
{
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
omap_serial_init();
- usb_musb_init(&rm680_musb_data);
+ usb_musb_init(NULL);
rm680_peripherals_init();
}
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index bbcb677..f6247e7 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/mmc/host.h>
+#include <linux/power/isp1704_charger.h>
#include <plat/mcspi.h>
#include <plat/board.h>
@@ -43,6 +44,7 @@
#include "mux.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define SYSTEM_REV_B_USES_VAUX3 0x1699
#define SYSTEM_REV_S_USES_VAUX3 0x8
@@ -52,6 +54,8 @@
#define RX51_FMTX_RESET_GPIO 163
#define RX51_FMTX_IRQ 53
+#define RX51_USB_TRANSCEIVER_RST_GPIO 67
+
/* list all spi devices here */
enum {
RX51_SPI_WL1251,
@@ -110,10 +114,30 @@
},
};
-static struct platform_device rx51_charger_device = {
- .name = "isp1704_charger",
+static void rx51_charger_set_power(bool on)
+{
+ gpio_set_value(RX51_USB_TRANSCEIVER_RST_GPIO, on);
+}
+
+static struct isp1704_charger_data rx51_charger_data = {
+ .set_power = rx51_charger_set_power,
};
+static struct platform_device rx51_charger_device = {
+ .name = "isp1704_charger",
+ .dev = {
+ .platform_data = &rx51_charger_data,
+ },
+};
+
+static void __init rx51_charger_init(void)
+{
+ WARN_ON(gpio_request_one(RX51_USB_TRANSCEIVER_RST_GPIO,
+ GPIOF_OUT_INIT_LOW, "isp1704_reset"));
+
+ platform_device_register(&rx51_charger_device);
+}
+
#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
#define RX51_GPIO_CAMERA_LENS_COVER 110
@@ -557,10 +581,8 @@
static int rx51_twlgpio_setup(struct device *dev, unsigned gpio, unsigned n)
{
/* FIXME this gpio setup is just a placeholder for now */
- gpio_request(gpio + 6, "backlight_pwm");
- gpio_direction_output(gpio + 6, 0);
- gpio_request(gpio + 7, "speaker_en");
- gpio_direction_output(gpio + 7, 1);
+ gpio_request_one(gpio + 6, GPIOF_OUT_INIT_LOW, "backlight_pwm");
+ gpio_request_one(gpio + 7, GPIOF_OUT_INIT_HIGH, "speaker_en");
return 0;
}
@@ -730,7 +752,7 @@
{ .resource = RES_RESET, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
- { .resource = RES_Main_Ref, .devgroup = -1,
+ { .resource = RES_MAIN_REF, .devgroup = -1,
.type = 1, .type2 = -1, .remap_off = -1, .remap_sleep = -1
},
{ 0, 0},
@@ -777,15 +799,6 @@
.power_gpio = 98,
};
-static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_1[] = {
- {
- I2C_BOARD_INFO("twl5030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &rx51_twldata,
- },
-};
-
/* Audio setup data */
static struct aic3x_setup_data rx51_aic34_setup = {
.gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
@@ -833,8 +846,7 @@
rx51_twldata.vaux3 = &rx51_vaux3_cam;
}
rx51_twldata.vmmc2 = &rx51_vmmc2;
- omap_register_i2c_bus(1, 2200, rx51_peripherals_i2c_board_info_1,
- ARRAY_SIZE(rx51_peripherals_i2c_board_info_1));
+ omap_pmic_init(1, 2200, "twl5030", INT_34XX_SYS_NIRQ, &rx51_twldata);
omap_register_i2c_bus(2, 100, rx51_peripherals_i2c_board_info_2,
ARRAY_SIZE(rx51_peripherals_i2c_board_info_2));
omap_register_i2c_bus(3, 400, NULL, 0);
@@ -921,26 +933,20 @@
gpio_set_value(RX51_WL1251_POWER_GPIO, enable);
}
+static struct gpio rx51_wl1251_gpios[] __initdata = {
+ { RX51_WL1251_POWER_GPIO, GPIOF_OUT_INIT_LOW, "wl1251 power" },
+ { RX51_WL1251_IRQ_GPIO, GPIOF_IN, "wl1251 irq" },
+};
+
static void __init rx51_init_wl1251(void)
{
int irq, ret;
- ret = gpio_request(RX51_WL1251_POWER_GPIO, "wl1251 power");
+ ret = gpio_request_array(rx51_wl1251_gpios,
+ ARRAY_SIZE(rx51_wl1251_gpios));
if (ret < 0)
goto error;
- ret = gpio_direction_output(RX51_WL1251_POWER_GPIO, 0);
- if (ret < 0)
- goto err_power;
-
- ret = gpio_request(RX51_WL1251_IRQ_GPIO, "wl1251 irq");
- if (ret < 0)
- goto err_power;
-
- ret = gpio_direction_input(RX51_WL1251_IRQ_GPIO);
- if (ret < 0)
- goto err_irq;
-
irq = gpio_to_irq(RX51_WL1251_IRQ_GPIO);
if (irq < 0)
goto err_irq;
@@ -952,10 +958,7 @@
err_irq:
gpio_free(RX51_WL1251_IRQ_GPIO);
-
-err_power:
gpio_free(RX51_WL1251_POWER_GPIO);
-
error:
printk(KERN_ERR "wl1251 board initialisation failed\n");
wl1251_pdata.set_power = NULL;
@@ -981,6 +984,6 @@
if (partition)
omap2_hsmmc_init(mmc);
- platform_device_register(&rx51_charger_device);
+ rx51_charger_init();
}
diff --git a/arch/arm/mach-omap2/board-rx51-video.c b/arch/arm/mach-omap2/board-rx51-video.c
index 2df10b6..2c1289b 100644
--- a/arch/arm/mach-omap2/board-rx51-video.c
+++ b/arch/arm/mach-omap2/board-rx51-video.c
@@ -76,13 +76,12 @@
return 0;
}
- if (gpio_request(RX51_LCD_RESET_GPIO, "LCD ACX565AKM reset")) {
+ if (gpio_request_one(RX51_LCD_RESET_GPIO, GPIOF_OUT_INIT_HIGH,
+ "LCD ACX565AKM reset")) {
pr_err("%s failed to get LCD Reset GPIO\n", __func__);
return 0;
}
- gpio_direction_output(RX51_LCD_RESET_GPIO, 1);
-
omap_display_init(&rx51_dss_board_info);
return 0;
}
diff --git a/arch/arm/mach-omap2/board-rx51.c b/arch/arm/mach-omap2/board-rx51.c
index f8ba20a..fec4cac 100644
--- a/arch/arm/mach-omap2/board-rx51.c
+++ b/arch/arm/mach-omap2/board-rx51.c
@@ -58,21 +58,25 @@
},
};
+/*
+ * cpuidle C-states definition override from the default values.
+ * The 'exit_latency' field is the sum of sleep and wake-up latencies.
+ */
static struct cpuidle_params rx51_cpuidle_params[] = {
/* C1 */
- {1, 110, 162, 5},
+ {110 + 162, 5 , 1},
/* C2 */
- {1, 106, 180, 309},
+ {106 + 180, 309, 1},
/* C3 */
- {0, 107, 410, 46057},
+ {107 + 410, 46057, 0},
/* C4 */
- {0, 121, 3374, 46057},
+ {121 + 3374, 46057, 0},
/* C5 */
- {1, 855, 1146, 46057},
+ {855 + 1146, 46057, 1},
/* C6 */
- {0, 7580, 4134, 484329},
+ {7580 + 4134, 484329, 0},
/* C7 */
- {1, 7505, 15274, 484329},
+ {7505 + 15274, 484329, 1},
};
static struct omap_lcd_config rx51_lcd_config = {
diff --git a/arch/arm/mach-omap2/board-zoom-debugboard.c b/arch/arm/mach-omap2/board-zoom-debugboard.c
index 007ebdc..6402e78 100644
--- a/arch/arm/mach-omap2/board-zoom-debugboard.c
+++ b/arch/arm/mach-omap2/board-zoom-debugboard.c
@@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#include <plat/gpmc.h>
+#include <plat/gpmc-smsc911x.h>
#include <mach/board-zoom.h>
@@ -26,60 +27,16 @@
#define DEBUG_BASE 0x08000000
#define ZOOM_ETHR_START DEBUG_BASE
-static struct resource zoom_smsc911x_resources[] = {
- [0] = {
- .start = ZOOM_ETHR_START,
- .end = ZOOM_ETHR_START + SZ_4K,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
- },
-};
-
-static struct smsc911x_platform_config zoom_smsc911x_config = {
- .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
- .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN,
+static struct omap_smsc911x_platform_data zoom_smsc911x_cfg = {
+ .cs = ZOOM_SMSC911X_CS,
+ .gpio_irq = ZOOM_SMSC911X_GPIO,
+ .gpio_reset = -EINVAL,
.flags = SMSC911X_USE_32BIT,
- .phy_interface = PHY_INTERFACE_MODE_MII,
-};
-
-static struct platform_device zoom_smsc911x_device = {
- .name = "smsc911x",
- .id = -1,
- .num_resources = ARRAY_SIZE(zoom_smsc911x_resources),
- .resource = zoom_smsc911x_resources,
- .dev = {
- .platform_data = &zoom_smsc911x_config,
- },
};
static inline void __init zoom_init_smsc911x(void)
{
- int eth_cs;
- unsigned long cs_mem_base;
- int eth_gpio = 0;
-
- eth_cs = ZOOM_SMSC911X_CS;
-
- if (gpmc_cs_request(eth_cs, SZ_16M, &cs_mem_base) < 0) {
- printk(KERN_ERR "Failed to request GPMC mem for smsc911x\n");
- return;
- }
-
- zoom_smsc911x_resources[0].start = cs_mem_base + 0x0;
- zoom_smsc911x_resources[0].end = cs_mem_base + 0xff;
-
- eth_gpio = ZOOM_SMSC911X_GPIO;
-
- zoom_smsc911x_resources[1].start = OMAP_GPIO_IRQ(eth_gpio);
-
- if (gpio_request(eth_gpio, "smsc911x irq") < 0) {
- printk(KERN_ERR "Failed to request GPIO%d for smsc911x IRQ\n",
- eth_gpio);
- return;
- }
- gpio_direction_input(eth_gpio);
+ gpmc_smsc911x_init(&zoom_smsc911x_cfg);
}
static struct plat_serial8250_port serial_platform_data[] = {
@@ -120,12 +77,9 @@
quart_gpio = ZOOM_QUADUART_GPIO;
- if (gpio_request(quart_gpio, "TL16CP754C GPIO") < 0) {
+ if (gpio_request_one(quart_gpio, GPIOF_IN, "TL16CP754C GPIO") < 0)
printk(KERN_ERR "Failed to request GPIO%d for TL16CP754C\n",
quart_gpio);
- return;
- }
- gpio_direction_input(quart_gpio);
}
static inline int omap_zoom_debugboard_detect(void)
@@ -135,12 +89,12 @@
debug_board_detect = ZOOM_SMSC911X_GPIO;
- if (gpio_request(debug_board_detect, "Zoom debug board detect") < 0) {
+ if (gpio_request_one(debug_board_detect, GPIOF_IN,
+ "Zoom debug board detect") < 0) {
printk(KERN_ERR "Failed to request GPIO%d for Zoom debug"
"board detect\n", debug_board_detect);
return 0;
}
- gpio_direction_input(debug_board_detect);
if (!gpio_get_value(debug_board_detect)) {
ret = 0;
@@ -150,7 +104,6 @@
}
static struct platform_device *zoom_devices[] __initdata = {
- &zoom_smsc911x_device,
&zoom_debugboard_serial_device,
};
diff --git a/arch/arm/mach-omap2/board-zoom-display.c b/arch/arm/mach-omap2/board-zoom-display.c
index 60e8645..c7c6beb 100644
--- a/arch/arm/mach-omap2/board-zoom-display.c
+++ b/arch/arm/mach-omap2/board-zoom-display.c
@@ -21,34 +21,19 @@
#define LCD_PANEL_RESET_GPIO_PILOT 55
#define LCD_PANEL_QVGA_GPIO 56
+static struct gpio zoom_lcd_gpios[] __initdata = {
+ { -EINVAL, GPIOF_OUT_INIT_HIGH, "lcd reset" },
+ { LCD_PANEL_QVGA_GPIO, GPIOF_OUT_INIT_HIGH, "lcd qvga" },
+};
+
static void zoom_lcd_panel_init(void)
{
- int ret;
- unsigned char lcd_panel_reset_gpio;
-
- lcd_panel_reset_gpio = (omap_rev() > OMAP3430_REV_ES3_0) ?
+ zoom_lcd_gpios[0].gpio = (omap_rev() > OMAP3430_REV_ES3_0) ?
LCD_PANEL_RESET_GPIO_PROD :
LCD_PANEL_RESET_GPIO_PILOT;
- ret = gpio_request(lcd_panel_reset_gpio, "lcd reset");
- if (ret) {
- pr_err("Failed to get LCD reset GPIO (gpio%d).\n",
- lcd_panel_reset_gpio);
- return;
- }
- gpio_direction_output(lcd_panel_reset_gpio, 1);
-
- ret = gpio_request(LCD_PANEL_QVGA_GPIO, "lcd qvga");
- if (ret) {
- pr_err("Failed to get LCD_PANEL_QVGA_GPIO (gpio%d).\n",
- LCD_PANEL_QVGA_GPIO);
- goto err0;
- }
- gpio_direction_output(LCD_PANEL_QVGA_GPIO, 1);
-
- return;
-err0:
- gpio_free(lcd_panel_reset_gpio);
+ if (gpio_request_array(zoom_lcd_gpios, ARRAY_SIZE(zoom_lcd_gpios)))
+ pr_err("%s: Failed to get LCD GPIOs.\n", __func__);
}
static int zoom_panel_enable_lcd(struct omap_dss_device *dssdev)
diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c
index 8dee754..118c6f5 100644
--- a/arch/arm/mach-omap2/board-zoom-peripherals.c
+++ b/arch/arm/mach-omap2/board-zoom-peripherals.c
@@ -31,6 +31,7 @@
#include "mux.h"
#include "hsmmc.h"
+#include "common-board-devices.h"
#define OMAP_ZOOM_WLAN_PMENA_GPIO (101)
#define OMAP_ZOOM_WLAN_IRQ_GPIO (162)
@@ -276,13 +277,11 @@
zoom_vsim_supply.dev = mmc[0].dev;
zoom_vmmc2_supply.dev = mmc[1].dev;
- ret = gpio_request(LCD_PANEL_ENABLE_GPIO, "lcd enable");
- if (ret) {
+ ret = gpio_request_one(LCD_PANEL_ENABLE_GPIO, GPIOF_OUT_INIT_LOW,
+ "lcd enable");
+ if (ret)
pr_err("Failed to get LCD_PANEL_ENABLE_GPIO (gpio%d).\n",
LCD_PANEL_ENABLE_GPIO);
- return ret;
- }
- gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0);
return ret;
}
@@ -349,15 +348,6 @@
.vdac = &zoom_vdac,
};
-static struct i2c_board_info __initdata zoom_i2c_boardinfo[] = {
- {
- I2C_BOARD_INFO("twl5030", 0x48),
- .flags = I2C_CLIENT_WAKE,
- .irq = INT_34XX_SYS_NIRQ,
- .platform_data = &zoom_twldata,
- },
-};
-
static int __init omap_i2c_init(void)
{
if (machine_is_omap_zoom2()) {
@@ -365,19 +355,12 @@
zoom_audio_data.hs_extmute = 1;
zoom_audio_data.set_hs_extmute = zoom2_set_hs_extmute;
}
- omap_register_i2c_bus(1, 2400, zoom_i2c_boardinfo,
- ARRAY_SIZE(zoom_i2c_boardinfo));
+ omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata);
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
}
-static struct omap_musb_board_data musb_board_data = {
- .interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_OTG,
- .power = 100,
-};
-
static void enable_board_wakeup_source(void)
{
/* T2 interrupt line (keypad) */
@@ -392,7 +375,7 @@
omap_i2c_init();
platform_device_register(&omap_vwlan_device);
- usb_musb_init(&musb_board_data);
+ usb_musb_init(NULL);
enable_board_wakeup_source();
omap_serial_init();
}
diff --git a/arch/arm/mach-omap2/common-board-devices.c b/arch/arm/mach-omap2/common-board-devices.c
new file mode 100644
index 0000000..e94903b
--- /dev/null
+++ b/arch/arm/mach-omap2/common-board-devices.c
@@ -0,0 +1,163 @@
+/*
+ * common-board-devices.c
+ *
+ * Copyright (C) 2011 CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c/twl.h>
+
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+
+#include <plat/i2c.h>
+#include <plat/mcspi.h>
+#include <plat/nand.h>
+
+#include "common-board-devices.h"
+
+static struct i2c_board_info __initdata pmic_i2c_board_info = {
+ .addr = 0x48,
+ .flags = I2C_CLIENT_WAKE,
+};
+
+void __init omap_pmic_init(int bus, u32 clkrate,
+ const char *pmic_type, int pmic_irq,
+ struct twl4030_platform_data *pmic_data)
+{
+ strncpy(pmic_i2c_board_info.type, pmic_type,
+ sizeof(pmic_i2c_board_info.type));
+ pmic_i2c_board_info.irq = pmic_irq;
+ pmic_i2c_board_info.platform_data = pmic_data;
+
+ omap_register_i2c_bus(bus, clkrate, &pmic_i2c_board_info, 1);
+}
+
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || \
+ defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+static struct omap2_mcspi_device_config ads7846_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+};
+
+static struct ads7846_platform_data ads7846_config = {
+ .x_max = 0x0fff,
+ .y_max = 0x0fff,
+ .x_plate_ohms = 180,
+ .pressure_max = 255,
+ .debounce_max = 10,
+ .debounce_tol = 3,
+ .debounce_rep = 1,
+ .gpio_pendown = -EINVAL,
+ .keep_vref_on = 1,
+};
+
+static struct spi_board_info ads7846_spi_board_info __initdata = {
+ .modalias = "ads7846",
+ .bus_num = -EINVAL,
+ .chip_select = 0,
+ .max_speed_hz = 1500000,
+ .controller_data = &ads7846_mcspi_config,
+ .irq = -EINVAL,
+ .platform_data = &ads7846_config,
+};
+
+void __init omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
+ struct ads7846_platform_data *board_pdata)
+{
+ struct spi_board_info *spi_bi = &ads7846_spi_board_info;
+ int err;
+
+ err = gpio_request(gpio_pendown, "TS PenDown");
+ if (err) {
+ pr_err("Could not obtain gpio for TS PenDown: %d\n", err);
+ return;
+ }
+
+ gpio_direction_input(gpio_pendown);
+ gpio_export(gpio_pendown, 0);
+
+ if (gpio_debounce)
+ gpio_set_debounce(gpio_pendown, gpio_debounce);
+
+ ads7846_config.gpio_pendown = gpio_pendown;
+
+ spi_bi->bus_num = bus_num;
+ spi_bi->irq = OMAP_GPIO_IRQ(gpio_pendown);
+
+ if (board_pdata)
+ spi_bi->platform_data = board_pdata;
+
+ spi_register_board_info(&ads7846_spi_board_info, 1);
+}
+#else
+void __init omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
+ struct ads7846_platform_data *board_pdata)
+{
+}
+#endif
+
+#if defined(CONFIG_MTD_NAND_OMAP2) || defined(CONFIG_MTD_NAND_OMAP2_MODULE)
+static struct omap_nand_platform_data nand_data = {
+ .dma_channel = -1, /* disable DMA in OMAP NAND driver */
+};
+
+void __init omap_nand_flash_init(int options, struct mtd_partition *parts,
+ int nr_parts)
+{
+ u8 cs = 0;
+ u8 nandcs = GPMC_CS_NUM + 1;
+
+ /* find out the chip-select on which NAND exists */
+ while (cs < GPMC_CS_NUM) {
+ u32 ret = 0;
+ ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+
+ if ((ret & 0xC00) == 0x800) {
+ printk(KERN_INFO "Found NAND on CS%d\n", cs);
+ if (nandcs > GPMC_CS_NUM)
+ nandcs = cs;
+ }
+ cs++;
+ }
+
+ if (nandcs > GPMC_CS_NUM) {
+ printk(KERN_INFO "NAND: Unable to find configuration "
+ "in GPMC\n ");
+ return;
+ }
+
+ if (nandcs < GPMC_CS_NUM) {
+ nand_data.cs = nandcs;
+ nand_data.parts = parts;
+ nand_data.nr_parts = nr_parts;
+ nand_data.options = options;
+
+ printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
+ if (gpmc_nand_init(&nand_data) < 0)
+ printk(KERN_ERR "Unable to register NAND device\n");
+ }
+}
+#else
+void __init omap_nand_flash_init(int options, struct mtd_partition *parts,
+ int nr_parts)
+{
+}
+#endif
diff --git a/arch/arm/mach-omap2/common-board-devices.h b/arch/arm/mach-omap2/common-board-devices.h
new file mode 100644
index 0000000..eb80b3b
--- /dev/null
+++ b/arch/arm/mach-omap2/common-board-devices.h
@@ -0,0 +1,35 @@
+#ifndef __OMAP_COMMON_BOARD_DEVICES__
+#define __OMAP_COMMON_BOARD_DEVICES__
+
+struct twl4030_platform_data;
+struct mtd_partition;
+
+void omap_pmic_init(int bus, u32 clkrate, const char *pmic_type, int pmic_irq,
+ struct twl4030_platform_data *pmic_data);
+
+static inline void omap2_pmic_init(const char *pmic_type,
+ struct twl4030_platform_data *pmic_data)
+{
+ omap_pmic_init(2, 2600, pmic_type, INT_24XX_SYS_NIRQ, pmic_data);
+}
+
+static inline void omap3_pmic_init(const char *pmic_type,
+ struct twl4030_platform_data *pmic_data)
+{
+ omap_pmic_init(1, 2600, pmic_type, INT_34XX_SYS_NIRQ, pmic_data);
+}
+
+static inline void omap4_pmic_init(const char *pmic_type,
+ struct twl4030_platform_data *pmic_data)
+{
+ /* Phoenix Audio IC needs I2C1 to start with 400 KHz or less */
+ omap_pmic_init(1, 400, pmic_type, OMAP44XX_IRQ_SYS_1N, pmic_data);
+}
+
+struct ads7846_platform_data;
+
+void omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
+ struct ads7846_platform_data *board_pdata);
+void omap_nand_flash_init(int opts, struct mtd_partition *parts, int n_parts);
+
+#endif /* __OMAP_COMMON_BOARD_DEVICES__ */
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 1c240ef..4bf6e6e 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -36,36 +36,6 @@
#ifdef CONFIG_CPU_IDLE
-#define OMAP3_MAX_STATES 7
-#define OMAP3_STATE_C1 0 /* C1 - MPU WFI + Core active */
-#define OMAP3_STATE_C2 1 /* C2 - MPU WFI + Core inactive */
-#define OMAP3_STATE_C3 2 /* C3 - MPU CSWR + Core inactive */
-#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core iactive */
-#define OMAP3_STATE_C5 4 /* C5 - MPU RET + Core RET */
-#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core RET */
-#define OMAP3_STATE_C7 6 /* C7 - MPU OFF + Core OFF */
-
-#define OMAP3_STATE_MAX OMAP3_STATE_C7
-
-#define CPUIDLE_FLAG_CHECK_BM 0x10000 /* use omap3_enter_idle_bm() */
-
-struct omap3_processor_cx {
- u8 valid;
- u8 type;
- u32 sleep_latency;
- u32 wakeup_latency;
- u32 mpu_state;
- u32 core_state;
- u32 threshold;
- u32 flags;
- const char *desc;
-};
-
-struct omap3_processor_cx omap3_power_states[OMAP3_MAX_STATES];
-struct omap3_processor_cx current_cx_state;
-struct powerdomain *mpu_pd, *core_pd, *per_pd;
-struct powerdomain *cam_pd;
-
/*
* The latencies/thresholds for various C states have
* to be configured from the respective board files.
@@ -75,27 +45,31 @@
*/
static struct cpuidle_params cpuidle_params_table[] = {
/* C1 */
- {1, 2, 2, 5},
+ {2 + 2, 5, 1},
/* C2 */
- {1, 10, 10, 30},
+ {10 + 10, 30, 1},
/* C3 */
- {1, 50, 50, 300},
+ {50 + 50, 300, 1},
/* C4 */
- {1, 1500, 1800, 4000},
+ {1500 + 1800, 4000, 1},
/* C5 */
- {1, 2500, 7500, 12000},
+ {2500 + 7500, 12000, 1},
/* C6 */
- {1, 3000, 8500, 15000},
+ {3000 + 8500, 15000, 1},
/* C7 */
- {1, 10000, 30000, 300000},
+ {10000 + 30000, 300000, 1},
};
+#define OMAP3_NUM_STATES ARRAY_SIZE(cpuidle_params_table)
-static int omap3_idle_bm_check(void)
-{
- if (!omap3_can_sleep())
- return 1;
- return 0;
-}
+/* Mach specific information to be recorded in the C-state driver_data */
+struct omap3_idle_statedata {
+ u32 mpu_state;
+ u32 core_state;
+ u8 valid;
+};
+struct omap3_idle_statedata omap3_idle_data[OMAP3_NUM_STATES];
+
+struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
static int _cpuidle_allow_idle(struct powerdomain *pwrdm,
struct clockdomain *clkdm)
@@ -122,12 +96,10 @@
static int omap3_enter_idle(struct cpuidle_device *dev,
struct cpuidle_state *state)
{
- struct omap3_processor_cx *cx = cpuidle_get_statedata(state);
+ struct omap3_idle_statedata *cx = cpuidle_get_statedata(state);
struct timespec ts_preidle, ts_postidle, ts_idle;
u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
- current_cx_state = *cx;
-
/* Used to keep track of the total time in idle */
getnstimeofday(&ts_preidle);
@@ -140,7 +112,8 @@
if (omap_irq_pending() || need_resched())
goto return_sleep_time;
- if (cx->type == OMAP3_STATE_C1) {
+ /* Deny idle for C1 */
+ if (state == &dev->states[0]) {
pwrdm_for_each_clkdm(mpu_pd, _cpuidle_deny_idle);
pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle);
}
@@ -148,7 +121,8 @@
/* Execute ARM wfi */
omap_sram_idle();
- if (cx->type == OMAP3_STATE_C1) {
+ /* Re-allow idle for C1 */
+ if (state == &dev->states[0]) {
pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle);
pwrdm_for_each_clkdm(core_pd, _cpuidle_allow_idle);
}
@@ -164,41 +138,53 @@
}
/**
- * next_valid_state - Find next valid c-state
+ * next_valid_state - Find next valid C-state
* @dev: cpuidle device
- * @state: Currently selected c-state
+ * @state: Currently selected C-state
*
* If the current state is valid, it is returned back to the caller.
* Else, this function searches for a lower c-state which is still
- * valid (as defined in omap3_power_states[]).
+ * valid.
+ *
+ * A state is valid if the 'valid' field is enabled and
+ * if it satisfies the enable_off_mode condition.
*/
static struct cpuidle_state *next_valid_state(struct cpuidle_device *dev,
- struct cpuidle_state *curr)
+ struct cpuidle_state *curr)
{
struct cpuidle_state *next = NULL;
- struct omap3_processor_cx *cx;
+ struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr);
+ u32 mpu_deepest_state = PWRDM_POWER_RET;
+ u32 core_deepest_state = PWRDM_POWER_RET;
- cx = (struct omap3_processor_cx *)cpuidle_get_statedata(curr);
+ if (enable_off_mode) {
+ mpu_deepest_state = PWRDM_POWER_OFF;
+ /*
+ * Erratum i583: valable for ES rev < Es1.2 on 3630.
+ * CORE OFF mode is not supported in a stable form, restrict
+ * instead the CORE state to RET.
+ */
+ if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583))
+ core_deepest_state = PWRDM_POWER_OFF;
+ }
/* Check if current state is valid */
- if (cx->valid) {
+ if ((cx->valid) &&
+ (cx->mpu_state >= mpu_deepest_state) &&
+ (cx->core_state >= core_deepest_state)) {
return curr;
} else {
- u8 idx = OMAP3_STATE_MAX;
+ int idx = OMAP3_NUM_STATES - 1;
- /*
- * Reach the current state starting at highest C-state
- */
- for (; idx >= OMAP3_STATE_C1; idx--) {
+ /* Reach the current state starting at highest C-state */
+ for (; idx >= 0; idx--) {
if (&dev->states[idx] == curr) {
next = &dev->states[idx];
break;
}
}
- /*
- * Should never hit this condition.
- */
+ /* Should never hit this condition */
WARN_ON(next == NULL);
/*
@@ -206,17 +192,17 @@
* Start search from the next (lower) state.
*/
idx--;
- for (; idx >= OMAP3_STATE_C1; idx--) {
- struct omap3_processor_cx *cx;
-
+ for (; idx >= 0; idx--) {
cx = cpuidle_get_statedata(&dev->states[idx]);
- if (cx->valid) {
+ if ((cx->valid) &&
+ (cx->mpu_state >= mpu_deepest_state) &&
+ (cx->core_state >= core_deepest_state)) {
next = &dev->states[idx];
break;
}
}
/*
- * C1 and C2 are always valid.
+ * C1 is always valid.
* So, no need to check for 'next==NULL' outside this loop.
*/
}
@@ -229,36 +215,22 @@
* @dev: cpuidle device
* @state: The target state to be programmed
*
- * Used for C states with CPUIDLE_FLAG_CHECK_BM flag set. This
- * function checks for any pending activity and then programs the
- * device to the specified or a safer state.
+ * This function checks for any pending activity and then programs
+ * the device to the specified or a safer state.
*/
static int omap3_enter_idle_bm(struct cpuidle_device *dev,
struct cpuidle_state *state)
{
- struct cpuidle_state *new_state = next_valid_state(dev, state);
- u32 core_next_state, per_next_state = 0, per_saved_state = 0;
- u32 cam_state;
- struct omap3_processor_cx *cx;
+ struct cpuidle_state *new_state;
+ u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
+ struct omap3_idle_statedata *cx;
int ret;
- if ((state->flags & CPUIDLE_FLAG_CHECK_BM) && omap3_idle_bm_check()) {
- BUG_ON(!dev->safe_state);
+ if (!omap3_can_sleep()) {
new_state = dev->safe_state;
goto select_state;
}
- cx = cpuidle_get_statedata(state);
- core_next_state = cx->core_state;
-
- /*
- * FIXME: we currently manage device-specific idle states
- * for PER and CORE in combination with CPU-specific
- * idle states. This is wrong, and device-specific
- * idle management needs to be separated out into
- * its own code.
- */
-
/*
* Prevent idle completely if CAM is active.
* CAM does not have wakeup capability in OMAP3.
@@ -270,9 +242,19 @@
}
/*
+ * FIXME: we currently manage device-specific idle states
+ * for PER and CORE in combination with CPU-specific
+ * idle states. This is wrong, and device-specific
+ * idle management needs to be separated out into
+ * its own code.
+ */
+
+ /*
* Prevent PER off if CORE is not in retention or off as this
* would disable PER wakeups completely.
*/
+ cx = cpuidle_get_statedata(state);
+ core_next_state = cx->core_state;
per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
if ((per_next_state == PWRDM_POWER_OFF) &&
(core_next_state > PWRDM_POWER_RET))
@@ -282,6 +264,8 @@
if (per_next_state != per_saved_state)
pwrdm_set_next_pwrst(per_pd, per_next_state);
+ new_state = next_valid_state(dev, state);
+
select_state:
dev->last_state = new_state;
ret = omap3_enter_idle(dev, new_state);
@@ -295,31 +279,6 @@
DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
-/**
- * omap3_cpuidle_update_states() - Update the cpuidle states
- * @mpu_deepest_state: Enable states up to and including this for mpu domain
- * @core_deepest_state: Enable states up to and including this for core domain
- *
- * This goes through the list of states available and enables and disables the
- * validity of C states based on deepest state that can be achieved for the
- * variable domain
- */
-void omap3_cpuidle_update_states(u32 mpu_deepest_state, u32 core_deepest_state)
-{
- int i;
-
- for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) {
- struct omap3_processor_cx *cx = &omap3_power_states[i];
-
- if ((cx->mpu_state >= mpu_deepest_state) &&
- (cx->core_state >= core_deepest_state)) {
- cx->valid = 1;
- } else {
- cx->valid = 0;
- }
- }
-}
-
void omap3_pm_init_cpuidle(struct cpuidle_params *cpuidle_board_params)
{
int i;
@@ -327,212 +286,109 @@
if (!cpuidle_board_params)
return;
- for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) {
- cpuidle_params_table[i].valid =
- cpuidle_board_params[i].valid;
- cpuidle_params_table[i].sleep_latency =
- cpuidle_board_params[i].sleep_latency;
- cpuidle_params_table[i].wake_latency =
- cpuidle_board_params[i].wake_latency;
- cpuidle_params_table[i].threshold =
- cpuidle_board_params[i].threshold;
+ for (i = 0; i < OMAP3_NUM_STATES; i++) {
+ cpuidle_params_table[i].valid = cpuidle_board_params[i].valid;
+ cpuidle_params_table[i].exit_latency =
+ cpuidle_board_params[i].exit_latency;
+ cpuidle_params_table[i].target_residency =
+ cpuidle_board_params[i].target_residency;
}
return;
}
-/* omap3_init_power_states - Initialises the OMAP3 specific C states.
- *
- * Below is the desciption of each C state.
- * C1 . MPU WFI + Core active
- * C2 . MPU WFI + Core inactive
- * C3 . MPU CSWR + Core inactive
- * C4 . MPU OFF + Core inactive
- * C5 . MPU CSWR + Core CSWR
- * C6 . MPU OFF + Core CSWR
- * C7 . MPU OFF + Core OFF
- */
-void omap_init_power_states(void)
-{
- /* C1 . MPU WFI + Core active */
- omap3_power_states[OMAP3_STATE_C1].valid =
- cpuidle_params_table[OMAP3_STATE_C1].valid;
- omap3_power_states[OMAP3_STATE_C1].type = OMAP3_STATE_C1;
- omap3_power_states[OMAP3_STATE_C1].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C1].sleep_latency;
- omap3_power_states[OMAP3_STATE_C1].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C1].wake_latency;
- omap3_power_states[OMAP3_STATE_C1].threshold =
- cpuidle_params_table[OMAP3_STATE_C1].threshold;
- omap3_power_states[OMAP3_STATE_C1].mpu_state = PWRDM_POWER_ON;
- omap3_power_states[OMAP3_STATE_C1].core_state = PWRDM_POWER_ON;
- omap3_power_states[OMAP3_STATE_C1].flags = CPUIDLE_FLAG_TIME_VALID;
- omap3_power_states[OMAP3_STATE_C1].desc = "MPU ON + CORE ON";
-
- /* C2 . MPU WFI + Core inactive */
- omap3_power_states[OMAP3_STATE_C2].valid =
- cpuidle_params_table[OMAP3_STATE_C2].valid;
- omap3_power_states[OMAP3_STATE_C2].type = OMAP3_STATE_C2;
- omap3_power_states[OMAP3_STATE_C2].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C2].sleep_latency;
- omap3_power_states[OMAP3_STATE_C2].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C2].wake_latency;
- omap3_power_states[OMAP3_STATE_C2].threshold =
- cpuidle_params_table[OMAP3_STATE_C2].threshold;
- omap3_power_states[OMAP3_STATE_C2].mpu_state = PWRDM_POWER_ON;
- omap3_power_states[OMAP3_STATE_C2].core_state = PWRDM_POWER_ON;
- omap3_power_states[OMAP3_STATE_C2].flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_CHECK_BM;
- omap3_power_states[OMAP3_STATE_C2].desc = "MPU ON + CORE ON";
-
- /* C3 . MPU CSWR + Core inactive */
- omap3_power_states[OMAP3_STATE_C3].valid =
- cpuidle_params_table[OMAP3_STATE_C3].valid;
- omap3_power_states[OMAP3_STATE_C3].type = OMAP3_STATE_C3;
- omap3_power_states[OMAP3_STATE_C3].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C3].sleep_latency;
- omap3_power_states[OMAP3_STATE_C3].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C3].wake_latency;
- omap3_power_states[OMAP3_STATE_C3].threshold =
- cpuidle_params_table[OMAP3_STATE_C3].threshold;
- omap3_power_states[OMAP3_STATE_C3].mpu_state = PWRDM_POWER_RET;
- omap3_power_states[OMAP3_STATE_C3].core_state = PWRDM_POWER_ON;
- omap3_power_states[OMAP3_STATE_C3].flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_CHECK_BM;
- omap3_power_states[OMAP3_STATE_C3].desc = "MPU RET + CORE ON";
-
- /* C4 . MPU OFF + Core inactive */
- omap3_power_states[OMAP3_STATE_C4].valid =
- cpuidle_params_table[OMAP3_STATE_C4].valid;
- omap3_power_states[OMAP3_STATE_C4].type = OMAP3_STATE_C4;
- omap3_power_states[OMAP3_STATE_C4].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C4].sleep_latency;
- omap3_power_states[OMAP3_STATE_C4].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C4].wake_latency;
- omap3_power_states[OMAP3_STATE_C4].threshold =
- cpuidle_params_table[OMAP3_STATE_C4].threshold;
- omap3_power_states[OMAP3_STATE_C4].mpu_state = PWRDM_POWER_OFF;
- omap3_power_states[OMAP3_STATE_C4].core_state = PWRDM_POWER_ON;
- omap3_power_states[OMAP3_STATE_C4].flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_CHECK_BM;
- omap3_power_states[OMAP3_STATE_C4].desc = "MPU OFF + CORE ON";
-
- /* C5 . MPU CSWR + Core CSWR*/
- omap3_power_states[OMAP3_STATE_C5].valid =
- cpuidle_params_table[OMAP3_STATE_C5].valid;
- omap3_power_states[OMAP3_STATE_C5].type = OMAP3_STATE_C5;
- omap3_power_states[OMAP3_STATE_C5].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C5].sleep_latency;
- omap3_power_states[OMAP3_STATE_C5].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C5].wake_latency;
- omap3_power_states[OMAP3_STATE_C5].threshold =
- cpuidle_params_table[OMAP3_STATE_C5].threshold;
- omap3_power_states[OMAP3_STATE_C5].mpu_state = PWRDM_POWER_RET;
- omap3_power_states[OMAP3_STATE_C5].core_state = PWRDM_POWER_RET;
- omap3_power_states[OMAP3_STATE_C5].flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_CHECK_BM;
- omap3_power_states[OMAP3_STATE_C5].desc = "MPU RET + CORE RET";
-
- /* C6 . MPU OFF + Core CSWR */
- omap3_power_states[OMAP3_STATE_C6].valid =
- cpuidle_params_table[OMAP3_STATE_C6].valid;
- omap3_power_states[OMAP3_STATE_C6].type = OMAP3_STATE_C6;
- omap3_power_states[OMAP3_STATE_C6].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C6].sleep_latency;
- omap3_power_states[OMAP3_STATE_C6].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C6].wake_latency;
- omap3_power_states[OMAP3_STATE_C6].threshold =
- cpuidle_params_table[OMAP3_STATE_C6].threshold;
- omap3_power_states[OMAP3_STATE_C6].mpu_state = PWRDM_POWER_OFF;
- omap3_power_states[OMAP3_STATE_C6].core_state = PWRDM_POWER_RET;
- omap3_power_states[OMAP3_STATE_C6].flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_CHECK_BM;
- omap3_power_states[OMAP3_STATE_C6].desc = "MPU OFF + CORE RET";
-
- /* C7 . MPU OFF + Core OFF */
- omap3_power_states[OMAP3_STATE_C7].valid =
- cpuidle_params_table[OMAP3_STATE_C7].valid;
- omap3_power_states[OMAP3_STATE_C7].type = OMAP3_STATE_C7;
- omap3_power_states[OMAP3_STATE_C7].sleep_latency =
- cpuidle_params_table[OMAP3_STATE_C7].sleep_latency;
- omap3_power_states[OMAP3_STATE_C7].wakeup_latency =
- cpuidle_params_table[OMAP3_STATE_C7].wake_latency;
- omap3_power_states[OMAP3_STATE_C7].threshold =
- cpuidle_params_table[OMAP3_STATE_C7].threshold;
- omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_OFF;
- omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_OFF;
- omap3_power_states[OMAP3_STATE_C7].flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_CHECK_BM;
- omap3_power_states[OMAP3_STATE_C7].desc = "MPU OFF + CORE OFF";
-
- /*
- * Erratum i583: implementation for ES rev < Es1.2 on 3630. We cannot
- * enable OFF mode in a stable form for previous revisions.
- * we disable C7 state as a result.
- */
- if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583)) {
- omap3_power_states[OMAP3_STATE_C7].valid = 0;
- cpuidle_params_table[OMAP3_STATE_C7].valid = 0;
- pr_warn("%s: core off state C7 disabled due to i583\n",
- __func__);
- }
-}
-
struct cpuidle_driver omap3_idle_driver = {
.name = "omap3_idle",
.owner = THIS_MODULE,
};
+/* Helper to fill the C-state common data and register the driver_data */
+static inline struct omap3_idle_statedata *_fill_cstate(
+ struct cpuidle_device *dev,
+ int idx, const char *descr)
+{
+ struct omap3_idle_statedata *cx = &omap3_idle_data[idx];
+ struct cpuidle_state *state = &dev->states[idx];
+
+ state->exit_latency = cpuidle_params_table[idx].exit_latency;
+ state->target_residency = cpuidle_params_table[idx].target_residency;
+ state->flags = CPUIDLE_FLAG_TIME_VALID;
+ state->enter = omap3_enter_idle_bm;
+ cx->valid = cpuidle_params_table[idx].valid;
+ sprintf(state->name, "C%d", idx + 1);
+ strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
+ cpuidle_set_statedata(state, cx);
+
+ return cx;
+}
+
/**
* omap3_idle_init - Init routine for OMAP3 idle
*
- * Registers the OMAP3 specific cpuidle driver with the cpuidle
+ * Registers the OMAP3 specific cpuidle driver to the cpuidle
* framework with the valid set of states.
*/
int __init omap3_idle_init(void)
{
- int i, count = 0;
- struct omap3_processor_cx *cx;
- struct cpuidle_state *state;
struct cpuidle_device *dev;
+ struct omap3_idle_statedata *cx;
mpu_pd = pwrdm_lookup("mpu_pwrdm");
core_pd = pwrdm_lookup("core_pwrdm");
per_pd = pwrdm_lookup("per_pwrdm");
cam_pd = pwrdm_lookup("cam_pwrdm");
- omap_init_power_states();
cpuidle_register_driver(&omap3_idle_driver);
-
dev = &per_cpu(omap3_idle_dev, smp_processor_id());
- for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) {
- cx = &omap3_power_states[i];
- state = &dev->states[count];
+ /* C1 . MPU WFI + Core active */
+ cx = _fill_cstate(dev, 0, "MPU ON + CORE ON");
+ (&dev->states[0])->enter = omap3_enter_idle;
+ dev->safe_state = &dev->states[0];
+ cx->valid = 1; /* C1 is always valid */
+ cx->mpu_state = PWRDM_POWER_ON;
+ cx->core_state = PWRDM_POWER_ON;
- if (!cx->valid)
- continue;
- cpuidle_set_statedata(state, cx);
- state->exit_latency = cx->sleep_latency + cx->wakeup_latency;
- state->target_residency = cx->threshold;
- state->flags = cx->flags;
- state->enter = (state->flags & CPUIDLE_FLAG_CHECK_BM) ?
- omap3_enter_idle_bm : omap3_enter_idle;
- if (cx->type == OMAP3_STATE_C1)
- dev->safe_state = state;
- sprintf(state->name, "C%d", count+1);
- strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
- count++;
+ /* C2 . MPU WFI + Core inactive */
+ cx = _fill_cstate(dev, 1, "MPU ON + CORE ON");
+ cx->mpu_state = PWRDM_POWER_ON;
+ cx->core_state = PWRDM_POWER_ON;
+
+ /* C3 . MPU CSWR + Core inactive */
+ cx = _fill_cstate(dev, 2, "MPU RET + CORE ON");
+ cx->mpu_state = PWRDM_POWER_RET;
+ cx->core_state = PWRDM_POWER_ON;
+
+ /* C4 . MPU OFF + Core inactive */
+ cx = _fill_cstate(dev, 3, "MPU OFF + CORE ON");
+ cx->mpu_state = PWRDM_POWER_OFF;
+ cx->core_state = PWRDM_POWER_ON;
+
+ /* C5 . MPU RET + Core RET */
+ cx = _fill_cstate(dev, 4, "MPU RET + CORE RET");
+ cx->mpu_state = PWRDM_POWER_RET;
+ cx->core_state = PWRDM_POWER_RET;
+
+ /* C6 . MPU OFF + Core RET */
+ cx = _fill_cstate(dev, 5, "MPU OFF + CORE RET");
+ cx->mpu_state = PWRDM_POWER_OFF;
+ cx->core_state = PWRDM_POWER_RET;
+
+ /* C7 . MPU OFF + Core OFF */
+ cx = _fill_cstate(dev, 6, "MPU OFF + CORE OFF");
+ /*
+ * Erratum i583: implementation for ES rev < Es1.2 on 3630. We cannot
+ * enable OFF mode in a stable form for previous revisions.
+ * We disable C7 state as a result.
+ */
+ if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583)) {
+ cx->valid = 0;
+ pr_warn("%s: core off state C7 disabled due to i583\n",
+ __func__);
}
+ cx->mpu_state = PWRDM_POWER_OFF;
+ cx->core_state = PWRDM_POWER_OFF;
- if (!count)
- return -EINVAL;
- dev->state_count = count;
-
- if (enable_off_mode)
- omap3_cpuidle_update_states(PWRDM_POWER_OFF, PWRDM_POWER_OFF);
- else
- omap3_cpuidle_update_states(PWRDM_POWER_RET, PWRDM_POWER_RET);
-
+ dev->state_count = OMAP3_NUM_STATES;
if (cpuidle_register_device(dev)) {
printk(KERN_ERR "%s: CPUidle register device failed\n",
__func__);
diff --git a/arch/arm/mach-omap2/gpmc-smc91x.c b/arch/arm/mach-omap2/gpmc-smc91x.c
index 877c6f5..ba10c24 100644
--- a/arch/arm/mach-omap2/gpmc-smc91x.c
+++ b/arch/arm/mach-omap2/gpmc-smc91x.c
@@ -147,25 +147,24 @@
goto free1;
}
- if (gpio_request(gpmc_cfg->gpio_irq, "SMC91X irq") < 0)
+ if (gpio_request_one(gpmc_cfg->gpio_irq, GPIOF_IN, "SMC91X irq") < 0)
goto free1;
- gpio_direction_input(gpmc_cfg->gpio_irq);
gpmc_smc91x_resources[1].start = gpio_to_irq(gpmc_cfg->gpio_irq);
if (gpmc_cfg->gpio_pwrdwn) {
- ret = gpio_request(gpmc_cfg->gpio_pwrdwn, "SMC91X powerdown");
+ ret = gpio_request_one(gpmc_cfg->gpio_pwrdwn,
+ GPIOF_OUT_INIT_LOW, "SMC91X powerdown");
if (ret)
goto free2;
- gpio_direction_output(gpmc_cfg->gpio_pwrdwn, 0);
}
if (gpmc_cfg->gpio_reset) {
- ret = gpio_request(gpmc_cfg->gpio_reset, "SMC91X reset");
+ ret = gpio_request_one(gpmc_cfg->gpio_reset,
+ GPIOF_OUT_INIT_LOW, "SMC91X reset");
if (ret)
goto free3;
- gpio_direction_output(gpmc_cfg->gpio_reset, 0);
gpio_set_value(gpmc_cfg->gpio_reset, 1);
msleep(100);
gpio_set_value(gpmc_cfg->gpio_reset, 0);
diff --git a/arch/arm/mach-omap2/gpmc-smsc911x.c b/arch/arm/mach-omap2/gpmc-smsc911x.c
index 703f150..9970331 100644
--- a/arch/arm/mach-omap2/gpmc-smsc911x.c
+++ b/arch/arm/mach-omap2/gpmc-smsc911x.c
@@ -10,6 +10,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/platform_device.h>
@@ -30,7 +31,7 @@
.flags = IORESOURCE_MEM,
},
[1] = {
- .flags = IORESOURCE_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
},
};
@@ -41,16 +42,6 @@
.flags = SMSC911X_USE_16BIT,
};
-static struct platform_device gpmc_smsc911x_device = {
- .name = "smsc911x",
- .id = -1,
- .num_resources = ARRAY_SIZE(gpmc_smsc911x_resources),
- .resource = gpmc_smsc911x_resources,
- .dev = {
- .platform_data = &gpmc_smsc911x_config,
- },
-};
-
/*
* Initialize smsc911x device connected to the GPMC. Note that we
* assume that pin multiplexing is done in the board-*.c file,
@@ -58,46 +49,49 @@
*/
void __init gpmc_smsc911x_init(struct omap_smsc911x_platform_data *board_data)
{
+ struct platform_device *pdev;
unsigned long cs_mem_base;
int ret;
gpmc_cfg = board_data;
if (gpmc_cs_request(gpmc_cfg->cs, SZ_16M, &cs_mem_base) < 0) {
- printk(KERN_ERR "Failed to request GPMC mem for smsc911x\n");
+ pr_err("Failed to request GPMC mem region\n");
return;
}
gpmc_smsc911x_resources[0].start = cs_mem_base + 0x0;
gpmc_smsc911x_resources[0].end = cs_mem_base + 0xff;
- if (gpio_request(gpmc_cfg->gpio_irq, "smsc911x irq") < 0) {
- printk(KERN_ERR "Failed to request GPIO%d for smsc911x IRQ\n",
- gpmc_cfg->gpio_irq);
+ if (gpio_request_one(gpmc_cfg->gpio_irq, GPIOF_IN, "smsc911x irq")) {
+ pr_err("Failed to request IRQ GPIO%d\n", gpmc_cfg->gpio_irq);
goto free1;
}
- gpio_direction_input(gpmc_cfg->gpio_irq);
gpmc_smsc911x_resources[1].start = gpio_to_irq(gpmc_cfg->gpio_irq);
- gpmc_smsc911x_resources[1].flags |=
- (gpmc_cfg->flags & IRQF_TRIGGER_MASK);
if (gpio_is_valid(gpmc_cfg->gpio_reset)) {
- ret = gpio_request(gpmc_cfg->gpio_reset, "smsc911x reset");
+ ret = gpio_request_one(gpmc_cfg->gpio_reset,
+ GPIOF_OUT_INIT_HIGH, "smsc911x reset");
if (ret) {
- printk(KERN_ERR "Failed to request GPIO%d for smsc911x reset\n",
- gpmc_cfg->gpio_reset);
+ pr_err("Failed to request reset GPIO%d\n",
+ gpmc_cfg->gpio_reset);
goto free2;
}
- gpio_direction_output(gpmc_cfg->gpio_reset, 1);
gpio_set_value(gpmc_cfg->gpio_reset, 0);
msleep(100);
gpio_set_value(gpmc_cfg->gpio_reset, 1);
}
- if (platform_device_register(&gpmc_smsc911x_device) < 0) {
- printk(KERN_ERR "Unable to register smsc911x device\n");
+ if (gpmc_cfg->flags)
+ gpmc_smsc911x_config.flags = gpmc_cfg->flags;
+
+ pdev = platform_device_register_resndata(NULL, "smsc911x", gpmc_cfg->id,
+ gpmc_smsc911x_resources, ARRAY_SIZE(gpmc_smsc911x_resources),
+ &gpmc_smsc911x_config, sizeof(gpmc_smsc911x_config));
+ if (!pdev) {
+ pr_err("Unable to register platform device\n");
gpio_free(gpmc_cfg->gpio_reset);
goto free2;
}
@@ -109,5 +103,5 @@
free1:
gpmc_cs_free(gpmc_cfg->cs);
- printk(KERN_ERR "Could not initialize smsc911x\n");
+ pr_err("Could not initialize smsc911x device\n");
}
diff --git a/arch/arm/mach-omap2/omap_l3_noc.c b/arch/arm/mach-omap2/omap_l3_noc.c
index 82632c2..7b9f190 100644
--- a/arch/arm/mach-omap2/omap_l3_noc.c
+++ b/arch/arm/mach-omap2/omap_l3_noc.c
@@ -63,10 +63,7 @@
char *source_name;
/* Get the Type of interrupt */
- if (irq == l3->app_irq)
- inttype = L3_APPLICATION_ERROR;
- else
- inttype = L3_DEBUG_ERROR;
+ inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
for (i = 0; i < L3_MODULES; i++) {
/*
@@ -84,10 +81,10 @@
err_src = j;
/* Read the stderrlog_main_source from clk domain */
- std_err_main_addr = base + (*(l3_targ[i] + err_src));
- std_err_main = readl(std_err_main_addr);
+ std_err_main_addr = base + *(l3_targ[i] + err_src);
+ std_err_main = readl(std_err_main_addr);
- switch ((std_err_main & CUSTOM_ERROR)) {
+ switch (std_err_main & CUSTOM_ERROR) {
case STANDARD_ERROR:
source_name =
l3_targ_stderrlog_main_name[i][err_src];
@@ -132,49 +129,49 @@
l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
if (!l3)
- ret = -ENOMEM;
+ return -ENOMEM;
platform_set_drvdata(pdev, l3);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource 0\n");
ret = -ENODEV;
- goto err1;
+ goto err0;
}
l3->l3_base[0] = ioremap(res->start, resource_size(res));
- if (!(l3->l3_base[0])) {
+ if (!l3->l3_base[0]) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
- goto err2;
+ goto err0;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource 1\n");
ret = -ENODEV;
- goto err3;
+ goto err1;
}
l3->l3_base[1] = ioremap(res->start, resource_size(res));
- if (!(l3->l3_base[1])) {
+ if (!l3->l3_base[1]) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
- goto err4;
+ goto err1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (!res) {
dev_err(&pdev->dev, "couldn't find resource 2\n");
ret = -ENODEV;
- goto err5;
+ goto err2;
}
l3->l3_base[2] = ioremap(res->start, resource_size(res));
- if (!(l3->l3_base[2])) {
+ if (!l3->l3_base[2]) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
- goto err6;
+ goto err2;
}
/*
@@ -187,7 +184,7 @@
if (ret) {
pr_crit("L3: request_irq failed to register for 0x%x\n",
OMAP44XX_IRQ_L3_DBG);
- goto err7;
+ goto err3;
}
l3->debug_irq = irq;
@@ -198,24 +195,22 @@
if (ret) {
pr_crit("L3: request_irq failed to register for 0x%x\n",
OMAP44XX_IRQ_L3_APP);
- goto err8;
+ goto err4;
}
l3->app_irq = irq;
- goto err0;
-err8:
-err7:
- iounmap(l3->l3_base[2]);
-err6:
-err5:
- iounmap(l3->l3_base[1]);
+ return 0;
+
err4:
+ free_irq(l3->debug_irq, l3);
err3:
- iounmap(l3->l3_base[0]);
+ iounmap(l3->l3_base[2]);
err2:
+ iounmap(l3->l3_base[1]);
err1:
- kfree(l3);
+ iounmap(l3->l3_base[0]);
err0:
+ kfree(l3);
return ret;
}
diff --git a/arch/arm/mach-omap2/omap_l3_smx.c b/arch/arm/mach-omap2/omap_l3_smx.c
index 4321e79..873c0e3 100644
--- a/arch/arm/mach-omap2/omap_l3_smx.c
+++ b/arch/arm/mach-omap2/omap_l3_smx.c
@@ -155,7 +155,7 @@
u8 multi = error & L3_ERROR_LOG_MULTI;
u32 address = omap3_l3_decode_addr(error_addr);
- WARN(true, "%s Error seen by %s %s at address %x\n",
+ WARN(true, "%s seen by %s %s at address %x\n",
omap3_l3_code_string(code),
omap3_l3_initiator_string(initid),
multi ? "Multiple Errors" : "",
@@ -167,21 +167,15 @@
static irqreturn_t omap3_l3_app_irq(int irq, void *_l3)
{
struct omap3_l3 *l3 = _l3;
-
u64 status, clear;
u64 error;
u64 error_addr;
u64 err_source = 0;
void __iomem *base;
int int_type;
-
irqreturn_t ret = IRQ_NONE;
- if (irq == l3->app_irq)
- int_type = L3_APPLICATION_ERROR;
- else
- int_type = L3_DEBUG_ERROR;
-
+ int_type = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
if (!int_type) {
status = omap3_l3_readll(l3->rt, L3_SI_FLAG_STATUS_0);
/*
@@ -202,7 +196,6 @@
base = l3->rt + *(omap3_l3_bases[int_type] + err_source);
error = omap3_l3_readll(base, L3_ERROR_LOG);
-
if (error) {
error_addr = omap3_l3_readll(base, L3_ERROR_LOG_ADDR);
@@ -210,9 +203,8 @@
}
/* Clear the status register */
- clear = ((L3_AGENT_STATUS_CLEAR_IA << int_type) |
- (L3_AGENT_STATUS_CLEAR_TA));
-
+ clear = (L3_AGENT_STATUS_CLEAR_IA << int_type) |
+ L3_AGENT_STATUS_CLEAR_TA;
omap3_l3_writell(base, L3_AGENT_STATUS, clear);
/* clear the error log register */
@@ -228,10 +220,8 @@
int ret;
l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
- if (!l3) {
- ret = -ENOMEM;
- goto err0;
- }
+ if (!l3)
+ return -ENOMEM;
platform_set_drvdata(pdev, l3);
@@ -239,13 +229,13 @@
if (!res) {
dev_err(&pdev->dev, "couldn't find resource\n");
ret = -ENODEV;
- goto err1;
+ goto err0;
}
l3->rt = ioremap(res->start, resource_size(res));
- if (!(l3->rt)) {
+ if (!l3->rt) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
- goto err2;
+ goto err0;
}
l3->debug_irq = platform_get_irq(pdev, 0);
@@ -254,28 +244,26 @@
"l3-debug-irq", l3);
if (ret) {
dev_err(&pdev->dev, "couldn't request debug irq\n");
- goto err3;
+ goto err1;
}
l3->app_irq = platform_get_irq(pdev, 1);
ret = request_irq(l3->app_irq, omap3_l3_app_irq,
IRQF_DISABLED | IRQF_TRIGGER_RISING,
"l3-app-irq", l3);
-
if (ret) {
dev_err(&pdev->dev, "couldn't request app irq\n");
- goto err4;
+ goto err2;
}
- goto err0;
+ return 0;
-err4:
-err3:
- iounmap(l3->rt);
err2:
+ free_irq(l3->debug_irq, l3);
err1:
- kfree(l3);
+ iounmap(l3->rt);
err0:
+ kfree(l3);
return ret;
}
diff --git a/arch/arm/mach-omap2/omap_phy_internal.c b/arch/arm/mach-omap2/omap_phy_internal.c
index 05f6abc..f47813e 100644
--- a/arch/arm/mach-omap2/omap_phy_internal.c
+++ b/arch/arm/mach-omap2/omap_phy_internal.c
@@ -50,13 +50,16 @@
{
ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K);
if (!ctrl_base) {
- dev_err(dev, "control module ioremap failed\n");
+ pr_err("control module ioremap failed\n");
return -ENOMEM;
}
/* Power down the phy */
__raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
- phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
+ if (!dev)
+ return 0;
+
+ phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
if (IS_ERR(phyclk)) {
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n");
iounmap(ctrl_base);
@@ -228,7 +231,7 @@
regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
}
-void am35x_musb_set_mode(u8 musb_mode)
+void am35x_set_mode(u8 musb_mode)
{
u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 797bfd1..45bcfce 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -36,11 +36,16 @@
}
#endif
+/*
+ * cpuidle mach specific parameters
+ *
+ * The board code can override the default C-states definition using
+ * omap3_pm_init_cpuidle
+ */
struct cpuidle_params {
- u8 valid;
- u32 sleep_latency;
- u32 wake_latency;
- u32 threshold;
+ u32 exit_latency; /* exit_latency = sleep + wake-up latencies */
+ u32 target_residency;
+ u8 valid; /* validates the C-state */
};
#if defined(CONFIG_PM) && defined(CONFIG_CPU_IDLE)
@@ -73,10 +78,6 @@
#define sleep_while_idle 0
#endif
-#if defined(CONFIG_CPU_IDLE)
-extern void omap3_cpuidle_update_states(u32, u32);
-#endif
-
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
extern void pm_dbg_update_time(struct powerdomain *pwrdm, int prev);
extern int pm_dbg_regset_save(int reg_set);
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 0c5e3a4..c155c9d 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -779,18 +779,6 @@
else
state = PWRDM_POWER_RET;
-#ifdef CONFIG_CPU_IDLE
- /*
- * Erratum i583: implementation for ES rev < Es1.2 on 3630. We cannot
- * enable OFF mode in a stable form for previous revisions, restrict
- * instead to RET
- */
- if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583))
- omap3_cpuidle_update_states(state, PWRDM_POWER_RET);
- else
- omap3_cpuidle_update_states(state, state);
-#endif
-
list_for_each_entry(pwrst, &pwrst_list, node) {
if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583) &&
pwrst->pwrdm == core_pwrdm &&
@@ -895,8 +883,6 @@
pm_errata_configure();
- printk(KERN_ERR "Power Management for TI OMAP3.\n");
-
/* XXX prcm_setup_regs needs to be before enabling hw
* supervised mode for powerdomains */
prcm_setup_regs();
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 76cfff2..59a870b 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -105,13 +105,11 @@
pr_err("Power Management for TI OMAP4.\n");
-#ifdef CONFIG_PM
ret = pwrdm_for_each(pwrdms_setup, NULL);
if (ret) {
pr_err("Failed to setup powerdomains\n");
goto err2;
}
-#endif
#ifdef CONFIG_SUSPEND
suspend_set_ops(&omap_pm_ops);
diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c
index 13e24f9..fb7dc52 100644
--- a/arch/arm/mach-omap2/smartreflex.c
+++ b/arch/arm/mach-omap2/smartreflex.c
@@ -847,6 +847,14 @@
goto err_free_devinfo;
}
+ mem = request_mem_region(mem->start, resource_size(mem),
+ dev_name(&pdev->dev));
+ if (!mem) {
+ dev_err(&pdev->dev, "%s: no mem region\n", __func__);
+ ret = -EBUSY;
+ goto err_free_devinfo;
+ }
+
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pm_runtime_enable(&pdev->dev);
@@ -883,7 +891,7 @@
ret = sr_late_init(sr_info);
if (ret) {
pr_warning("%s: Error in SR late init\n", __func__);
- goto err_release_region;
+ return ret;
}
}
@@ -896,7 +904,7 @@
vdd_dbg_dir = omap_voltage_get_dbgdir(sr_info->voltdm);
if (!vdd_dbg_dir) {
ret = -EINVAL;
- goto err_release_region;
+ goto err_iounmap;
}
sr_info->dbg_dir = debugfs_create_dir("smartreflex", vdd_dbg_dir);
@@ -904,7 +912,7 @@
dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n",
__func__);
ret = PTR_ERR(sr_info->dbg_dir);
- goto err_release_region;
+ goto err_iounmap;
}
(void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR,
@@ -921,7 +929,7 @@
dev_err(&pdev->dev, "%s: Unable to create debugfs directory"
"for n-values\n", __func__);
ret = PTR_ERR(nvalue_dir);
- goto err_release_region;
+ goto err_debugfs;
}
omap_voltage_get_volttable(sr_info->voltdm, &volt_data);
@@ -931,7 +939,7 @@
"entries for n-values\n",
__func__, sr_info->voltdm->name);
ret = -ENODATA;
- goto err_release_region;
+ goto err_debugfs;
}
for (i = 0; i < sr_info->nvalue_count; i++) {
@@ -945,6 +953,11 @@
return ret;
+err_debugfs:
+ debugfs_remove_recursive(sr_info->dbg_dir);
+err_iounmap:
+ list_del(&sr_info->node);
+ iounmap(sr_info->base);
err_release_region:
release_mem_region(mem->start, resource_size(mem));
err_free_devinfo:
diff --git a/arch/arm/mach-omap2/usb-musb.c b/arch/arm/mach-omap2/usb-musb.c
index 35559f7..c7ed540 100644
--- a/arch/arm/mach-omap2/usb-musb.c
+++ b/arch/arm/mach-omap2/usb-musb.c
@@ -108,7 +108,13 @@
}
}
-void __init usb_musb_init(struct omap_musb_board_data *board_data)
+static struct omap_musb_board_data musb_default_board_data = {
+ .interface_type = MUSB_INTERFACE_ULPI,
+ .mode = MUSB_OTG,
+ .power = 100,
+};
+
+void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
{
struct omap_hwmod *oh;
struct omap_device *od;
@@ -116,11 +122,12 @@
struct device *dev;
int bus_id = -1;
const char *oh_name, *name;
+ struct omap_musb_board_data *board_data;
- if (cpu_is_omap3517() || cpu_is_omap3505()) {
- } else if (cpu_is_omap44xx()) {
- usb_musb_mux_init(board_data);
- }
+ if (musb_board_data)
+ board_data = musb_board_data;
+ else
+ board_data = &musb_default_board_data;
/*
* REVISIT: This line can be removed once all the platforms using
@@ -164,10 +171,15 @@
dev->dma_mask = &musb_dmamask;
dev->coherent_dma_mask = musb_dmamask;
put_device(dev);
+
+ if (cpu_is_omap44xx())
+ omap4430_phy_init(dev);
}
#else
void __init usb_musb_init(struct omap_musb_board_data *board_data)
{
+ if (cpu_is_omap44xx())
+ omap4430_phy_init(NULL);
}
#endif /* CONFIG_USB_MUSB_SOC */
diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c
index 8a3c05f..8dd26b7 100644
--- a/arch/arm/mach-omap2/usb-tusb6010.c
+++ b/arch/arm/mach-omap2/usb-tusb6010.c
@@ -293,12 +293,11 @@
);
/* IRQ */
- status = gpio_request(irq, "TUSB6010 irq");
+ status = gpio_request_one(irq, GPIOF_IN, "TUSB6010 irq");
if (status < 0) {
printk(error, 3, status);
return status;
}
- gpio_direction_input(irq);
tusb_resources[2].start = irq + IH_GPIO_BASE;
/* set up memory timings ... can speed them up later */
diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c
index 0c1552d..9ef3789 100644
--- a/arch/arm/mach-omap2/voltage.c
+++ b/arch/arm/mach-omap2/voltage.c
@@ -148,7 +148,6 @@
}
vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);
- pr_notice("curr_vsel = %x\n", vsel);
if (!vdd->pmic_info->vsel_to_uv) {
pr_warning("PMIC function to convert vsel to voltage"
diff --git a/arch/arm/plat-omap/include/plat/gpmc-smsc911x.h b/arch/arm/plat-omap/include/plat/gpmc-smsc911x.h
index 872de0bf..ea6c9c8 100644
--- a/arch/arm/plat-omap/include/plat/gpmc-smsc911x.h
+++ b/arch/arm/plat-omap/include/plat/gpmc-smsc911x.h
@@ -14,14 +14,14 @@
#ifndef __ASM_ARCH_OMAP_GPMC_SMSC911X_H__
struct omap_smsc911x_platform_data {
+ int id;
int cs;
int gpio_irq;
int gpio_reset;
u32 flags;
};
-#if defined(CONFIG_SMSC911X) || \
- defined(CONFIG_SMSC911X_MODULE)
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
extern void gpmc_smsc911x_init(struct omap_smsc911x_platform_data *d);
diff --git a/arch/arm/plat-omap/include/plat/uncompress.h b/arch/arm/plat-omap/include/plat/uncompress.h
index 565d266..ac4b60d 100644
--- a/arch/arm/plat-omap/include/plat/uncompress.h
+++ b/arch/arm/plat-omap/include/plat/uncompress.h
@@ -129,7 +129,6 @@
DEBUG_LL_OMAP1(3, sx1);
/* omap2 based boards using UART1 */
- DEBUG_LL_OMAP2(1, omap2evm);
DEBUG_LL_OMAP2(1, omap_2430sdp);
DEBUG_LL_OMAP2(1, omap_apollon);
DEBUG_LL_OMAP2(1, omap_h4);
diff --git a/arch/arm/plat-omap/include/plat/usb.h b/arch/arm/plat-omap/include/plat/usb.h
index 02b96c8..17d3c93 100644
--- a/arch/arm/plat-omap/include/plat/usb.h
+++ b/arch/arm/plat-omap/include/plat/usb.h
@@ -113,7 +113,7 @@
extern void am35x_musb_reset(void);
extern void am35x_musb_phy_power(u8 on);
extern void am35x_musb_clear_irq(void);
-extern void am35x_musb_set_mode(u8 musb_mode);
+extern void am35x_set_mode(u8 musb_mode);
/*
* FIXME correct answer depends on hmc_mode,
diff --git a/arch/avr32/include/asm/bitops.h b/arch/avr32/include/asm/bitops.h
index 72444d9..b70c19b 100644
--- a/arch/avr32/include/asm/bitops.h
+++ b/arch/avr32/include/asm/bitops.h
@@ -270,14 +270,21 @@
unsigned long find_first_zero_bit(const unsigned long *addr,
unsigned long size);
+#define find_first_zero_bit find_first_zero_bit
+
unsigned long find_next_zero_bit(const unsigned long *addr,
unsigned long size,
unsigned long offset);
+#define find_next_zero_bit find_next_zero_bit
+
unsigned long find_first_bit(const unsigned long *addr,
unsigned long size);
+#define find_first_bit find_first_bit
+
unsigned long find_next_bit(const unsigned long *addr,
unsigned long size,
unsigned long offset);
+#define find_next_bit find_next_bit
/*
* ffs: find first bit set. This is defined the same way as
@@ -299,6 +306,14 @@
#include <asm-generic/bitops/hweight.h>
#include <asm-generic/bitops/lock.h>
+extern unsigned long find_next_zero_bit_le(const void *addr,
+ unsigned long size, unsigned long offset);
+#define find_next_zero_bit_le find_next_zero_bit_le
+
+extern unsigned long find_next_bit_le(const void *addr,
+ unsigned long size, unsigned long offset);
+#define find_next_bit_le find_next_bit_le
+
#include <asm-generic/bitops/le.h>
#include <asm-generic/bitops/ext2-atomic.h>
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index a18180f..d619b17 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -47,9 +47,6 @@
config ZONE_DMA
def_bool y
-config GENERIC_FIND_NEXT_BIT
- def_bool y
-
config GENERIC_GPIO
def_bool y
diff --git a/arch/blackfin/include/asm/kgdb.h b/arch/blackfin/include/asm/kgdb.h
index 3ac0c72..aaf8845 100644
--- a/arch/blackfin/include/asm/kgdb.h
+++ b/arch/blackfin/include/asm/kgdb.h
@@ -108,6 +108,7 @@
#else
# define CACHE_FLUSH_IS_SAFE 1
#endif
+#define GDB_ADJUSTS_BREAK_OFFSET
#define HW_INST_WATCHPOINT_NUM 6
#define HW_WATCHPOINT_NUM 8
#define TYPE_INST_WATCHPOINT 0
diff --git a/arch/blackfin/include/asm/ptrace.h b/arch/blackfin/include/asm/ptrace.h
index 1066d63..7854d43 100644
--- a/arch/blackfin/include/asm/ptrace.h
+++ b/arch/blackfin/include/asm/ptrace.h
@@ -102,9 +102,6 @@
/* user_mode returns true if only one bit is set in IPEND, other than the
master interrupt enable. */
#define user_mode(regs) (!(((regs)->ipend & ~0x10) & (((regs)->ipend & ~0x10) - 1)))
-#define instruction_pointer(regs) ((regs)->pc)
-#define user_stack_pointer(regs) ((regs)->usp)
-#define profile_pc(regs) instruction_pointer(regs)
extern void show_regs(struct pt_regs *);
#define arch_has_single_step() (1)
@@ -128,6 +125,8 @@
((unsigned long)task_stack_page(task) + \
(THREAD_SIZE - sizeof(struct pt_regs)))
+#include <asm-generic/ptrace.h>
+
#endif /* __KERNEL__ */
#endif /* __ASSEMBLY__ */
diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig
index a6d0306..b6b94a2 100644
--- a/arch/cris/Kconfig
+++ b/arch/cris/Kconfig
@@ -31,10 +31,6 @@
bool
default n
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
config GENERIC_HWEIGHT
bool
default y
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
index 064f621..cb884e4 100644
--- a/arch/frv/Kconfig
+++ b/arch/frv/Kconfig
@@ -19,14 +19,6 @@
config RWSEM_XCHGADD_ALGORITHM
bool
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_HWEIGHT
bool
default y
diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig
index e20322f..091ed61 100644
--- a/arch/h8300/Kconfig
+++ b/arch/h8300/Kconfig
@@ -41,14 +41,6 @@
bool
default n
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_HWEIGHT
bool
default y
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index e5cc56a..38280ef 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -78,10 +78,6 @@
depends on HUGETLB_PAGE
default y
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
config GENERIC_CALIBRATE_DELAY
bool
default y
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index 04440cc..85118df 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -36,7 +36,7 @@
static cycle_t itc_get_cycles(struct clocksource *cs);
struct fsyscall_gtod_data_t fsyscall_gtod_data = {
- .lock = SEQLOCK_UNLOCKED,
+ .lock = __SEQLOCK_UNLOCKED(fsyscall_gtod_data.lock),
};
struct itc_jitter_data_t itc_jitter_data;
diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig
index 736b808..85b44e8 100644
--- a/arch/m32r/Kconfig
+++ b/arch/m32r/Kconfig
@@ -256,14 +256,6 @@
bool
default n
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_HWEIGHT
bool
default y
diff --git a/arch/m32r/include/asm/smp.h b/arch/m32r/include/asm/smp.h
index 8accc1b..cf7829a 100644
--- a/arch/m32r/include/asm/smp.h
+++ b/arch/m32r/include/asm/smp.h
@@ -81,11 +81,11 @@
static __inline__ unsigned int num_booting_cpus(void)
{
- return cpus_weight(cpu_callout_map);
+ return cpumask_weight(&cpu_callout_map);
}
extern void smp_send_timer(void);
-extern unsigned long send_IPI_mask_phys(cpumask_t, int, int);
+extern unsigned long send_IPI_mask_phys(const cpumask_t*, int, int);
extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
diff --git a/arch/m32r/kernel/smp.c b/arch/m32r/kernel/smp.c
index fc10b39..092d40a 100644
--- a/arch/m32r/kernel/smp.c
+++ b/arch/m32r/kernel/smp.c
@@ -30,6 +30,7 @@
#include <asm/io.h>
#include <asm/mmu_context.h>
#include <asm/m32r.h>
+#include <asm/tlbflush.h>
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Data structures and variables */
@@ -61,33 +62,22 @@
/* Function Prototypes */
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
-void smp_send_reschedule(int);
void smp_reschedule_interrupt(void);
-
-void smp_flush_cache_all(void);
void smp_flush_cache_all_interrupt(void);
-void smp_flush_tlb_all(void);
static void flush_tlb_all_ipi(void *);
-
-void smp_flush_tlb_mm(struct mm_struct *);
-void smp_flush_tlb_range(struct vm_area_struct *, unsigned long, \
- unsigned long);
-void smp_flush_tlb_page(struct vm_area_struct *, unsigned long);
static void flush_tlb_others(cpumask_t, struct mm_struct *,
struct vm_area_struct *, unsigned long);
+
void smp_invalidate_interrupt(void);
-void smp_send_stop(void);
static void stop_this_cpu(void *);
-void smp_send_timer(void);
void smp_ipi_timer_interrupt(struct pt_regs *);
void smp_local_timer_interrupt(void);
static void send_IPI_allbutself(int, int);
static void send_IPI_mask(const struct cpumask *, int, int);
-unsigned long send_IPI_mask_phys(cpumask_t, int, int);
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
/* Rescheduling request Routines */
@@ -162,10 +152,10 @@
unsigned long *mask;
preempt_disable();
- cpumask = cpu_online_map;
- cpu_clear(smp_processor_id(), cpumask);
+ cpumask_copy(&cpumask, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &cpumask);
spin_lock(&flushcache_lock);
- mask=cpus_addr(cpumask);
+ mask=cpumask_bits(&cpumask);
atomic_set_mask(*mask, (atomic_t *)&flushcache_cpumask);
send_IPI_mask(&cpumask, INVALIDATE_CACHE_IPI, 0);
_flush_cache_copyback_all();
@@ -263,8 +253,8 @@
preempt_disable();
cpu_id = smp_processor_id();
mmc = &mm->context[cpu_id];
- cpu_mask = *mm_cpumask(mm);
- cpu_clear(cpu_id, cpu_mask);
+ cpumask_copy(&cpu_mask, mm_cpumask(mm));
+ cpumask_clear_cpu(cpu_id, &cpu_mask);
if (*mmc != NO_CONTEXT) {
local_irq_save(flags);
@@ -275,7 +265,7 @@
cpumask_clear_cpu(cpu_id, mm_cpumask(mm));
local_irq_restore(flags);
}
- if (!cpus_empty(cpu_mask))
+ if (!cpumask_empty(&cpu_mask))
flush_tlb_others(cpu_mask, mm, NULL, FLUSH_ALL);
preempt_enable();
@@ -333,8 +323,8 @@
preempt_disable();
cpu_id = smp_processor_id();
mmc = &mm->context[cpu_id];
- cpu_mask = *mm_cpumask(mm);
- cpu_clear(cpu_id, cpu_mask);
+ cpumask_copy(&cpu_mask, mm_cpumask(mm));
+ cpumask_clear_cpu(cpu_id, &cpu_mask);
#ifdef DEBUG_SMP
if (!mm)
@@ -348,7 +338,7 @@
__flush_tlb_page(va);
local_irq_restore(flags);
}
- if (!cpus_empty(cpu_mask))
+ if (!cpumask_empty(&cpu_mask))
flush_tlb_others(cpu_mask, mm, vma, va);
preempt_enable();
@@ -395,14 +385,14 @@
* - current CPU must not be in mask
* - mask must exist :)
*/
- BUG_ON(cpus_empty(cpumask));
+ BUG_ON(cpumask_empty(&cpumask));
- BUG_ON(cpu_isset(smp_processor_id(), cpumask));
+ BUG_ON(cpumask_test_cpu(smp_processor_id(), &cpumask));
BUG_ON(!mm);
/* If a CPU which we ran on has gone down, OK. */
- cpus_and(cpumask, cpumask, cpu_online_map);
- if (cpus_empty(cpumask))
+ cpumask_and(&cpumask, &cpumask, cpu_online_mask);
+ if (cpumask_empty(&cpumask))
return;
/*
@@ -416,7 +406,7 @@
flush_mm = mm;
flush_vma = vma;
flush_va = va;
- mask=cpus_addr(cpumask);
+ mask=cpumask_bits(&cpumask);
atomic_set_mask(*mask, (atomic_t *)&flush_cpumask);
/*
@@ -425,7 +415,7 @@
*/
send_IPI_mask(&cpumask, INVALIDATE_TLB_IPI, 0);
- while (!cpus_empty(flush_cpumask)) {
+ while (!cpumask_empty((cpumask_t*)&flush_cpumask)) {
/* nothing. lockup detection does not belong here */
mb();
}
@@ -460,7 +450,7 @@
int cpu_id = smp_processor_id();
unsigned long *mmc = &flush_mm->context[cpu_id];
- if (!cpu_isset(cpu_id, flush_cpumask))
+ if (!cpumask_test_cpu(cpu_id, &flush_cpumask))
return;
if (flush_va == FLUSH_ALL) {
@@ -478,7 +468,7 @@
__flush_tlb_page(va);
}
}
- cpu_clear(cpu_id, flush_cpumask);
+ cpumask_clear_cpu(cpu_id, (cpumask_t*)&flush_cpumask);
}
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
@@ -530,7 +520,7 @@
/*
* Remove this CPU:
*/
- cpu_clear(cpu_id, cpu_online_map);
+ set_cpu_online(cpu_id, false);
/*
* PSW IE = 1;
@@ -725,8 +715,8 @@
{
cpumask_t cpumask;
- cpumask = cpu_online_map;
- cpu_clear(smp_processor_id(), cpumask);
+ cpumask_copy(&cpumask, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &cpumask);
send_IPI_mask(&cpumask, ipi_num, try);
}
@@ -763,13 +753,13 @@
cpumask_and(&tmp, cpumask, cpu_online_mask);
BUG_ON(!cpumask_equal(cpumask, &tmp));
- physid_mask = CPU_MASK_NONE;
+ cpumask_clear(&physid_mask);
for_each_cpu(cpu_id, cpumask) {
if ((phys_id = cpu_to_physid(cpu_id)) != -1)
- cpu_set(phys_id, physid_mask);
+ cpumask_set_cpu(phys_id, &physid_mask);
}
- send_IPI_mask_phys(physid_mask, ipi_num, try);
+ send_IPI_mask_phys(&physid_mask, ipi_num, try);
}
/*==========================================================================*
@@ -792,14 +782,14 @@
* ---------- --- --------------------------------------------------------
*
*==========================================================================*/
-unsigned long send_IPI_mask_phys(cpumask_t physid_mask, int ipi_num,
+unsigned long send_IPI_mask_phys(const cpumask_t *physid_mask, int ipi_num,
int try)
{
spinlock_t *ipilock;
volatile unsigned long *ipicr_addr;
unsigned long ipicr_val;
unsigned long my_physid_mask;
- unsigned long mask = cpus_addr(physid_mask)[0];
+ unsigned long mask = cpumask_bits(physid_mask)[0];
if (mask & ~physids_coerce(phys_cpu_present_map))
diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c
index e034844..cfdbe5d 100644
--- a/arch/m32r/kernel/smpboot.c
+++ b/arch/m32r/kernel/smpboot.c
@@ -135,9 +135,9 @@
{
bsp_phys_id = hard_smp_processor_id();
physid_set(bsp_phys_id, phys_cpu_present_map);
- cpu_set(0, cpu_online_map); /* BSP's cpu_id == 0 */
- cpu_set(0, cpu_callout_map);
- cpu_set(0, cpu_callin_map);
+ set_cpu_online(0, true); /* BSP's cpu_id == 0 */
+ cpumask_set_cpu(0, &cpu_callout_map);
+ cpumask_set_cpu(0, &cpu_callin_map);
/*
* Initialize the logical to physical CPU number mapping
@@ -178,7 +178,7 @@
for (phys_id = 0 ; phys_id < nr_cpu ; phys_id++)
physid_set(phys_id, phys_cpu_present_map);
#ifndef CONFIG_HOTPLUG_CPU
- init_cpu_present(&cpu_possible_map);
+ init_cpu_present(cpu_possible_mask);
#endif
show_mp_info(nr_cpu);
@@ -294,10 +294,10 @@
send_status = 0;
boot_status = 0;
- cpu_set(phys_id, cpu_bootout_map);
+ cpumask_set_cpu(phys_id, &cpu_bootout_map);
/* Send Startup IPI */
- send_IPI_mask_phys(cpumask_of_cpu(phys_id), CPU_BOOT_IPI, 0);
+ send_IPI_mask_phys(cpumask_of(phys_id), CPU_BOOT_IPI, 0);
Dprintk("Waiting for send to finish...\n");
timeout = 0;
@@ -306,7 +306,7 @@
do {
Dprintk("+");
udelay(1000);
- send_status = !cpu_isset(phys_id, cpu_bootin_map);
+ send_status = !cpumask_test_cpu(phys_id, &cpu_bootin_map);
} while (send_status && (timeout++ < 100));
Dprintk("After Startup.\n");
@@ -316,19 +316,19 @@
* allow APs to start initializing.
*/
Dprintk("Before Callout %d.\n", cpu_id);
- cpu_set(cpu_id, cpu_callout_map);
+ cpumask_set_cpu(cpu_id, &cpu_callout_map);
Dprintk("After Callout %d.\n", cpu_id);
/*
* Wait 5s total for a response
*/
for (timeout = 0; timeout < 5000; timeout++) {
- if (cpu_isset(cpu_id, cpu_callin_map))
+ if (cpumask_test_cpu(cpu_id, &cpu_callin_map))
break; /* It has booted */
udelay(1000);
}
- if (cpu_isset(cpu_id, cpu_callin_map)) {
+ if (cpumask_test_cpu(cpu_id, &cpu_callin_map)) {
/* number CPUs logically, starting from 1 (BSP is 0) */
Dprintk("OK.\n");
} else {
@@ -340,9 +340,9 @@
if (send_status || boot_status) {
unmap_cpu_to_physid(cpu_id, phys_id);
- cpu_clear(cpu_id, cpu_callout_map);
- cpu_clear(cpu_id, cpu_callin_map);
- cpu_clear(cpu_id, cpu_initialized);
+ cpumask_clear_cpu(cpu_id, &cpu_callout_map);
+ cpumask_clear_cpu(cpu_id, &cpu_callin_map);
+ cpumask_clear_cpu(cpu_id, &cpu_initialized);
cpucount--;
}
}
@@ -351,17 +351,17 @@
{
int timeout;
- cpu_set(cpu_id, smp_commenced_mask);
+ cpumask_set_cpu(cpu_id, &smp_commenced_mask);
/*
* Wait 5s total for a response
*/
for (timeout = 0; timeout < 5000; timeout++) {
- if (cpu_isset(cpu_id, cpu_online_map))
+ if (cpu_online(cpu_id))
break;
udelay(1000);
}
- if (!cpu_isset(cpu_id, cpu_online_map))
+ if (!cpu_online(cpu_id))
BUG();
return 0;
@@ -373,11 +373,11 @@
unsigned long bogosum = 0;
for (timeout = 0; timeout < 5000; timeout++) {
- if (cpus_equal(cpu_callin_map, cpu_online_map))
+ if (cpumask_equal(&cpu_callin_map, cpu_online_mask))
break;
udelay(1000);
}
- if (!cpus_equal(cpu_callin_map, cpu_online_map))
+ if (!cpumask_equal(&cpu_callin_map, cpu_online_mask))
BUG();
for (cpu_id = 0 ; cpu_id < num_online_cpus() ; cpu_id++)
@@ -388,7 +388,7 @@
*/
Dprintk("Before bogomips.\n");
if (cpucount) {
- for_each_cpu_mask(cpu_id, cpu_online_map)
+ for_each_cpu(cpu_id,cpu_online_mask)
bogosum += cpu_data[cpu_id].loops_per_jiffy;
printk(KERN_INFO "Total of %d processors activated " \
@@ -425,7 +425,7 @@
cpu_init();
preempt_disable();
smp_callin();
- while (!cpu_isset(smp_processor_id(), smp_commenced_mask))
+ while (!cpumask_test_cpu(smp_processor_id(), &smp_commenced_mask))
cpu_relax();
smp_online();
@@ -463,7 +463,7 @@
int cpu_id = smp_processor_id();
unsigned long timeout;
- if (cpu_isset(cpu_id, cpu_callin_map)) {
+ if (cpumask_test_cpu(cpu_id, &cpu_callin_map)) {
printk("huh, phys CPU#%d, CPU#%d already present??\n",
phys_id, cpu_id);
BUG();
@@ -474,7 +474,7 @@
timeout = jiffies + (2 * HZ);
while (time_before(jiffies, timeout)) {
/* Has the boot CPU finished it's STARTUP sequence ? */
- if (cpu_isset(cpu_id, cpu_callout_map))
+ if (cpumask_test_cpu(cpu_id, &cpu_callout_map))
break;
cpu_relax();
}
@@ -486,7 +486,7 @@
}
/* Allow the master to continue. */
- cpu_set(cpu_id, cpu_callin_map);
+ cpumask_set_cpu(cpu_id, &cpu_callin_map);
}
static void __init smp_online(void)
@@ -503,7 +503,7 @@
/* Save our processor parameters */
smp_store_cpu_info(cpu_id);
- cpu_set(cpu_id, cpu_online_map);
+ set_cpu_online(cpu_id, true);
}
/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/
diff --git a/arch/m68k/Kconfig.nommu b/arch/m68k/Kconfig.nommu
index 273bcca..fc98f9b 100644
--- a/arch/m68k/Kconfig.nommu
+++ b/arch/m68k/Kconfig.nommu
@@ -2,10 +2,6 @@
bool
default n
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
config GENERIC_GPIO
bool
default n
diff --git a/arch/m68k/include/asm/bitops_mm.h b/arch/m68k/include/asm/bitops_mm.h
index e9020f8..89cf5b8 100644
--- a/arch/m68k/include/asm/bitops_mm.h
+++ b/arch/m68k/include/asm/bitops_mm.h
@@ -200,6 +200,7 @@
res += ((long)p - (long)vaddr - 4) * 8;
return res < size ? res : size;
}
+#define find_first_zero_bit find_first_zero_bit
static inline int find_next_zero_bit(const unsigned long *vaddr, int size,
int offset)
@@ -229,6 +230,7 @@
/* No zero yet, search remaining full bytes for a zero */
return offset + find_first_zero_bit(p, size - offset);
}
+#define find_next_zero_bit find_next_zero_bit
static inline int find_first_bit(const unsigned long *vaddr, unsigned size)
{
@@ -253,6 +255,7 @@
res += ((long)p - (long)vaddr - 4) * 8;
return res < size ? res : size;
}
+#define find_first_bit find_first_bit
static inline int find_next_bit(const unsigned long *vaddr, int size,
int offset)
@@ -282,6 +285,7 @@
/* No one yet, search remaining full bytes for a one */
return offset + find_first_bit(p, size - offset);
}
+#define find_next_bit find_next_bit
/*
* ffz = Find First Zero in word. Undefined if no zero exists,
@@ -398,6 +402,7 @@
res += (p - addr) * 32;
return res < size ? res : size;
}
+#define find_first_zero_bit_le find_first_zero_bit_le
static inline unsigned long find_next_zero_bit_le(const void *addr,
unsigned long size, unsigned long offset)
@@ -427,6 +432,7 @@
/* No zero yet, search remaining full bytes for a zero */
return offset + find_first_zero_bit_le(p, size - offset);
}
+#define find_next_zero_bit_le find_next_zero_bit_le
static inline int find_first_bit_le(const void *vaddr, unsigned size)
{
@@ -451,6 +457,7 @@
res += (p - addr) * 32;
return res < size ? res : size;
}
+#define find_first_bit_le find_first_bit_le
static inline unsigned long find_next_bit_le(const void *addr,
unsigned long size, unsigned long offset)
@@ -480,6 +487,7 @@
/* No set bit yet, search remaining full bytes for a set bit */
return offset + find_first_bit_le(p, size - offset);
}
+#define find_next_bit_le find_next_bit_le
/* Bitmap functions for the ext2 filesystem. */
diff --git a/arch/m68k/include/asm/bitops_no.h b/arch/m68k/include/asm/bitops_no.h
index 6b0e2d3..72e85ac 100644
--- a/arch/m68k/include/asm/bitops_no.h
+++ b/arch/m68k/include/asm/bitops_no.h
@@ -319,6 +319,10 @@
found_middle:
return result + ffz(__swab32(tmp));
}
+#define find_next_zero_bit_le find_next_zero_bit_le
+
+extern unsigned long find_next_bit_le(const void *addr,
+ unsigned long size, unsigned long offset);
#endif /* __KERNEL__ */
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index eccdefe..e446bab 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -33,12 +33,6 @@
config ARCH_HAS_ILOG2_U64
def_bool n
-config GENERIC_FIND_NEXT_BIT
- def_bool y
-
-config GENERIC_FIND_BIT_LE
- def_bool y
-
config GENERIC_HWEIGHT
def_bool y
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index cef1a85..653da62 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -821,14 +821,6 @@
bool
default y if !MIPS_MT_SMTC
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_HWEIGHT
bool
default y
diff --git a/arch/mips/configs/bcm47xx_defconfig b/arch/mips/configs/bcm47xx_defconfig
index 22fdf2f..ad15fb1 100644
--- a/arch/mips/configs/bcm47xx_defconfig
+++ b/arch/mips/configs/bcm47xx_defconfig
@@ -16,7 +16,6 @@
CONFIG_AUDIT=y
CONFIG_TINY_RCU=y
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index feaf09c..1f87034 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -44,9 +44,6 @@
config GENERIC_CMOS_UPDATE
def_bool n
-config GENERIC_FIND_NEXT_BIT
- def_bool y
-
config GENERIC_HWEIGHT
def_bool y
diff --git a/arch/mn10300/configs/asb2364_defconfig b/arch/mn10300/configs/asb2364_defconfig
index 31d7626..fbb96ae 100644
--- a/arch/mn10300/configs/asb2364_defconfig
+++ b/arch/mn10300/configs/asb2364_defconfig
@@ -8,7 +8,6 @@
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 69ff049..65adc86 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -47,14 +47,6 @@
bool
default n
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_BUG
bool
default y
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 423145a6..2729c66 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -91,14 +91,6 @@
bool
default y
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_GPIO
bool
help
@@ -141,6 +133,7 @@
select GENERIC_IRQ_SHOW
select GENERIC_IRQ_SHOW_LEVEL
select HAVE_RCU_TABLE_FREE if SMP
+ select HAVE_SYSCALL_TRACEPOINTS
config EARLY_PRINTK
bool
diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 2779f08..22dd6ae 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -530,5 +530,23 @@
0x0 0x0 0x0 0x3 &UIC3 0x12 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC3 0x13 0x4 /* swizzled int D */>;
};
+
+ MSI: ppc4xx-msi@C10000000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0xC 0x10000000 0x100>;
+ sdr-base = <0x36C>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts = <0 1 2 3>;
+ interrupt-parent = <&UIC3>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC3 0x18 1
+ 1 &UIC3 0x19 1
+ 2 &UIC3 0x1A 1
+ 3 &UIC3 0x1B 1>;
+ };
};
};
diff --git a/arch/powerpc/boot/dts/katmai.dts b/arch/powerpc/boot/dts/katmai.dts
index 7c3be5e..f913dbe 100644
--- a/arch/powerpc/boot/dts/katmai.dts
+++ b/arch/powerpc/boot/dts/katmai.dts
@@ -442,6 +442,24 @@
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&UIC0>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
+
I2O: i2o@400100000 {
compatible = "ibm,i2o-440spe";
reg = <0x00000004 0x00100000 0x100>;
diff --git a/arch/powerpc/boot/dts/kilauea.dts b/arch/powerpc/boot/dts/kilauea.dts
index 89edb16..1613d6e 100644
--- a/arch/powerpc/boot/dts/kilauea.dts
+++ b/arch/powerpc/boot/dts/kilauea.dts
@@ -403,5 +403,33 @@
0x0 0x0 0x0 0x3 &UIC2 0xd 0x4 /* swizzled int C */
0x0 0x0 0x0 0x4 &UIC2 0xe 0x4 /* swizzled int D */>;
};
+
+ MSI: ppc4xx-msi@C10000000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x0 0xEF620000 0x100>;
+ sdr-base = <0x4B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <12>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 0xA 0xB 0xC 0xD>;
+ interrupt-parent = <&UIC2>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC2 0x10 1
+ 1 &UIC2 0x11 1
+ 2 &UIC2 0x12 1
+ 2 &UIC2 0x13 1
+ 2 &UIC2 0x14 1
+ 2 &UIC2 0x15 1
+ 2 &UIC2 0x16 1
+ 2 &UIC2 0x17 1
+ 2 &UIC2 0x18 1
+ 2 &UIC2 0x19 1
+ 2 &UIC2 0x1A 1
+ 2 &UIC2 0x1B 1
+ 2 &UIC2 0x1C 1
+ 3 &UIC2 0x1D 1>;
+ };
};
};
diff --git a/arch/powerpc/boot/dts/redwood.dts b/arch/powerpc/boot/dts/redwood.dts
index 81636c0..d86a3a4 100644
--- a/arch/powerpc/boot/dts/redwood.dts
+++ b/arch/powerpc/boot/dts/redwood.dts
@@ -358,8 +358,28 @@
0x0 0x0 0x0 0x4 &UIC3 0xb 0x4 /* swizzled int D */>;
};
+ MSI: ppc4xx-msi@400300000 {
+ compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+ reg = < 0x4 0x00300000 0x100
+ 0x4 0x00300000 0x100>;
+ sdr-base = <0x3B0>;
+ msi-data = <0x00000000>;
+ msi-mask = <0x44440000>;
+ interrupt-count = <3>;
+ interrupts =<0 1 2 3>;
+ interrupt-parent = <&UIC0>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC0 0xC 1
+ 1 &UIC0 0x0D 1
+ 2 &UIC0 0x0E 1
+ 3 &UIC0 0x0F 1>;
+ };
+
};
+
chosen {
linux,stdout-path = "/plb/opb/serial@ef600200";
};
diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig
index 2142089..04360f9 100644
--- a/arch/powerpc/configs/ppc6xx_defconfig
+++ b/arch/powerpc/configs/ppc6xx_defconfig
@@ -10,7 +10,6 @@
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_AUDIT=y
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig
index 7de1386..c9f212b 100644
--- a/arch/powerpc/configs/pseries_defconfig
+++ b/arch/powerpc/configs/pseries_defconfig
@@ -15,7 +15,6 @@
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h
index 5c1bf34..8a0b5ec 100644
--- a/arch/powerpc/include/asm/fsl_lbc.h
+++ b/arch/powerpc/include/asm/fsl_lbc.h
@@ -157,6 +157,8 @@
#define LBCR_EPAR_SHIFT 16
#define LBCR_BMT 0x0000FF00
#define LBCR_BMT_SHIFT 8
+#define LBCR_BMTPS 0x0000000F
+#define LBCR_BMTPS_SHIFT 0
#define LBCR_INIT 0x00040000
__be32 lcrr; /**< Clock Ratio Register */
#define LCRR_DBYP 0x80000000
diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h
index dde1296..169d039e 100644
--- a/arch/powerpc/include/asm/ftrace.h
+++ b/arch/powerpc/include/asm/ftrace.h
@@ -60,4 +60,18 @@
#endif
+#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64) && !defined(__ASSEMBLY__)
+#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
+static inline bool arch_syscall_match_sym_name(const char *sym, const char *name)
+{
+ /*
+ * Compare the symbol name with the system call name. Skip the .sys or .SyS
+ * prefix from the symbol name and the sys prefix from the system call name and
+ * just match the rest. This is only needed on ppc64 since symbol names on
+ * 32bit do not start with a period so the generic function will work.
+ */
+ return !strcmp(sym + 4, name + 3);
+}
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 && !__ASSEMBLY__ */
+
#endif /* _ASM_POWERPC_FTRACE */
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 852b8c1..fd8201d 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -236,7 +236,7 @@
#define H_HOME_NODE_ASSOCIATIVITY 0x2EC
#define H_BEST_ENERGY 0x2F4
#define H_GET_MPP_X 0x314
-#define MAX_HCALL_OPCODE H_BEST_ENERGY
+#define MAX_HCALL_OPCODE H_GET_MPP_X
#ifndef __ASSEMBLY__
diff --git a/arch/powerpc/include/asm/rio.h b/arch/powerpc/include/asm/rio.h
index 0018bf8..d902abd 100644
--- a/arch/powerpc/include/asm/rio.h
+++ b/arch/powerpc/include/asm/rio.h
@@ -14,5 +14,10 @@
#define ASM_PPC_RIO_H
extern void platform_rio_init(void);
+#ifdef CONFIG_RAPIDIO
+extern int fsl_rio_mcheck_exception(struct pt_regs *);
+#else
+static inline int fsl_rio_mcheck_exception(struct pt_regs *regs) {return 0; }
+#endif
#endif /* ASM_PPC_RIO_H */
diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
index 880b8c1..11eb404 100644
--- a/arch/powerpc/include/asm/smp.h
+++ b/arch/powerpc/include/asm/smp.h
@@ -191,8 +191,6 @@
extern unsigned long __secondary_hold_acknowledge;
extern char __secondary_hold;
-extern irqreturn_t debug_ipi_action(int irq, void *data);
-
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/include/asm/syscall.h b/arch/powerpc/include/asm/syscall.h
index 23913e9..b54b2ad 100644
--- a/arch/powerpc/include/asm/syscall.h
+++ b/arch/powerpc/include/asm/syscall.h
@@ -15,6 +15,11 @@
#include <linux/sched.h>
+/* ftrace syscalls requires exporting the sys_call_table */
+#ifdef CONFIG_FTRACE_SYSCALLS
+extern const unsigned long *sys_call_table;
+#endif /* CONFIG_FTRACE_SYSCALLS */
+
static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
{
diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h
index 37c353e..836f231 100644
--- a/arch/powerpc/include/asm/thread_info.h
+++ b/arch/powerpc/include/asm/thread_info.h
@@ -110,7 +110,8 @@
#define TIF_NOERROR 12 /* Force successful syscall return */
#define TIF_NOTIFY_RESUME 13 /* callback before returning to user */
#define TIF_FREEZE 14 /* Freezing for suspend */
-#define TIF_RUNLATCH 15 /* Is the runlatch enabled? */
+#define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */
+#define TIF_RUNLATCH 16 /* Is the runlatch enabled? */
/* as above, but as bit values */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
@@ -127,8 +128,10 @@
#define _TIF_NOERROR (1<<TIF_NOERROR)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_FREEZE (1<<TIF_FREEZE)
+#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
#define _TIF_RUNLATCH (1<<TIF_RUNLATCH)
-#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
+#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+ _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT)
#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
_TIF_NOTIFY_RESUME)
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 9aab363..e8b9818 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -109,6 +109,7 @@
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
index ce1f3e44..bf99cfa 100644
--- a/arch/powerpc/kernel/ftrace.c
+++ b/arch/powerpc/kernel/ftrace.c
@@ -22,6 +22,7 @@
#include <asm/cacheflush.h>
#include <asm/code-patching.h>
#include <asm/ftrace.h>
+#include <asm/syscall.h>
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -600,3 +601,10 @@
}
}
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_PPC64)
+unsigned long __init arch_syscall_addr(int nr)
+{
+ return sys_call_table[nr*2];
+}
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index a24d37d..5b428e3 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -295,17 +295,20 @@
unsigned long saved_sp_limit;
struct irq_desc *desc;
+ desc = irq_to_desc(irq);
+ if (!desc)
+ return;
+
/* Switch to the irq stack to handle this */
curtp = current_thread_info();
irqtp = hardirq_ctx[smp_processor_id()];
if (curtp == irqtp) {
/* We're already on the irq stack, just handle it */
- generic_handle_irq(irq);
+ desc->handle_irq(irq, desc);
return;
}
- desc = irq_to_desc(irq);
saved_sp_limit = current->thread.ksp_limit;
irqtp->task = curtp->task;
@@ -557,15 +560,8 @@
if (revmap_type == IRQ_HOST_MAP_LEGACY) {
if (irq_map[0].host != NULL) {
raw_spin_unlock_irqrestore(&irq_big_lock, flags);
- /* If we are early boot, we can't free the structure,
- * too bad...
- * this will be fixed once slab is made available early
- * instead of the current cruft
- */
- if (mem_init_done) {
- of_node_put(host->of_node);
- kfree(host);
- }
+ of_node_put(host->of_node);
+ kfree(host);
return NULL;
}
irq_map[0].host = host;
@@ -727,9 +723,7 @@
}
pr_debug("irq: -> using host @%p\n", host);
- /* Check if mapping already exist, if it does, call
- * host->ops->map() to update the flags
- */
+ /* Check if mapping already exists */
virq = irq_find_mapping(host, hwirq);
if (virq != NO_IRQ) {
pr_debug("irq: -> existing mapping on virq %d\n", virq);
@@ -899,10 +893,13 @@
return irq_find_mapping(host, hwirq);
/*
- * No rcu_read_lock(ing) needed, the ptr returned can't go under us
- * as it's referencing an entry in the static irq_map table.
+ * The ptr returned references the static global irq_map.
+ * but freeing an irq can delete nodes along the path to
+ * do the lookup via call_rcu.
*/
+ rcu_read_lock();
ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq);
+ rcu_read_unlock();
/*
* If found in radix tree, then fine.
@@ -1010,14 +1007,23 @@
WARN_ON (virq < NUM_ISA_INTERRUPTS);
WARN_ON (count == 0 || (virq + count) > irq_virq_count);
+ if (virq < NUM_ISA_INTERRUPTS) {
+ if (virq + count < NUM_ISA_INTERRUPTS)
+ return;
+ count =- NUM_ISA_INTERRUPTS - virq;
+ virq = NUM_ISA_INTERRUPTS;
+ }
+
+ if (count > irq_virq_count || virq > irq_virq_count - count) {
+ if (virq > irq_virq_count)
+ return;
+ count = irq_virq_count - virq;
+ }
+
raw_spin_lock_irqsave(&irq_big_lock, flags);
for (i = virq; i < (virq + count); i++) {
struct irq_host *host;
- if (i < NUM_ISA_INTERRUPTS ||
- (virq + count) > irq_virq_count)
- continue;
-
host = irq_map[i].host;
irq_map[i].hwirq = host->inval_irq;
smp_wmb();
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index a6ae1cf..cb22024 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -29,6 +29,7 @@
#include <linux/signal.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
+#include <trace/syscall.h>
#ifdef CONFIG_PPC32
#include <linux/module.h>
#endif
@@ -40,6 +41,9 @@
#include <asm/pgtable.h>
#include <asm/system.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
/*
* The parameter save area on the stack is used to store arguments being passed
* to callee function and is located at fixed offset from stack pointer.
@@ -1710,6 +1714,9 @@
*/
ret = -1L;
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_enter(regs, regs->gpr[0]);
+
if (unlikely(current->audit_context)) {
#ifdef CONFIG_PPC64
if (!is_32bit_task())
@@ -1738,6 +1745,9 @@
audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result);
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_exit(regs, regs->result);
+
step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, step);
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 4a6f2ec..8ebc670 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -129,7 +129,7 @@
return IRQ_HANDLED;
}
-irqreturn_t debug_ipi_action(int irq, void *data)
+static irqreturn_t debug_ipi_action(int irq, void *data)
{
if (crash_ipi_function_ptr) {
crash_ipi_function_ptr(get_irq_regs());
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index b13306b..0ff4ab9 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -55,6 +55,7 @@
#endif
#include <asm/kexec.h>
#include <asm/ppc-opcode.h>
+#include <asm/rio.h>
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
int (*__debugger)(struct pt_regs *regs) __read_mostly;
@@ -424,6 +425,12 @@
unsigned long reason = mcsr;
int recoverable = 1;
+ if (reason & MCSR_BUS_RBERR) {
+ recoverable = fsl_rio_mcheck_exception(regs);
+ if (recoverable == 1)
+ goto silent_out;
+ }
+
printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason);
@@ -499,6 +506,7 @@
reason & MCSR_MEA ? "Effective" : "Physical", addr);
}
+silent_out:
mtspr(SPRN_MCSR, mcsr);
return mfspr(SPRN_MCSR) == 0 && recoverable;
}
@@ -507,6 +515,11 @@
{
unsigned long reason = get_mc_reason(regs);
+ if (reason & MCSR_BUS_RBERR) {
+ if (fsl_rio_mcheck_exception(regs))
+ return 1;
+ }
+
printk("Machine check in kernel mode.\n");
printk("Caused by (from MCSR=%lx): ", reason);
diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c
index 8ee51a2..e6bec74 100644
--- a/arch/powerpc/oprofile/op_model_power4.c
+++ b/arch/powerpc/oprofile/op_model_power4.c
@@ -261,6 +261,28 @@
return is_kernel;
}
+static bool pmc_overflow(unsigned long val)
+{
+ if ((int)val < 0)
+ return true;
+
+ /*
+ * Events on POWER7 can roll back if a speculative event doesn't
+ * eventually complete. Unfortunately in some rare cases they will
+ * raise a performance monitor exception. We need to catch this to
+ * ensure we reset the PMC. In all cases the PMC will be 256 or less
+ * cycles from overflow.
+ *
+ * We only do this if the first pass fails to find any overflowing
+ * PMCs because a user might set a period of less than 256 and we
+ * don't want to mistakenly reset them.
+ */
+ if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256))
+ return true;
+
+ return false;
+}
+
static void power4_handle_interrupt(struct pt_regs *regs,
struct op_counter_config *ctr)
{
@@ -281,7 +303,7 @@
for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
val = classic_ctr_read(i);
- if (val < 0) {
+ if (pmc_overflow(val)) {
if (oprofile_running && ctr[i].enabled) {
oprofile_add_ext_sample(pc, regs, i, is_kernel);
classic_ctr_write(i, reset_value[i]);
diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig
index b721764..d733d7c 100644
--- a/arch/powerpc/platforms/40x/Kconfig
+++ b/arch/powerpc/platforms/40x/Kconfig
@@ -57,6 +57,8 @@
select 405EX
select PPC40x_SIMPLE
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PPC4xx_MSI
help
This option enables support for the AMCC PPC405EX evaluation board.
diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig
index f485fc5f..e958b6f 100644
--- a/arch/powerpc/platforms/44x/Kconfig
+++ b/arch/powerpc/platforms/44x/Kconfig
@@ -74,6 +74,8 @@
select 440SPe
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PCC4xx_MSI
help
This option enables support for the AMCC PPC440SPe evaluation board.
@@ -118,6 +120,8 @@
select 460EX
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PPC4xx_MSI
select IBM_NEW_EMAC_RGMII
select IBM_NEW_EMAC_ZMII
help
@@ -144,6 +148,8 @@
select 460SX
select PCI
select PPC4xx_PCI_EXPRESS
+ select PCI_MSI
+ select PPC4xx_MSI
help
This option enables support for the AMCC PPC460SX Redwood board.
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 449c08c..3e4eba6 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -176,14 +176,14 @@
#ifdef CONFIG_SMP
/* Use the highest interrupt priorities for IPI */
-static inline int iic_ipi_to_irq(int ipi)
+static inline int iic_msg_to_irq(int msg)
{
- return IIC_IRQ_TYPE_IPI + 0xf - ipi;
+ return IIC_IRQ_TYPE_IPI + 0xf - msg;
}
-void iic_cause_IPI(int cpu, int mesg)
+void iic_message_pass(int cpu, int msg)
{
- out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - mesg) << 4);
+ out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - msg) << 4);
}
struct irq_host *iic_get_irq_host(int node)
@@ -192,50 +192,31 @@
}
EXPORT_SYMBOL_GPL(iic_get_irq_host);
-static irqreturn_t iic_ipi_action(int irq, void *dev_id)
-{
- int ipi = (int)(long)dev_id;
-
- switch(ipi) {
- case PPC_MSG_CALL_FUNCTION:
- generic_smp_call_function_interrupt();
- break;
- case PPC_MSG_RESCHEDULE:
- scheduler_ipi();
- break;
- case PPC_MSG_CALL_FUNC_SINGLE:
- generic_smp_call_function_single_interrupt();
- break;
- case PPC_MSG_DEBUGGER_BREAK:
- debug_ipi_action(0, NULL);
- break;
- }
- return IRQ_HANDLED;
-}
-static void iic_request_ipi(int ipi, const char *name)
+static void iic_request_ipi(int msg)
{
int virq;
- virq = irq_create_mapping(iic_host, iic_ipi_to_irq(ipi));
+ virq = irq_create_mapping(iic_host, iic_msg_to_irq(msg));
if (virq == NO_IRQ) {
printk(KERN_ERR
- "iic: failed to map IPI %s\n", name);
+ "iic: failed to map IPI %s\n", smp_ipi_name[msg]);
return;
}
- if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, name,
- (void *)(long)ipi))
- printk(KERN_ERR
- "iic: failed to request IPI %s\n", name);
+
+ /*
+ * If smp_request_message_ipi encounters an error it will notify
+ * the error. If a message is not needed it will return non-zero.
+ */
+ if (smp_request_message_ipi(virq, msg))
+ irq_dispose_mapping(virq);
}
void iic_request_IPIs(void)
{
- iic_request_ipi(PPC_MSG_CALL_FUNCTION, "IPI-call");
- iic_request_ipi(PPC_MSG_RESCHEDULE, "IPI-resched");
- iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE, "IPI-call-single");
-#ifdef CONFIG_DEBUGGER
- iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
-#endif /* CONFIG_DEBUGGER */
+ iic_request_ipi(PPC_MSG_CALL_FUNCTION);
+ iic_request_ipi(PPC_MSG_RESCHEDULE);
+ iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE);
+ iic_request_ipi(PPC_MSG_DEBUGGER_BREAK);
}
#endif /* CONFIG_SMP */
diff --git a/arch/powerpc/platforms/cell/interrupt.h b/arch/powerpc/platforms/cell/interrupt.h
index 942dc39..4f60ae6 100644
--- a/arch/powerpc/platforms/cell/interrupt.h
+++ b/arch/powerpc/platforms/cell/interrupt.h
@@ -75,7 +75,7 @@
};
extern void iic_init_IRQ(void);
-extern void iic_cause_IPI(int cpu, int mesg);
+extern void iic_message_pass(int cpu, int msg);
extern void iic_request_IPIs(void);
extern void iic_setup_cpu(void);
diff --git a/arch/powerpc/platforms/cell/smp.c b/arch/powerpc/platforms/cell/smp.c
index d176e61..dbb641e 100644
--- a/arch/powerpc/platforms/cell/smp.c
+++ b/arch/powerpc/platforms/cell/smp.c
@@ -152,7 +152,7 @@
return 1;
}
static struct smp_ops_t bpa_iic_smp_ops = {
- .message_pass = iic_cause_IPI,
+ .message_pass = iic_message_pass,
.probe = smp_iic_probe,
.kick_cpu = smp_cell_kick_cpu,
.setup_cpu = smp_cell_setup_cpu,
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index d775fd1..7b4df37 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -7,11 +7,18 @@
depends on PCI && 4xx
default n
+config PPC4xx_MSI
+ bool
+ depends on PCI_MSI
+ depends on PCI && 4xx
+ default n
+
config PPC_MSI_BITMAP
bool
depends on PCI_MSI
default y if MPIC
default y if FSL_PCI
+ default y if PPC4xx_MSI
source "arch/powerpc/sysdev/xics/Kconfig"
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 6076e00..0efa990 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -41,6 +41,7 @@
ifeq ($(CONFIG_PCI),y)
obj-$(CONFIG_4xx) += ppc4xx_pci.o
endif
+obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
index 4fcb5a4..0608b16 100644
--- a/arch/powerpc/sysdev/fsl_lbc.c
+++ b/arch/powerpc/sysdev/fsl_lbc.c
@@ -184,7 +184,8 @@
}
EXPORT_SYMBOL(fsl_upm_run_pattern);
-static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
+static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl,
+ struct device_node *node)
{
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
@@ -198,6 +199,10 @@
/* Enable interrupts for any detected events */
out_be32(&lbc->lteir, LTEIR_ENABLE);
+ /* Set the monitor timeout value to the maximum for erratum A001 */
+ if (of_device_is_compatible(node, "fsl,elbc"))
+ clrsetbits_be32(&lbc->lbcr, LBCR_BMT, LBCR_BMTPS);
+
return 0;
}
@@ -304,7 +309,7 @@
fsl_lbc_ctrl_dev->dev = &dev->dev;
- ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
+ ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node);
if (ret < 0)
goto err;
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c
index 4979853..5b206a2 100644
--- a/arch/powerpc/sysdev/fsl_rio.c
+++ b/arch/powerpc/sysdev/fsl_rio.c
@@ -10,7 +10,7 @@
* - Added Port-Write message handling
* - Added Machine Check exception handling
*
- * Copyright (C) 2007, 2008 Freescale Semiconductor, Inc.
+ * Copyright (C) 2007, 2008, 2010 Freescale Semiconductor, Inc.
* Zhang Wei <wei.zhang@freescale.com>
*
* Copyright 2005 MontaVista Software, Inc.
@@ -47,15 +47,33 @@
#define IRQ_RIO_RX(m) (((struct rio_priv *)(m->priv))->rxirq)
#define IRQ_RIO_PW(m) (((struct rio_priv *)(m->priv))->pwirq)
+#define IPWSR_CLEAR 0x98
+#define OMSR_CLEAR 0x1cb3
+#define IMSR_CLEAR 0x491
+#define IDSR_CLEAR 0x91
+#define ODSR_CLEAR 0x1c00
+#define LTLEECSR_ENABLE_ALL 0xFFC000FC
+#define ESCSR_CLEAR 0x07120204
+
+#define RIO_PORT1_EDCSR 0x0640
+#define RIO_PORT2_EDCSR 0x0680
+#define RIO_PORT1_IECSR 0x10130
+#define RIO_PORT2_IECSR 0x101B0
+#define RIO_IM0SR 0x13064
+#define RIO_IM1SR 0x13164
+#define RIO_OM0SR 0x13004
+#define RIO_OM1SR 0x13104
+
#define RIO_ATMU_REGS_OFFSET 0x10c00
#define RIO_P_MSG_REGS_OFFSET 0x11000
#define RIO_S_MSG_REGS_OFFSET 0x13000
#define RIO_GCCSR 0x13c
#define RIO_ESCSR 0x158
+#define RIO_PORT2_ESCSR 0x178
#define RIO_CCSR 0x15c
#define RIO_LTLEDCSR 0x0608
-#define RIO_LTLEDCSR_IER 0x80000000
-#define RIO_LTLEDCSR_PRT 0x01000000
+#define RIO_LTLEDCSR_IER 0x80000000
+#define RIO_LTLEDCSR_PRT 0x01000000
#define RIO_LTLEECSR 0x060c
#define RIO_EPWISR 0x10010
#define RIO_ISR_AACR 0x10120
@@ -88,7 +106,10 @@
#define RIO_IPWSR_PWD 0x00000008
#define RIO_IPWSR_PWB 0x00000004
-#define RIO_EPWISR_PINT 0x80000000
+/* EPWISR Error match value */
+#define RIO_EPWISR_PINT1 0x80000000
+#define RIO_EPWISR_PINT2 0x40000000
+#define RIO_EPWISR_MU 0x00000002
#define RIO_EPWISR_PW 0x00000001
#define RIO_MSG_DESC_SIZE 32
@@ -260,9 +281,7 @@
static void __iomem *rio_regs_win;
#ifdef CONFIG_E500
-static int (*saved_mcheck_exception)(struct pt_regs *regs);
-
-static int fsl_rio_mcheck_exception(struct pt_regs *regs)
+int fsl_rio_mcheck_exception(struct pt_regs *regs)
{
const struct exception_table_entry *entry = NULL;
unsigned long reason = mfspr(SPRN_MCSR);
@@ -284,11 +303,9 @@
}
}
- if (saved_mcheck_exception)
- return saved_mcheck_exception(regs);
- else
- return cur_cpu_spec->machine_check(regs);
+ return 0;
}
+EXPORT_SYMBOL_GPL(fsl_rio_mcheck_exception);
#endif
/**
@@ -1064,6 +1081,40 @@
return rc;
}
+static void port_error_handler(struct rio_mport *port, int offset)
+{
+ /*XXX: Error recovery is not implemented, we just clear errors */
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0);
+
+ if (offset == 0) {
+ out_be32((u32 *)(rio_regs_win + RIO_PORT1_EDCSR), 0);
+ out_be32((u32 *)(rio_regs_win + RIO_PORT1_IECSR), 0);
+ out_be32((u32 *)(rio_regs_win + RIO_ESCSR), ESCSR_CLEAR);
+ } else {
+ out_be32((u32 *)(rio_regs_win + RIO_PORT2_EDCSR), 0);
+ out_be32((u32 *)(rio_regs_win + RIO_PORT2_IECSR), 0);
+ out_be32((u32 *)(rio_regs_win + RIO_PORT2_ESCSR), ESCSR_CLEAR);
+ }
+}
+
+static void msg_unit_error_handler(struct rio_mport *port)
+{
+ struct rio_priv *priv = port->priv;
+
+ /*XXX: Error recovery is not implemented, we just clear errors */
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0);
+
+ out_be32((u32 *)(rio_regs_win + RIO_IM0SR), IMSR_CLEAR);
+ out_be32((u32 *)(rio_regs_win + RIO_IM1SR), IMSR_CLEAR);
+ out_be32((u32 *)(rio_regs_win + RIO_OM0SR), OMSR_CLEAR);
+ out_be32((u32 *)(rio_regs_win + RIO_OM1SR), OMSR_CLEAR);
+
+ out_be32(&priv->msg_regs->odsr, ODSR_CLEAR);
+ out_be32(&priv->msg_regs->dsr, IDSR_CLEAR);
+
+ out_be32(&priv->msg_regs->pwsr, IPWSR_CLEAR);
+}
+
/**
* fsl_rio_port_write_handler - MPC85xx port write interrupt handler
* @irq: Linux interrupt number
@@ -1144,10 +1195,22 @@
}
pw_done:
- if (epwisr & RIO_EPWISR_PINT) {
+ if (epwisr & RIO_EPWISR_PINT1) {
tmp = in_be32(priv->regs_win + RIO_LTLEDCSR);
pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
- out_be32(priv->regs_win + RIO_LTLEDCSR, 0);
+ port_error_handler(port, 0);
+ }
+
+ if (epwisr & RIO_EPWISR_PINT2) {
+ tmp = in_be32(priv->regs_win + RIO_LTLEDCSR);
+ pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
+ port_error_handler(port, 1);
+ }
+
+ if (epwisr & RIO_EPWISR_MU) {
+ tmp = in_be32(priv->regs_win + RIO_LTLEDCSR);
+ pr_debug("RIO_LTLEDCSR = 0x%x\n", tmp);
+ msg_unit_error_handler(port);
}
return IRQ_HANDLED;
@@ -1258,12 +1321,14 @@
/* Hook up port-write handler */
- rc = request_irq(IRQ_RIO_PW(mport), fsl_rio_port_write_handler, 0,
- "port-write", (void *)mport);
+ rc = request_irq(IRQ_RIO_PW(mport), fsl_rio_port_write_handler,
+ IRQF_SHARED, "port-write", (void *)mport);
if (rc < 0) {
pr_err("MPC85xx RIO: unable to request inbound doorbell irq");
goto err_out;
}
+ /* Enable Error Interrupt */
+ out_be32((u32 *)(rio_regs_win + RIO_LTLEECSR), LTLEECSR_ENABLE_ALL);
INIT_WORK(&priv->pw_work, fsl_pw_dpc);
spin_lock_init(&priv->pw_fifo_lock);
@@ -1538,11 +1603,6 @@
fsl_rio_doorbell_init(port);
fsl_rio_port_write_init(port);
-#ifdef CONFIG_E500
- saved_mcheck_exception = ppc_md.machine_check_exception;
- ppc_md.machine_check_exception = fsl_rio_mcheck_exception;
-#endif
-
return 0;
err:
iounmap(priv->regs_win);
diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c
new file mode 100644
index 0000000..367af02
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_msi.c
@@ -0,0 +1,276 @@
+/*
+ * Adding PCI-E MSI support for PPC4XX SoCs.
+ *
+ * Copyright (c) 2010, Applied Micro Circuits Corporation
+ * Authors: Tirumala R Marri <tmarri@apm.com>
+ * Feng Kan <fkan@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/irq.h>
+#include <linux/bootmem.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <asm/prom.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+#include <boot/dcr.h>
+#include <asm/dcr-regs.h>
+#include <asm/msi_bitmap.h>
+
+#define PEIH_TERMADH 0x00
+#define PEIH_TERMADL 0x08
+#define PEIH_MSIED 0x10
+#define PEIH_MSIMK 0x18
+#define PEIH_MSIASS 0x20
+#define PEIH_FLUSH0 0x30
+#define PEIH_FLUSH1 0x38
+#define PEIH_CNTRST 0x48
+#define NR_MSI_IRQS 4
+
+struct ppc4xx_msi {
+ u32 msi_addr_lo;
+ u32 msi_addr_hi;
+ void __iomem *msi_regs;
+ int msi_virqs[NR_MSI_IRQS];
+ struct msi_bitmap bitmap;
+ struct device_node *msi_dev;
+};
+
+static struct ppc4xx_msi ppc4xx_msi;
+
+static int ppc4xx_msi_init_allocator(struct platform_device *dev,
+ struct ppc4xx_msi *msi_data)
+{
+ int err;
+
+ err = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
+ dev->dev.of_node);
+ if (err)
+ return err;
+
+ err = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap);
+ if (err < 0) {
+ msi_bitmap_free(&msi_data->bitmap);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ppc4xx_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ int int_no = -ENOMEM;
+ unsigned int virq;
+ struct msi_msg msg;
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = &ppc4xx_msi;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ int_no = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1);
+ if (int_no >= 0)
+ break;
+ if (int_no < 0) {
+ pr_debug("%s: fail allocating msi interrupt\n",
+ __func__);
+ }
+ virq = irq_of_parse_and_map(msi_data->msi_dev, int_no);
+ if (virq == NO_IRQ) {
+ dev_err(&dev->dev, "%s: fail mapping irq\n", __func__);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap, int_no, 1);
+ return -ENOSPC;
+ }
+ dev_dbg(&dev->dev, "%s: virq = %d\n", __func__, virq);
+
+ /* Setup msi address space */
+ msg.address_hi = msi_data->msi_addr_hi;
+ msg.address_lo = msi_data->msi_addr_lo;
+
+ irq_set_msi_desc(virq, entry);
+ msg.data = int_no;
+ write_msi_msg(virq, &msg);
+ }
+ return 0;
+}
+
+void ppc4xx_teardown_msi_irqs(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+ struct ppc4xx_msi *msi_data = &ppc4xx_msi;
+
+ dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n");
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ irq_set_msi_desc(entry->irq, NULL);
+ msi_bitmap_free_hwirqs(&msi_data->bitmap,
+ virq_to_hw(entry->irq), 1);
+ irq_dispose_mapping(entry->irq);
+ }
+}
+
+static int ppc4xx_msi_check_device(struct pci_dev *pdev, int nvec, int type)
+{
+ dev_dbg(&pdev->dev, "PCIE-MSI:%s called. vec %x type %d\n",
+ __func__, nvec, type);
+ if (type == PCI_CAP_ID_MSIX)
+ pr_debug("ppc4xx msi: MSI-X untested, trying anyway.\n");
+
+ return 0;
+}
+
+static int ppc4xx_setup_pcieh_hw(struct platform_device *dev,
+ struct resource res, struct ppc4xx_msi *msi)
+{
+ const u32 *msi_data;
+ const u32 *msi_mask;
+ const u32 *sdr_addr;
+ dma_addr_t msi_phys;
+ void *msi_virt;
+
+ sdr_addr = of_get_property(dev->dev.of_node, "sdr-base", NULL);
+ if (!sdr_addr)
+ return -1;
+
+ SDR0_WRITE(sdr_addr, (u64)res.start >> 32); /*HIGH addr */
+ SDR0_WRITE(sdr_addr + 1, res.start & 0xFFFFFFFF); /* Low addr */
+
+
+ msi->msi_dev = of_find_node_by_name(NULL, "ppc4xx-msi");
+ if (msi->msi_dev)
+ return -ENODEV;
+
+ msi->msi_regs = of_iomap(msi->msi_dev, 0);
+ if (!msi->msi_regs) {
+ dev_err(&dev->dev, "of_iomap problem failed\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&dev->dev, "PCIE-MSI: msi register mapped 0x%x 0x%x\n",
+ (u32) (msi->msi_regs + PEIH_TERMADH), (u32) (msi->msi_regs));
+
+ msi_virt = dma_alloc_coherent(&dev->dev, 64, &msi_phys, GFP_KERNEL);
+ msi->msi_addr_hi = 0x0;
+ msi->msi_addr_lo = (u32) msi_phys;
+ dev_dbg(&dev->dev, "PCIE-MSI: msi address 0x%x\n", msi->msi_addr_lo);
+
+ /* Progam the Interrupt handler Termination addr registers */
+ out_be32(msi->msi_regs + PEIH_TERMADH, msi->msi_addr_hi);
+ out_be32(msi->msi_regs + PEIH_TERMADL, msi->msi_addr_lo);
+
+ msi_data = of_get_property(dev->dev.of_node, "msi-data", NULL);
+ if (!msi_data)
+ return -1;
+ msi_mask = of_get_property(dev->dev.of_node, "msi-mask", NULL);
+ if (!msi_mask)
+ return -1;
+ /* Program MSI Expected data and Mask bits */
+ out_be32(msi->msi_regs + PEIH_MSIED, *msi_data);
+ out_be32(msi->msi_regs + PEIH_MSIMK, *msi_mask);
+
+ return 0;
+}
+
+static int ppc4xx_of_msi_remove(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi = dev->dev.platform_data;
+ int i;
+ int virq;
+
+ for (i = 0; i < NR_MSI_IRQS; i++) {
+ virq = msi->msi_virqs[i];
+ if (virq != NO_IRQ)
+ irq_dispose_mapping(virq);
+ }
+
+ if (msi->bitmap.bitmap)
+ msi_bitmap_free(&msi->bitmap);
+ iounmap(msi->msi_regs);
+ of_node_put(msi->msi_dev);
+ kfree(msi);
+
+ return 0;
+}
+
+static int __devinit ppc4xx_msi_probe(struct platform_device *dev)
+{
+ struct ppc4xx_msi *msi;
+ struct resource res;
+ int err = 0;
+
+ msi = &ppc4xx_msi;/*keep the msi data for further use*/
+
+ dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
+
+ msi = kzalloc(sizeof(struct ppc4xx_msi), GFP_KERNEL);
+ if (!msi) {
+ dev_err(&dev->dev, "No memory for MSI structure\n");
+ return -ENOMEM;
+ }
+ dev->dev.platform_data = msi;
+
+ /* Get MSI ranges */
+ err = of_address_to_resource(dev->dev.of_node, 0, &res);
+ if (err) {
+ dev_err(&dev->dev, "%s resource error!\n",
+ dev->dev.of_node->full_name);
+ goto error_out;
+ }
+
+ if (ppc4xx_setup_pcieh_hw(dev, res, msi))
+ goto error_out;
+
+ err = ppc4xx_msi_init_allocator(dev, msi);
+ if (err) {
+ dev_err(&dev->dev, "Error allocating MSI bitmap\n");
+ goto error_out;
+ }
+
+ ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+ ppc_md.msi_check_device = ppc4xx_msi_check_device;
+ return err;
+
+error_out:
+ ppc4xx_of_msi_remove(dev);
+ return err;
+}
+static const struct of_device_id ppc4xx_msi_ids[] = {
+ {
+ .compatible = "amcc,ppc4xx-msi",
+ },
+ {}
+};
+static struct platform_driver ppc4xx_msi_driver = {
+ .probe = ppc4xx_msi_probe,
+ .remove = ppc4xx_of_msi_remove,
+ .driver = {
+ .name = "ppc4xx-msi",
+ .owner = THIS_MODULE,
+ .of_match_table = ppc4xx_msi_ids,
+ },
+
+};
+
+static __init int ppc4xx_msi_init(void)
+{
+ return platform_driver_register(&ppc4xx_msi_driver);
+}
+
+subsys_initcall(ppc4xx_msi_init);
diff --git a/arch/s390/include/asm/bitops.h b/arch/s390/include/asm/bitops.h
index e1c8f3a..667c6e9 100644
--- a/arch/s390/include/asm/bitops.h
+++ b/arch/s390/include/asm/bitops.h
@@ -621,6 +621,7 @@
bits = __ffz_word(bytes*8, __load_ulong_be(addr, bytes));
return (bits < size) ? bits : size;
}
+#define find_first_zero_bit find_first_zero_bit
/**
* find_first_bit - find the first set bit in a memory region
@@ -641,6 +642,7 @@
bits = __ffs_word(bytes*8, __load_ulong_be(addr, bytes));
return (bits < size) ? bits : size;
}
+#define find_first_bit find_first_bit
/**
* find_next_zero_bit - find the first zero bit in a memory region
@@ -677,6 +679,7 @@
}
return offset + find_first_zero_bit(p, size);
}
+#define find_next_zero_bit find_next_zero_bit
/**
* find_next_bit - find the first set bit in a memory region
@@ -713,6 +716,7 @@
}
return offset + find_first_bit(p, size);
}
+#define find_next_bit find_next_bit
/*
* Every architecture must define this function. It's the fastest
@@ -742,41 +746,6 @@
* 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 24
*/
-static inline void __set_bit_le(unsigned long nr, void *addr)
-{
- __set_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
-static inline void __clear_bit_le(unsigned long nr, void *addr)
-{
- __clear_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
-static inline int __test_and_set_bit_le(unsigned long nr, void *addr)
-{
- return __test_and_set_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
-static inline int test_and_set_bit_le(unsigned long nr, void *addr)
-{
- return test_and_set_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
-static inline int __test_and_clear_bit_le(unsigned long nr, void *addr)
-{
- return __test_and_clear_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
-static inline int test_and_clear_bit_le(unsigned long nr, void *addr)
-{
- return test_and_clear_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
-static inline int test_bit_le(unsigned long nr, const void *addr)
-{
- return test_bit(nr ^ (__BITOPS_WORDSIZE - 8), addr);
-}
-
static inline int find_first_zero_bit_le(void *vaddr, unsigned int size)
{
unsigned long bytes, bits;
@@ -787,6 +756,7 @@
bits = __ffz_word(bytes*8, __load_ulong_le(vaddr, bytes));
return (bits < size) ? bits : size;
}
+#define find_first_zero_bit_le find_first_zero_bit_le
static inline int find_next_zero_bit_le(void *vaddr, unsigned long size,
unsigned long offset)
@@ -816,6 +786,7 @@
}
return offset + find_first_zero_bit_le(p, size);
}
+#define find_next_zero_bit_le find_next_zero_bit_le
static inline unsigned long find_first_bit_le(void *vaddr, unsigned long size)
{
@@ -827,6 +798,7 @@
bits = __ffs_word(bytes*8, __load_ulong_le(vaddr, bytes));
return (bits < size) ? bits : size;
}
+#define find_first_bit_le find_first_bit_le
static inline int find_next_bit_le(void *vaddr, unsigned long size,
unsigned long offset)
@@ -856,6 +828,9 @@
}
return offset + find_first_bit_le(p, size);
}
+#define find_next_bit_le find_next_bit_le
+
+#include <asm-generic/bitops/le.h>
#define ext2_set_bit_atomic(lock, nr, addr) \
test_and_set_bit_le(nr, addr)
diff --git a/arch/score/Kconfig b/arch/score/Kconfig
index e73bc78..288add8 100644
--- a/arch/score/Kconfig
+++ b/arch/score/Kconfig
@@ -43,9 +43,6 @@
config RWSEM_GENERIC_SPINLOCK
def_bool y
-config GENERIC_FIND_NEXT_BIT
- def_bool y
-
config GENERIC_HWEIGHT
def_bool y
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index b44e377..74495a5 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -71,12 +71,6 @@
def_bool y
depends on SUPERH64
-config GENERIC_FIND_NEXT_BIT
- def_bool y
-
-config GENERIC_FIND_BIT_LE
- def_bool y
-
config GENERIC_HWEIGHT
def_bool y
diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig
index 77ec0e7..e758348 100644
--- a/arch/sh/configs/apsh4ad0a_defconfig
+++ b/arch/sh/configs/apsh4ad0a_defconfig
@@ -7,7 +7,6 @@
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig
index c416505..8a7dd7b 100644
--- a/arch/sh/configs/sdk7786_defconfig
+++ b/arch/sh/configs/sdk7786_defconfig
@@ -12,7 +12,6 @@
CONFIG_IKCONFIG_PROC=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_DEBUG=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
diff --git a/arch/sh/configs/se7206_defconfig b/arch/sh/configs/se7206_defconfig
index a468ff2..72c3fad 100644
--- a/arch/sh/configs/se7206_defconfig
+++ b/arch/sh/configs/se7206_defconfig
@@ -8,7 +8,6 @@
CONFIG_LOG_BUF_SHIFT=14
CONFIG_CGROUPS=y
CONFIG_CGROUP_DEBUG=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig
index 3f92d37..6bb4130 100644
--- a/arch/sh/configs/shx3_defconfig
+++ b/arch/sh/configs/shx3_defconfig
@@ -9,7 +9,6 @@
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
diff --git a/arch/sh/configs/urquell_defconfig b/arch/sh/configs/urquell_defconfig
index 7b3daec..8bfa4d0 100644
--- a/arch/sh/configs/urquell_defconfig
+++ b/arch/sh/configs/urquell_defconfig
@@ -9,7 +9,6 @@
CONFIG_LOG_BUF_SHIFT=14
CONFIG_CGROUPS=y
CONFIG_CGROUP_DEBUG=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
diff --git a/arch/sh/include/asm/kgdb.h b/arch/sh/include/asm/kgdb.h
index 4235e22..f361395 100644
--- a/arch/sh/include/asm/kgdb.h
+++ b/arch/sh/include/asm/kgdb.h
@@ -34,5 +34,6 @@
#define CACHE_FLUSH_IS_SAFE 1
#define BREAK_INSTR_SIZE 2
+#define GDB_ADJUSTS_BREAK_OFFSET
#endif /* __ASM_SH_KGDB_H */
diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h
index de167d3..40725b4 100644
--- a/arch/sh/include/asm/ptrace.h
+++ b/arch/sh/include/asm/ptrace.h
@@ -40,9 +40,8 @@
#include <asm/system.h>
#define user_mode(regs) (((regs)->sr & 0x40000000)==0)
-#define user_stack_pointer(_regs) ((unsigned long)(_regs)->regs[15])
#define kernel_stack_pointer(_regs) ((unsigned long)(_regs)->regs[15])
-#define instruction_pointer(regs) ((unsigned long)(regs)->pc)
+#define GET_USP(regs) ((regs)->regs[15])
extern void show_regs(struct pt_regs *);
@@ -139,6 +138,9 @@
return pc;
}
+#define profile_pc profile_pc
+
+#include <asm-generic/ptrace.h>
#endif /* __KERNEL__ */
#endif /* __ASM_SH_PTRACE_H */
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 63a027c..af32e17 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -190,14 +190,6 @@
bool
default y if SPARC64
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_FIND_BIT_LE
- bool
- default y
-
config GENERIC_HWEIGHT
bool
default y if !ULTRA_HAS_POPULATION_COUNT
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index 635e1bf..e1e5010 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -5,7 +5,6 @@
def_bool y
select HAVE_KVM if !TILEGX
select GENERIC_FIND_FIRST_BIT
- select GENERIC_FIND_NEXT_BIT
select USE_GENERIC_SMP_HELPERS
select CC_OPTIMIZE_FOR_SIZE
select HAVE_GENERIC_HARDIRQS
diff --git a/arch/um/Kconfig.x86 b/arch/um/Kconfig.x86
index 795ea8e..8aae429 100644
--- a/arch/um/Kconfig.x86
+++ b/arch/um/Kconfig.x86
@@ -15,7 +15,6 @@
config UML_X86
def_bool y
select GENERIC_FIND_FIRST_BIT
- select GENERIC_FIND_NEXT_BIT
config 64BIT
bool
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 483775f..da34972 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -64,7 +64,6 @@
select HAVE_GENERIC_HARDIRQS
select HAVE_SPARSE_IRQ
select GENERIC_FIND_FIRST_BIT
- select GENERIC_FIND_NEXT_BIT
select GENERIC_IRQ_PROBE
select GENERIC_PENDING_IRQ if SMP
select GENERIC_IRQ_SHOW
diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig
index 6f98726..2bf18059f 100644
--- a/arch/x86/configs/i386_defconfig
+++ b/arch/x86/configs/i386_defconfig
@@ -10,7 +10,6 @@
CONFIG_AUDIT=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig
index ee01a9d..22a0dc8 100644
--- a/arch/x86/configs/x86_64_defconfig
+++ b/arch/x86/configs/x86_64_defconfig
@@ -11,7 +11,6 @@
CONFIG_AUDIT=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
-CONFIG_CGROUP_NS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
diff --git a/arch/x86/include/asm/kgdb.h b/arch/x86/include/asm/kgdb.h
index 396f5b5..77e95f5 100644
--- a/arch/x86/include/asm/kgdb.h
+++ b/arch/x86/include/asm/kgdb.h
@@ -77,6 +77,7 @@
}
#define BREAK_INSTR_SIZE 1
#define CACHE_FLUSH_IS_SAFE 1
+#define GDB_ADJUSTS_BREAK_OFFSET
extern int kgdb_ll_trap(int cmd, const char *str,
struct pt_regs *regs, long err, int trap, int sig);
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 1babf8a..94e7618 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -136,6 +136,7 @@
struct task_struct;
extern unsigned long profile_pc(struct pt_regs *regs);
+#define profile_pc profile_pc
extern unsigned long
convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs);
@@ -202,20 +203,11 @@
#endif
}
-static inline unsigned long instruction_pointer(struct pt_regs *regs)
-{
- return regs->ip;
-}
+#define GET_IP(regs) ((regs)->ip)
+#define GET_FP(regs) ((regs)->bp)
+#define GET_USP(regs) ((regs)->sp)
-static inline unsigned long frame_pointer(struct pt_regs *regs)
-{
- return regs->bp;
-}
-
-static inline unsigned long user_stack_pointer(struct pt_regs *regs)
-{
- return regs->sp;
-}
+#include <asm-generic/ptrace.h>
/* Query offset/name of register from its name/offset */
extern int regs_query_register_offset(const char *name);
diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h
index 83e2efd..9db5583 100644
--- a/arch/x86/include/asm/tsc.h
+++ b/arch/x86/include/asm/tsc.h
@@ -51,6 +51,10 @@
extern int check_tsc_unstable(void);
extern unsigned long native_calibrate_tsc(void);
+#ifdef CONFIG_X86_64
+extern cycles_t vread_tsc(void);
+#endif
+
/*
* Boot-time check whether the TSCs are synchronized across
* all CPUs/cores:
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 9064052..bb05228 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -1,20 +1,6 @@
#ifndef _ASM_X86_VDSO_H
#define _ASM_X86_VDSO_H
-#ifdef CONFIG_X86_64
-extern const char VDSO64_PRELINK[];
-
-/*
- * Given a pointer to the vDSO image, find the pointer to VDSO64_name
- * as that symbol is defined in the vDSO sources or linker script.
- */
-#define VDSO64_SYMBOL(base, name) \
-({ \
- extern const char VDSO64_##name[]; \
- (void *)(VDSO64_##name - VDSO64_PRELINK + (unsigned long)(base)); \
-})
-#endif
-
#if defined CONFIG_X86_32 || defined CONFIG_COMPAT
extern const char VDSO32_PRELINK[];
diff --git a/arch/x86/include/asm/vgtod.h b/arch/x86/include/asm/vgtod.h
index 3d61e20..646b4c1 100644
--- a/arch/x86/include/asm/vgtod.h
+++ b/arch/x86/include/asm/vgtod.h
@@ -23,8 +23,6 @@
struct timespec wall_to_monotonic;
struct timespec wall_time_coarse;
};
-extern struct vsyscall_gtod_data __vsyscall_gtod_data
-__section_vsyscall_gtod_data;
extern struct vsyscall_gtod_data vsyscall_gtod_data;
#endif /* _ASM_X86_VGTOD_H */
diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
index d0983d2..d555973 100644
--- a/arch/x86/include/asm/vsyscall.h
+++ b/arch/x86/include/asm/vsyscall.h
@@ -16,27 +16,19 @@
#ifdef __KERNEL__
#include <linux/seqlock.h>
-#define __section_vgetcpu_mode __attribute__ ((unused, __section__ (".vgetcpu_mode"), aligned(16)))
-#define __section_jiffies __attribute__ ((unused, __section__ (".jiffies"), aligned(16)))
-
/* Definitions for CONFIG_GENERIC_TIME definitions */
-#define __section_vsyscall_gtod_data __attribute__ \
- ((unused, __section__ (".vsyscall_gtod_data"),aligned(16)))
-#define __section_vsyscall_clock __attribute__ \
- ((unused, __section__ (".vsyscall_clock"),aligned(16)))
#define __vsyscall_fn \
__attribute__ ((unused, __section__(".vsyscall_fn"))) notrace
#define VGETCPU_RDTSCP 1
#define VGETCPU_LSL 2
-extern int __vgetcpu_mode;
-extern volatile unsigned long __jiffies;
-
/* kernel space (writeable) */
extern int vgetcpu_mode;
extern struct timezone sys_tz;
+#include <asm/vvar.h>
+
extern void map_vsyscall(void);
#endif /* __KERNEL__ */
diff --git a/arch/x86/include/asm/vvar.h b/arch/x86/include/asm/vvar.h
new file mode 100644
index 0000000..341b355
--- /dev/null
+++ b/arch/x86/include/asm/vvar.h
@@ -0,0 +1,52 @@
+/*
+ * vvar.h: Shared vDSO/kernel variable declarations
+ * Copyright (c) 2011 Andy Lutomirski
+ * Subject to the GNU General Public License, version 2
+ *
+ * A handful of variables are accessible (read-only) from userspace
+ * code in the vsyscall page and the vdso. They are declared here.
+ * Some other file must define them with DEFINE_VVAR.
+ *
+ * In normal kernel code, they are used like any other variable.
+ * In user code, they are accessed through the VVAR macro.
+ *
+ * Each of these variables lives in the vsyscall page, and each
+ * one needs a unique offset within the little piece of the page
+ * reserved for vvars. Specify that offset in DECLARE_VVAR.
+ * (There are 896 bytes available. If you mess up, the linker will
+ * catch it.)
+ */
+
+/* Offset of vars within vsyscall page */
+#define VSYSCALL_VARS_OFFSET (3072 + 128)
+
+#if defined(__VVAR_KERNEL_LDS)
+
+/* The kernel linker script defines its own magic to put vvars in the
+ * right place.
+ */
+#define DECLARE_VVAR(offset, type, name) \
+ EMIT_VVAR(name, VSYSCALL_VARS_OFFSET + offset)
+
+#else
+
+#define DECLARE_VVAR(offset, type, name) \
+ static type const * const vvaraddr_ ## name = \
+ (void *)(VSYSCALL_START + VSYSCALL_VARS_OFFSET + (offset));
+
+#define DEFINE_VVAR(type, name) \
+ type __vvar_ ## name \
+ __attribute__((section(".vsyscall_var_" #name), aligned(16)))
+
+#define VVAR(name) (*vvaraddr_ ## name)
+
+#endif
+
+/* DECLARE_VVAR(offset, type, name) */
+
+DECLARE_VVAR(0, volatile unsigned long, jiffies)
+DECLARE_VVAR(8, int, vgetcpu_mode)
+DECLARE_VVAR(128, struct vsyscall_gtod_data, vsyscall_gtod_data)
+
+#undef DECLARE_VVAR
+#undef VSYSCALL_VARS_OFFSET
diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h
index 8508bfe..d240ea9 100644
--- a/arch/x86/include/asm/xen/hypercall.h
+++ b/arch/x86/include/asm/xen/hypercall.h
@@ -447,6 +447,13 @@
return _hypercall2(unsigned long, hvm_op, op, arg);
}
+static inline int
+HYPERVISOR_tmem_op(
+ struct tmem_op *op)
+{
+ return _hypercall1(int, tmem_op, op);
+}
+
static inline void
MULTI_fpu_taskswitch(struct multicall_entry *mcl, int set)
{
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 2508064..f5abe3a 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -8,7 +8,6 @@
ifdef CONFIG_FUNCTION_TRACER
# Do not profile debug and lowlevel utilities
-CFLAGS_REMOVE_tsc.o = -pg
CFLAGS_REMOVE_rtc.o = -pg
CFLAGS_REMOVE_paravirt-spinlocks.o = -pg
CFLAGS_REMOVE_pvclock.o = -pg
@@ -24,13 +23,16 @@
nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_vsyscall_64.o := $(PROFILING) -g0 $(nostackp)
CFLAGS_hpet.o := $(nostackp)
-CFLAGS_tsc.o := $(nostackp)
+CFLAGS_vread_tsc_64.o := $(nostackp)
CFLAGS_paravirt.o := $(nostackp)
GCOV_PROFILE_vsyscall_64.o := n
GCOV_PROFILE_hpet.o := n
GCOV_PROFILE_tsc.o := n
GCOV_PROFILE_paravirt.o := n
+# vread_tsc_64 is hot and should be fully optimized:
+CFLAGS_REMOVE_vread_tsc_64.o = -pg -fno-optimize-sibling-calls
+
obj-y := process_$(BITS).o signal.o entry_$(BITS).o
obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
obj-y += time.o ioport.o ldt.o dumpstack.o
@@ -39,7 +41,7 @@
obj-y += probe_roms.o
obj-$(CONFIG_X86_32) += sys_i386_32.o i386_ksyms_32.o
obj-$(CONFIG_X86_64) += sys_x86_64.o x8664_ksyms_64.o
-obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o
+obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o vread_tsc_64.o
obj-y += bootflag.o e820.o
obj-y += pci-dma.o quirks.o topology.o kdebugfs.o
obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c
index 25a28a2..00cbb27 100644
--- a/arch/x86/kernel/time.c
+++ b/arch/x86/kernel/time.c
@@ -23,7 +23,7 @@
#include <asm/time.h>
#ifdef CONFIG_X86_64
-volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
+DEFINE_VVAR(volatile unsigned long, jiffies) = INITIAL_JIFFIES;
#endif
unsigned long profile_pc(struct pt_regs *regs)
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 9335bf7..6cc6922 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -763,25 +763,6 @@
ret : clocksource_tsc.cycle_last;
}
-#ifdef CONFIG_X86_64
-static cycle_t __vsyscall_fn vread_tsc(void)
-{
- cycle_t ret;
-
- /*
- * Surround the RDTSC by barriers, to make sure it's not
- * speculated to outside the seqlock critical section and
- * does not cause time warps:
- */
- rdtsc_barrier();
- ret = (cycle_t)vget_cycles();
- rdtsc_barrier();
-
- return ret >= __vsyscall_gtod_data.clock.cycle_last ?
- ret : __vsyscall_gtod_data.clock.cycle_last;
-}
-#endif
-
static void resume_tsc(struct clocksource *cs)
{
clocksource_tsc.cycle_last = 0;
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 61682f0..89aed99 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -161,6 +161,12 @@
#define VVIRT_OFFSET (VSYSCALL_ADDR - __vsyscall_0)
#define VVIRT(x) (ADDR(x) - VVIRT_OFFSET)
+#define EMIT_VVAR(x, offset) .vsyscall_var_ ## x \
+ ADDR(.vsyscall_0) + offset \
+ : AT(VLOAD(.vsyscall_var_ ## x)) { \
+ *(.vsyscall_var_ ## x) \
+ } \
+ x = VVIRT(.vsyscall_var_ ## x);
. = ALIGN(4096);
__vsyscall_0 = .;
@@ -175,18 +181,6 @@
*(.vsyscall_fn)
}
- . = ALIGN(L1_CACHE_BYTES);
- .vsyscall_gtod_data : AT(VLOAD(.vsyscall_gtod_data)) {
- *(.vsyscall_gtod_data)
- }
-
- vsyscall_gtod_data = VVIRT(.vsyscall_gtod_data);
- .vsyscall_clock : AT(VLOAD(.vsyscall_clock)) {
- *(.vsyscall_clock)
- }
- vsyscall_clock = VVIRT(.vsyscall_clock);
-
-
.vsyscall_1 ADDR(.vsyscall_0) + 1024: AT(VLOAD(.vsyscall_1)) {
*(.vsyscall_1)
}
@@ -194,21 +188,14 @@
*(.vsyscall_2)
}
- .vgetcpu_mode : AT(VLOAD(.vgetcpu_mode)) {
- *(.vgetcpu_mode)
- }
- vgetcpu_mode = VVIRT(.vgetcpu_mode);
-
- . = ALIGN(L1_CACHE_BYTES);
- .jiffies : AT(VLOAD(.jiffies)) {
- *(.jiffies)
- }
- jiffies = VVIRT(.jiffies);
-
.vsyscall_3 ADDR(.vsyscall_0) + 3072: AT(VLOAD(.vsyscall_3)) {
*(.vsyscall_3)
}
+#define __VVAR_KERNEL_LDS
+#include <asm/vvar.h>
+#undef __VVAR_KERNEL_LDS
+
. = __vsyscall_0 + PAGE_SIZE;
#undef VSYSCALL_ADDR
@@ -216,6 +203,7 @@
#undef VLOAD
#undef VVIRT_OFFSET
#undef VVIRT
+#undef EMIT_VVAR
#endif /* CONFIG_X86_64 */
diff --git a/arch/x86/kernel/vread_tsc_64.c b/arch/x86/kernel/vread_tsc_64.c
new file mode 100644
index 0000000..a81aa9e
--- /dev/null
+++ b/arch/x86/kernel/vread_tsc_64.c
@@ -0,0 +1,36 @@
+/* This code runs in userspace. */
+
+#define DISABLE_BRANCH_PROFILING
+#include <asm/vgtod.h>
+
+notrace cycle_t __vsyscall_fn vread_tsc(void)
+{
+ cycle_t ret;
+ u64 last;
+
+ /*
+ * Empirically, a fence (of type that depends on the CPU)
+ * before rdtsc is enough to ensure that rdtsc is ordered
+ * with respect to loads. The various CPU manuals are unclear
+ * as to whether rdtsc can be reordered with later loads,
+ * but no one has ever seen it happen.
+ */
+ rdtsc_barrier();
+ ret = (cycle_t)vget_cycles();
+
+ last = VVAR(vsyscall_gtod_data).clock.cycle_last;
+
+ if (likely(ret >= last))
+ return ret;
+
+ /*
+ * GCC likes to generate cmov here, but this branch is extremely
+ * predictable (it's just a funciton of time and the likely is
+ * very likely) and there's a data dependence, so force GCC
+ * to generate a branch instead. I don't barrier() because
+ * we don't actually need a barrier, and if this function
+ * ever gets inlined it will generate worse code.
+ */
+ asm volatile ("");
+ return last;
+}
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index dcbb28c..3e68218 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -49,17 +49,10 @@
__attribute__ ((unused, __section__(".vsyscall_" #nr))) notrace
#define __syscall_clobber "r11","cx","memory"
-/*
- * vsyscall_gtod_data contains data that is :
- * - readonly from vsyscalls
- * - written by timer interrupt or systcl (/proc/sys/kernel/vsyscall64)
- * Try to keep this structure as small as possible to avoid cache line ping pongs
- */
-int __vgetcpu_mode __section_vgetcpu_mode;
-
-struct vsyscall_gtod_data __vsyscall_gtod_data __section_vsyscall_gtod_data =
+DEFINE_VVAR(int, vgetcpu_mode);
+DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data) =
{
- .lock = SEQLOCK_UNLOCKED,
+ .lock = __SEQLOCK_UNLOCKED(__vsyscall_gtod_data.lock),
.sysctl_enabled = 1,
};
@@ -97,7 +90,7 @@
*/
static __always_inline void do_get_tz(struct timezone * tz)
{
- *tz = __vsyscall_gtod_data.sys_tz;
+ *tz = VVAR(vsyscall_gtod_data).sys_tz;
}
static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz)
@@ -126,23 +119,24 @@
unsigned long mult, shift, nsec;
cycle_t (*vread)(void);
do {
- seq = read_seqbegin(&__vsyscall_gtod_data.lock);
+ seq = read_seqbegin(&VVAR(vsyscall_gtod_data).lock);
- vread = __vsyscall_gtod_data.clock.vread;
- if (unlikely(!__vsyscall_gtod_data.sysctl_enabled || !vread)) {
+ vread = VVAR(vsyscall_gtod_data).clock.vread;
+ if (unlikely(!VVAR(vsyscall_gtod_data).sysctl_enabled ||
+ !vread)) {
gettimeofday(tv,NULL);
return;
}
now = vread();
- base = __vsyscall_gtod_data.clock.cycle_last;
- mask = __vsyscall_gtod_data.clock.mask;
- mult = __vsyscall_gtod_data.clock.mult;
- shift = __vsyscall_gtod_data.clock.shift;
+ base = VVAR(vsyscall_gtod_data).clock.cycle_last;
+ mask = VVAR(vsyscall_gtod_data).clock.mask;
+ mult = VVAR(vsyscall_gtod_data).clock.mult;
+ shift = VVAR(vsyscall_gtod_data).clock.shift;
- tv->tv_sec = __vsyscall_gtod_data.wall_time_sec;
- nsec = __vsyscall_gtod_data.wall_time_nsec;
- } while (read_seqretry(&__vsyscall_gtod_data.lock, seq));
+ tv->tv_sec = VVAR(vsyscall_gtod_data).wall_time_sec;
+ nsec = VVAR(vsyscall_gtod_data).wall_time_nsec;
+ } while (read_seqretry(&VVAR(vsyscall_gtod_data).lock, seq));
/* calculate interval: */
cycle_delta = (now - base) & mask;
@@ -171,15 +165,15 @@
{
unsigned seq;
time_t result;
- if (unlikely(!__vsyscall_gtod_data.sysctl_enabled))
+ if (unlikely(!VVAR(vsyscall_gtod_data).sysctl_enabled))
return time_syscall(t);
do {
- seq = read_seqbegin(&__vsyscall_gtod_data.lock);
+ seq = read_seqbegin(&VVAR(vsyscall_gtod_data).lock);
- result = __vsyscall_gtod_data.wall_time_sec;
+ result = VVAR(vsyscall_gtod_data).wall_time_sec;
- } while (read_seqretry(&__vsyscall_gtod_data.lock, seq));
+ } while (read_seqretry(&VVAR(vsyscall_gtod_data).lock, seq));
if (t)
*t = result;
@@ -208,9 +202,9 @@
We do this here because otherwise user space would do it on
its own in a likely inferior way (no access to jiffies).
If you don't like it pass NULL. */
- if (tcache && tcache->blob[0] == (j = __jiffies)) {
+ if (tcache && tcache->blob[0] == (j = VVAR(jiffies))) {
p = tcache->blob[1];
- } else if (__vgetcpu_mode == VGETCPU_RDTSCP) {
+ } else if (VVAR(vgetcpu_mode) == VGETCPU_RDTSCP) {
/* Load per CPU data from RDTSCP */
native_read_tscp(&p);
} else {
diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile
index b6552b1..bef0bc9 100644
--- a/arch/x86/vdso/Makefile
+++ b/arch/x86/vdso/Makefile
@@ -11,7 +11,7 @@
# files to link into the vdso
-vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vvar.o
+vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
# files to link into kernel
obj-$(VDSO64-y) += vma.o vdso.o
@@ -37,11 +37,24 @@
$(obj)/%.so: $(obj)/%.so.dbg FORCE
$(call if_changed,objcopy)
+#
+# Don't omit frame pointers for ease of userspace debugging, but do
+# optimize sibling calls.
+#
CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \
- $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector)
+ $(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector) \
+ -fno-omit-frame-pointer -foptimize-sibling-calls
$(vobjs): KBUILD_CFLAGS += $(CFL)
+#
+# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
+#
+CFLAGS_REMOVE_vdso-note.o = -pg
+CFLAGS_REMOVE_vclock_gettime.o = -pg
+CFLAGS_REMOVE_vgetcpu.o = -pg
+CFLAGS_REMOVE_vvar.o = -pg
+
targets += vdso-syms.lds
obj-$(VDSO64-y) += vdso-syms.lds
diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c
index ee55754..a724905 100644
--- a/arch/x86/vdso/vclock_gettime.c
+++ b/arch/x86/vdso/vclock_gettime.c
@@ -2,7 +2,7 @@
* Copyright 2006 Andi Kleen, SUSE Labs.
* Subject to the GNU Public License, v.2
*
- * Fast user context implementation of clock_gettime and gettimeofday.
+ * Fast user context implementation of clock_gettime, gettimeofday, and time.
*
* The code should have no internal unresolved relocations.
* Check with readelf after changing.
@@ -22,9 +22,8 @@
#include <asm/hpet.h>
#include <asm/unistd.h>
#include <asm/io.h>
-#include "vextern.h"
-#define gtod vdso_vsyscall_gtod_data
+#define gtod (&VVAR(vsyscall_gtod_data))
notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
{
@@ -56,22 +55,6 @@
return 0;
}
-/* Copy of the version in kernel/time.c which we cannot directly access */
-notrace static void
-vset_normalized_timespec(struct timespec *ts, long sec, long nsec)
-{
- while (nsec >= NSEC_PER_SEC) {
- nsec -= NSEC_PER_SEC;
- ++sec;
- }
- while (nsec < 0) {
- nsec += NSEC_PER_SEC;
- --sec;
- }
- ts->tv_sec = sec;
- ts->tv_nsec = nsec;
-}
-
notrace static noinline int do_monotonic(struct timespec *ts)
{
unsigned long seq, ns, secs;
@@ -82,7 +65,17 @@
secs += gtod->wall_to_monotonic.tv_sec;
ns += gtod->wall_to_monotonic.tv_nsec;
} while (unlikely(read_seqretry(>od->lock, seq)));
- vset_normalized_timespec(ts, secs, ns);
+
+ /* wall_time_nsec, vgetns(), and wall_to_monotonic.tv_nsec
+ * are all guaranteed to be nonnegative.
+ */
+ while (ns >= NSEC_PER_SEC) {
+ ns -= NSEC_PER_SEC;
+ ++secs;
+ }
+ ts->tv_sec = secs;
+ ts->tv_nsec = ns;
+
return 0;
}
@@ -107,7 +100,17 @@
secs += gtod->wall_to_monotonic.tv_sec;
ns += gtod->wall_to_monotonic.tv_nsec;
} while (unlikely(read_seqretry(>od->lock, seq)));
- vset_normalized_timespec(ts, secs, ns);
+
+ /* wall_time_nsec and wall_to_monotonic.tv_nsec are
+ * guaranteed to be between 0 and NSEC_PER_SEC.
+ */
+ if (ns >= NSEC_PER_SEC) {
+ ns -= NSEC_PER_SEC;
+ ++secs;
+ }
+ ts->tv_sec = secs;
+ ts->tv_nsec = ns;
+
return 0;
}
@@ -157,3 +160,32 @@
}
int gettimeofday(struct timeval *, struct timezone *)
__attribute__((weak, alias("__vdso_gettimeofday")));
+
+/* This will break when the xtime seconds get inaccurate, but that is
+ * unlikely */
+
+static __always_inline long time_syscall(long *t)
+{
+ long secs;
+ asm volatile("syscall"
+ : "=a" (secs)
+ : "0" (__NR_time), "D" (t) : "cc", "r11", "cx", "memory");
+ return secs;
+}
+
+notrace time_t __vdso_time(time_t *t)
+{
+ time_t result;
+
+ if (unlikely(!VVAR(vsyscall_gtod_data).sysctl_enabled))
+ return time_syscall(t);
+
+ /* This is atomic on x86_64 so we don't need any locks. */
+ result = ACCESS_ONCE(VVAR(vsyscall_gtod_data).wall_time_sec);
+
+ if (t)
+ *t = result;
+ return result;
+}
+int time(time_t *t)
+ __attribute__((weak, alias("__vdso_time")));
diff --git a/arch/x86/vdso/vdso.lds.S b/arch/x86/vdso/vdso.lds.S
index 4e5dd3b..b96b267 100644
--- a/arch/x86/vdso/vdso.lds.S
+++ b/arch/x86/vdso/vdso.lds.S
@@ -23,15 +23,10 @@
__vdso_gettimeofday;
getcpu;
__vdso_getcpu;
+ time;
+ __vdso_time;
local: *;
};
}
VDSO64_PRELINK = VDSO_PRELINK;
-
-/*
- * Define VDSO64_x for each VEXTERN(x), for use via VDSO64_SYMBOL.
- */
-#define VEXTERN(x) VDSO64_ ## x = vdso_ ## x;
-#include "vextern.h"
-#undef VEXTERN
diff --git a/arch/x86/vdso/vextern.h b/arch/x86/vdso/vextern.h
deleted file mode 100644
index 1683ba2..0000000
--- a/arch/x86/vdso/vextern.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef VEXTERN
-#include <asm/vsyscall.h>
-#define VEXTERN(x) \
- extern typeof(x) *vdso_ ## x __attribute__((visibility("hidden")));
-#endif
-
-#define VMAGIC 0xfeedbabeabcdefabUL
-
-/* Any kernel variables used in the vDSO must be exported in the main
- kernel's vmlinux.lds.S/vsyscall.h/proper __section and
- put into vextern.h and be referenced as a pointer with vdso prefix.
- The main kernel later fills in the values. */
-
-VEXTERN(jiffies)
-VEXTERN(vgetcpu_mode)
-VEXTERN(vsyscall_gtod_data)
diff --git a/arch/x86/vdso/vgetcpu.c b/arch/x86/vdso/vgetcpu.c
index 9fbc6b2..5463ad5 100644
--- a/arch/x86/vdso/vgetcpu.c
+++ b/arch/x86/vdso/vgetcpu.c
@@ -11,14 +11,13 @@
#include <linux/time.h>
#include <asm/vsyscall.h>
#include <asm/vgtod.h>
-#include "vextern.h"
notrace long
__vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
{
unsigned int p;
- if (*vdso_vgetcpu_mode == VGETCPU_RDTSCP) {
+ if (VVAR(vgetcpu_mode) == VGETCPU_RDTSCP) {
/* Load per CPU data from RDTSCP */
native_read_tscp(&p);
} else {
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
index 4b5d26f..7abd2be 100644
--- a/arch/x86/vdso/vma.c
+++ b/arch/x86/vdso/vma.c
@@ -15,9 +15,6 @@
#include <asm/proto.h>
#include <asm/vdso.h>
-#include "vextern.h" /* Just for VMAGIC. */
-#undef VEXTERN
-
unsigned int __read_mostly vdso_enabled = 1;
extern char vdso_start[], vdso_end[];
@@ -26,20 +23,10 @@
static struct page **vdso_pages;
static unsigned vdso_size;
-static inline void *var_ref(void *p, char *name)
-{
- if (*(void **)p != (void *)VMAGIC) {
- printk("VDSO: variable %s broken\n", name);
- vdso_enabled = 0;
- }
- return p;
-}
-
static int __init init_vdso_vars(void)
{
int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE;
int i;
- char *vbase;
vdso_size = npages << PAGE_SHIFT;
vdso_pages = kmalloc(sizeof(struct page *) * npages, GFP_KERNEL);
@@ -54,20 +41,6 @@
copy_page(page_address(p), vdso_start + i*PAGE_SIZE);
}
- vbase = vmap(vdso_pages, npages, 0, PAGE_KERNEL);
- if (!vbase)
- goto oom;
-
- if (memcmp(vbase, "\177ELF", 4)) {
- printk("VDSO: I'm broken; not ELF\n");
- vdso_enabled = 0;
- }
-
-#define VEXTERN(x) \
- *(typeof(__ ## x) **) var_ref(VDSO64_SYMBOL(vbase, x), #x) = &__ ## x;
-#include "vextern.h"
-#undef VEXTERN
- vunmap(vbase);
return 0;
oom:
diff --git a/arch/x86/vdso/vvar.c b/arch/x86/vdso/vvar.c
deleted file mode 100644
index 1b7e703..0000000
--- a/arch/x86/vdso/vvar.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Define pointer to external vDSO variables.
- These are part of the vDSO. The kernel fills in the real addresses
- at boot time. This is done because when the vdso is linked the
- kernel isn't yet and we don't know the final addresses. */
-#include <linux/kernel.h>
-#include <linux/time.h>
-#include <asm/vsyscall.h>
-#include <asm/timex.h>
-#include <asm/vgtod.h>
-
-#define VEXTERN(x) typeof (__ ## x) *const vdso_ ## x = (void *)VMAGIC;
-#include "vextern.h"
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index 02d7524..dc708dc 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -75,67 +75,12 @@
#include "mmu.h"
#include "debugfs.h"
-#define MMU_UPDATE_HISTO 30
-
/*
* Protects atomic reservation decrease/increase against concurrent increases.
* Also protects non-atomic updates of current_pages and balloon lists.
*/
DEFINE_SPINLOCK(xen_reservation_lock);
-#ifdef CONFIG_XEN_DEBUG_FS
-
-static struct {
- u32 pgd_update;
- u32 pgd_update_pinned;
- u32 pgd_update_batched;
-
- u32 pud_update;
- u32 pud_update_pinned;
- u32 pud_update_batched;
-
- u32 pmd_update;
- u32 pmd_update_pinned;
- u32 pmd_update_batched;
-
- u32 pte_update;
- u32 pte_update_pinned;
- u32 pte_update_batched;
-
- u32 mmu_update;
- u32 mmu_update_extended;
- u32 mmu_update_histo[MMU_UPDATE_HISTO];
-
- u32 prot_commit;
- u32 prot_commit_batched;
-
- u32 set_pte_at;
- u32 set_pte_at_batched;
- u32 set_pte_at_pinned;
- u32 set_pte_at_current;
- u32 set_pte_at_kernel;
-} mmu_stats;
-
-static u8 zero_stats;
-
-static inline void check_zero(void)
-{
- if (unlikely(zero_stats)) {
- memset(&mmu_stats, 0, sizeof(mmu_stats));
- zero_stats = 0;
- }
-}
-
-#define ADD_STATS(elem, val) \
- do { check_zero(); mmu_stats.elem += (val); } while(0)
-
-#else /* !CONFIG_XEN_DEBUG_FS */
-
-#define ADD_STATS(elem, val) do { (void)(val); } while(0)
-
-#endif /* CONFIG_XEN_DEBUG_FS */
-
-
/*
* Identity map, in addition to plain kernel map. This needs to be
* large enough to allocate page table pages to allocate the rest.
@@ -243,11 +188,6 @@
return PagePinned(page);
}
-static bool xen_iomap_pte(pte_t pte)
-{
- return pte_flags(pte) & _PAGE_IOMAP;
-}
-
void xen_set_domain_pte(pte_t *ptep, pte_t pteval, unsigned domid)
{
struct multicall_space mcs;
@@ -257,7 +197,7 @@
u = mcs.args;
/* ptep might be kmapped when using 32-bit HIGHPTE */
- u->ptr = arbitrary_virt_to_machine(ptep).maddr;
+ u->ptr = virt_to_machine(ptep).maddr;
u->val = pte_val_ma(pteval);
MULTI_mmu_update(mcs.mc, mcs.args, 1, NULL, domid);
@@ -266,11 +206,6 @@
}
EXPORT_SYMBOL_GPL(xen_set_domain_pte);
-static void xen_set_iomap_pte(pte_t *ptep, pte_t pteval)
-{
- xen_set_domain_pte(ptep, pteval, DOMID_IO);
-}
-
static void xen_extend_mmu_update(const struct mmu_update *update)
{
struct multicall_space mcs;
@@ -279,27 +214,17 @@
mcs = xen_mc_extend_args(__HYPERVISOR_mmu_update, sizeof(*u));
if (mcs.mc != NULL) {
- ADD_STATS(mmu_update_extended, 1);
- ADD_STATS(mmu_update_histo[mcs.mc->args[1]], -1);
-
mcs.mc->args[1]++;
-
- if (mcs.mc->args[1] < MMU_UPDATE_HISTO)
- ADD_STATS(mmu_update_histo[mcs.mc->args[1]], 1);
- else
- ADD_STATS(mmu_update_histo[0], 1);
} else {
- ADD_STATS(mmu_update, 1);
mcs = __xen_mc_entry(sizeof(*u));
MULTI_mmu_update(mcs.mc, mcs.args, 1, NULL, DOMID_SELF);
- ADD_STATS(mmu_update_histo[1], 1);
}
u = mcs.args;
*u = *update;
}
-void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val)
+static void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val)
{
struct mmu_update u;
@@ -312,17 +237,13 @@
u.val = pmd_val_ma(val);
xen_extend_mmu_update(&u);
- ADD_STATS(pmd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
-
xen_mc_issue(PARAVIRT_LAZY_MMU);
preempt_enable();
}
-void xen_set_pmd(pmd_t *ptr, pmd_t val)
+static void xen_set_pmd(pmd_t *ptr, pmd_t val)
{
- ADD_STATS(pmd_update, 1);
-
/* If page is not pinned, we can just update the entry
directly */
if (!xen_page_pinned(ptr)) {
@@ -330,8 +251,6 @@
return;
}
- ADD_STATS(pmd_update_pinned, 1);
-
xen_set_pmd_hyper(ptr, val);
}
@@ -344,35 +263,34 @@
set_pte_vaddr(vaddr, mfn_pte(mfn, flags));
}
-void xen_set_pte_at(struct mm_struct *mm, unsigned long addr,
+static bool xen_batched_set_pte(pte_t *ptep, pte_t pteval)
+{
+ struct mmu_update u;
+
+ if (paravirt_get_lazy_mode() != PARAVIRT_LAZY_MMU)
+ return false;
+
+ xen_mc_batch();
+
+ u.ptr = virt_to_machine(ptep).maddr | MMU_NORMAL_PT_UPDATE;
+ u.val = pte_val_ma(pteval);
+ xen_extend_mmu_update(&u);
+
+ xen_mc_issue(PARAVIRT_LAZY_MMU);
+
+ return true;
+}
+
+static void xen_set_pte(pte_t *ptep, pte_t pteval)
+{
+ if (!xen_batched_set_pte(ptep, pteval))
+ native_set_pte(ptep, pteval);
+}
+
+static void xen_set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pteval)
{
- if (xen_iomap_pte(pteval)) {
- xen_set_iomap_pte(ptep, pteval);
- goto out;
- }
-
- ADD_STATS(set_pte_at, 1);
-// ADD_STATS(set_pte_at_pinned, xen_page_pinned(ptep));
- ADD_STATS(set_pte_at_current, mm == current->mm);
- ADD_STATS(set_pte_at_kernel, mm == &init_mm);
-
- if (mm == current->mm || mm == &init_mm) {
- if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) {
- struct multicall_space mcs;
- mcs = xen_mc_entry(0);
-
- MULTI_update_va_mapping(mcs.mc, addr, pteval, 0);
- ADD_STATS(set_pte_at_batched, 1);
- xen_mc_issue(PARAVIRT_LAZY_MMU);
- goto out;
- } else
- if (HYPERVISOR_update_va_mapping(addr, pteval, 0) == 0)
- goto out;
- }
xen_set_pte(ptep, pteval);
-
-out: return;
}
pte_t xen_ptep_modify_prot_start(struct mm_struct *mm,
@@ -389,13 +307,10 @@
xen_mc_batch();
- u.ptr = arbitrary_virt_to_machine(ptep).maddr | MMU_PT_UPDATE_PRESERVE_AD;
+ u.ptr = virt_to_machine(ptep).maddr | MMU_PT_UPDATE_PRESERVE_AD;
u.val = pte_val_ma(pte);
xen_extend_mmu_update(&u);
- ADD_STATS(prot_commit, 1);
- ADD_STATS(prot_commit_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
-
xen_mc_issue(PARAVIRT_LAZY_MMU);
}
@@ -463,7 +378,7 @@
return val;
}
-pteval_t xen_pte_val(pte_t pte)
+static pteval_t xen_pte_val(pte_t pte)
{
pteval_t pteval = pte.pte;
@@ -480,7 +395,7 @@
}
PV_CALLEE_SAVE_REGS_THUNK(xen_pte_val);
-pgdval_t xen_pgd_val(pgd_t pgd)
+static pgdval_t xen_pgd_val(pgd_t pgd)
{
return pte_mfn_to_pfn(pgd.pgd);
}
@@ -511,7 +426,7 @@
WARN_ON(pat != 0x0007010600070106ull);
}
-pte_t xen_make_pte(pteval_t pte)
+static pte_t xen_make_pte(pteval_t pte)
{
phys_addr_t addr = (pte & PTE_PFN_MASK);
@@ -581,20 +496,20 @@
PV_CALLEE_SAVE_REGS_THUNK(xen_make_pte_debug);
#endif
-pgd_t xen_make_pgd(pgdval_t pgd)
+static pgd_t xen_make_pgd(pgdval_t pgd)
{
pgd = pte_pfn_to_mfn(pgd);
return native_make_pgd(pgd);
}
PV_CALLEE_SAVE_REGS_THUNK(xen_make_pgd);
-pmdval_t xen_pmd_val(pmd_t pmd)
+static pmdval_t xen_pmd_val(pmd_t pmd)
{
return pte_mfn_to_pfn(pmd.pmd);
}
PV_CALLEE_SAVE_REGS_THUNK(xen_pmd_val);
-void xen_set_pud_hyper(pud_t *ptr, pud_t val)
+static void xen_set_pud_hyper(pud_t *ptr, pud_t val)
{
struct mmu_update u;
@@ -607,17 +522,13 @@
u.val = pud_val_ma(val);
xen_extend_mmu_update(&u);
- ADD_STATS(pud_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
-
xen_mc_issue(PARAVIRT_LAZY_MMU);
preempt_enable();
}
-void xen_set_pud(pud_t *ptr, pud_t val)
+static void xen_set_pud(pud_t *ptr, pud_t val)
{
- ADD_STATS(pud_update, 1);
-
/* If page is not pinned, we can just update the entry
directly */
if (!xen_page_pinned(ptr)) {
@@ -625,56 +536,28 @@
return;
}
- ADD_STATS(pud_update_pinned, 1);
-
xen_set_pud_hyper(ptr, val);
}
-void xen_set_pte(pte_t *ptep, pte_t pte)
-{
- if (xen_iomap_pte(pte)) {
- xen_set_iomap_pte(ptep, pte);
- return;
- }
-
- ADD_STATS(pte_update, 1);
-// ADD_STATS(pte_update_pinned, xen_page_pinned(ptep));
- ADD_STATS(pte_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
-
#ifdef CONFIG_X86_PAE
- ptep->pte_high = pte.pte_high;
- smp_wmb();
- ptep->pte_low = pte.pte_low;
-#else
- *ptep = pte;
-#endif
-}
-
-#ifdef CONFIG_X86_PAE
-void xen_set_pte_atomic(pte_t *ptep, pte_t pte)
+static void xen_set_pte_atomic(pte_t *ptep, pte_t pte)
{
- if (xen_iomap_pte(pte)) {
- xen_set_iomap_pte(ptep, pte);
- return;
- }
-
set_64bit((u64 *)ptep, native_pte_val(pte));
}
-void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
+static void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
- ptep->pte_low = 0;
- smp_wmb(); /* make sure low gets written first */
- ptep->pte_high = 0;
+ if (!xen_batched_set_pte(ptep, native_make_pte(0)))
+ native_pte_clear(mm, addr, ptep);
}
-void xen_pmd_clear(pmd_t *pmdp)
+static void xen_pmd_clear(pmd_t *pmdp)
{
set_pmd(pmdp, __pmd(0));
}
#endif /* CONFIG_X86_PAE */
-pmd_t xen_make_pmd(pmdval_t pmd)
+static pmd_t xen_make_pmd(pmdval_t pmd)
{
pmd = pte_pfn_to_mfn(pmd);
return native_make_pmd(pmd);
@@ -682,13 +565,13 @@
PV_CALLEE_SAVE_REGS_THUNK(xen_make_pmd);
#if PAGETABLE_LEVELS == 4
-pudval_t xen_pud_val(pud_t pud)
+static pudval_t xen_pud_val(pud_t pud)
{
return pte_mfn_to_pfn(pud.pud);
}
PV_CALLEE_SAVE_REGS_THUNK(xen_pud_val);
-pud_t xen_make_pud(pudval_t pud)
+static pud_t xen_make_pud(pudval_t pud)
{
pud = pte_pfn_to_mfn(pud);
@@ -696,7 +579,7 @@
}
PV_CALLEE_SAVE_REGS_THUNK(xen_make_pud);
-pgd_t *xen_get_user_pgd(pgd_t *pgd)
+static pgd_t *xen_get_user_pgd(pgd_t *pgd)
{
pgd_t *pgd_page = (pgd_t *)(((unsigned long)pgd) & PAGE_MASK);
unsigned offset = pgd - pgd_page;
@@ -728,7 +611,7 @@
* 2. It is always pinned
* 3. It has no user pagetable attached to it
*/
-void __init xen_set_pgd_hyper(pgd_t *ptr, pgd_t val)
+static void __init xen_set_pgd_hyper(pgd_t *ptr, pgd_t val)
{
preempt_disable();
@@ -741,12 +624,10 @@
preempt_enable();
}
-void xen_set_pgd(pgd_t *ptr, pgd_t val)
+static void xen_set_pgd(pgd_t *ptr, pgd_t val)
{
pgd_t *user_ptr = xen_get_user_pgd(ptr);
- ADD_STATS(pgd_update, 1);
-
/* If page is not pinned, we can just update the entry
directly */
if (!xen_page_pinned(ptr)) {
@@ -758,9 +639,6 @@
return;
}
- ADD_STATS(pgd_update_pinned, 1);
- ADD_STATS(pgd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
-
/* If it's pinned, then we can at least batch the kernel and
user updates together. */
xen_mc_batch();
@@ -1162,14 +1040,14 @@
spin_unlock(&pgd_lock);
}
-void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next)
+static void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next)
{
spin_lock(&next->page_table_lock);
xen_pgd_pin(next);
spin_unlock(&next->page_table_lock);
}
-void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm)
+static void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm)
{
spin_lock(&mm->page_table_lock);
xen_pgd_pin(mm);
@@ -1256,7 +1134,7 @@
* pagetable because of lazy tlb flushing. This means we need need to
* switch all CPUs off this pagetable before we can unpin it.
*/
-void xen_exit_mmap(struct mm_struct *mm)
+static void xen_exit_mmap(struct mm_struct *mm)
{
get_cpu(); /* make sure we don't move around */
xen_drop_mm_ref(mm);
@@ -2371,7 +2249,7 @@
struct remap_data *rmd = data;
pte_t pte = pte_mkspecial(pfn_pte(rmd->mfn++, rmd->prot));
- rmd->mmu_update->ptr = arbitrary_virt_to_machine(ptep).maddr;
+ rmd->mmu_update->ptr = virt_to_machine(ptep).maddr;
rmd->mmu_update->val = pte_val_ma(pte);
rmd->mmu_update++;
@@ -2425,7 +2303,6 @@
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range);
#ifdef CONFIG_XEN_DEBUG_FS
-
static int p2m_dump_open(struct inode *inode, struct file *filp)
{
return single_open(filp, p2m_dump_show, NULL);
@@ -2437,65 +2314,4 @@
.llseek = seq_lseek,
.release = single_release,
};
-
-static struct dentry *d_mmu_debug;
-
-static int __init xen_mmu_debugfs(void)
-{
- struct dentry *d_xen = xen_init_debugfs();
-
- if (d_xen == NULL)
- return -ENOMEM;
-
- d_mmu_debug = debugfs_create_dir("mmu", d_xen);
-
- debugfs_create_u8("zero_stats", 0644, d_mmu_debug, &zero_stats);
-
- debugfs_create_u32("pgd_update", 0444, d_mmu_debug, &mmu_stats.pgd_update);
- debugfs_create_u32("pgd_update_pinned", 0444, d_mmu_debug,
- &mmu_stats.pgd_update_pinned);
- debugfs_create_u32("pgd_update_batched", 0444, d_mmu_debug,
- &mmu_stats.pgd_update_pinned);
-
- debugfs_create_u32("pud_update", 0444, d_mmu_debug, &mmu_stats.pud_update);
- debugfs_create_u32("pud_update_pinned", 0444, d_mmu_debug,
- &mmu_stats.pud_update_pinned);
- debugfs_create_u32("pud_update_batched", 0444, d_mmu_debug,
- &mmu_stats.pud_update_pinned);
-
- debugfs_create_u32("pmd_update", 0444, d_mmu_debug, &mmu_stats.pmd_update);
- debugfs_create_u32("pmd_update_pinned", 0444, d_mmu_debug,
- &mmu_stats.pmd_update_pinned);
- debugfs_create_u32("pmd_update_batched", 0444, d_mmu_debug,
- &mmu_stats.pmd_update_pinned);
-
- debugfs_create_u32("pte_update", 0444, d_mmu_debug, &mmu_stats.pte_update);
-// debugfs_create_u32("pte_update_pinned", 0444, d_mmu_debug,
-// &mmu_stats.pte_update_pinned);
- debugfs_create_u32("pte_update_batched", 0444, d_mmu_debug,
- &mmu_stats.pte_update_pinned);
-
- debugfs_create_u32("mmu_update", 0444, d_mmu_debug, &mmu_stats.mmu_update);
- debugfs_create_u32("mmu_update_extended", 0444, d_mmu_debug,
- &mmu_stats.mmu_update_extended);
- xen_debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug,
- mmu_stats.mmu_update_histo, 20);
-
- debugfs_create_u32("set_pte_at", 0444, d_mmu_debug, &mmu_stats.set_pte_at);
- debugfs_create_u32("set_pte_at_batched", 0444, d_mmu_debug,
- &mmu_stats.set_pte_at_batched);
- debugfs_create_u32("set_pte_at_current", 0444, d_mmu_debug,
- &mmu_stats.set_pte_at_current);
- debugfs_create_u32("set_pte_at_kernel", 0444, d_mmu_debug,
- &mmu_stats.set_pte_at_kernel);
-
- debugfs_create_u32("prot_commit", 0444, d_mmu_debug, &mmu_stats.prot_commit);
- debugfs_create_u32("prot_commit_batched", 0444, d_mmu_debug,
- &mmu_stats.prot_commit_batched);
-
- debugfs_create_file("p2m", 0600, d_mmu_debug, NULL, &p2m_dump_fops);
- return 0;
-}
-fs_initcall(xen_mmu_debugfs);
-
-#endif /* CONFIG_XEN_DEBUG_FS */
+#endif /* CONFIG_XEN_DEBUG_FS */
diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h
index 537bb9a..73809bb 100644
--- a/arch/x86/xen/mmu.h
+++ b/arch/x86/xen/mmu.h
@@ -15,43 +15,6 @@
void set_pte_mfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags);
-
-void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next);
-void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm);
-void xen_exit_mmap(struct mm_struct *mm);
-
-pteval_t xen_pte_val(pte_t);
-pmdval_t xen_pmd_val(pmd_t);
-pgdval_t xen_pgd_val(pgd_t);
-
-pte_t xen_make_pte(pteval_t);
-pmd_t xen_make_pmd(pmdval_t);
-pgd_t xen_make_pgd(pgdval_t);
-
-void xen_set_pte(pte_t *ptep, pte_t pteval);
-void xen_set_pte_at(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pteval);
-
-#ifdef CONFIG_X86_PAE
-void xen_set_pte_atomic(pte_t *ptep, pte_t pte);
-void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
-void xen_pmd_clear(pmd_t *pmdp);
-#endif /* CONFIG_X86_PAE */
-
-void xen_set_pmd(pmd_t *pmdp, pmd_t pmdval);
-void xen_set_pud(pud_t *ptr, pud_t val);
-void xen_set_pmd_hyper(pmd_t *pmdp, pmd_t pmdval);
-void xen_set_pud_hyper(pud_t *ptr, pud_t val);
-
-#if PAGETABLE_LEVELS == 4
-pudval_t xen_pud_val(pud_t pud);
-pud_t xen_make_pud(pudval_t pudval);
-void xen_set_pgd(pgd_t *pgdp, pgd_t pgd);
-void xen_set_pgd_hyper(pgd_t *pgdp, pgd_t pgd);
-#endif
-
-pgd_t *xen_get_user_pgd(pgd_t *pgd);
-
pte_t xen_ptep_modify_prot_start(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
void xen_ptep_modify_prot_commit(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte);
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 7c275f5..5d43c1f 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -20,12 +20,6 @@
config RWSEM_XCHGADD_ALGORITHM
def_bool y
-config GENERIC_FIND_NEXT_BIT
- def_bool y
-
-config GENERIC_FIND_BIT_LE
- def_bool y
-
config GENERIC_HWEIGHT
def_bool y
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 07371cf..bcaf16e 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -30,10 +30,8 @@
static struct cgroup_subsys_state *blkiocg_create(struct cgroup_subsys *,
struct cgroup *);
-static int blkiocg_can_attach(struct cgroup_subsys *, struct cgroup *,
- struct task_struct *, bool);
-static void blkiocg_attach(struct cgroup_subsys *, struct cgroup *,
- struct cgroup *, struct task_struct *, bool);
+static int blkiocg_can_attach_task(struct cgroup *, struct task_struct *);
+static void blkiocg_attach_task(struct cgroup *, struct task_struct *);
static void blkiocg_destroy(struct cgroup_subsys *, struct cgroup *);
static int blkiocg_populate(struct cgroup_subsys *, struct cgroup *);
@@ -46,8 +44,8 @@
struct cgroup_subsys blkio_subsys = {
.name = "blkio",
.create = blkiocg_create,
- .can_attach = blkiocg_can_attach,
- .attach = blkiocg_attach,
+ .can_attach_task = blkiocg_can_attach_task,
+ .attach_task = blkiocg_attach_task,
.destroy = blkiocg_destroy,
.populate = blkiocg_populate,
#ifdef CONFIG_BLK_CGROUP
@@ -1616,9 +1614,7 @@
* of the main cic data structures. For now we allow a task to change
* its cgroup only if it's the only owner of its ioc.
*/
-static int blkiocg_can_attach(struct cgroup_subsys *subsys,
- struct cgroup *cgroup, struct task_struct *tsk,
- bool threadgroup)
+static int blkiocg_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
{
struct io_context *ioc;
int ret = 0;
@@ -1633,9 +1629,7 @@
return ret;
}
-static void blkiocg_attach(struct cgroup_subsys *subsys, struct cgroup *cgroup,
- struct cgroup *prev, struct task_struct *tsk,
- bool threadgroup)
+static void blkiocg_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
{
struct io_context *ioc;
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index ffd8797..471a040 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -6,6 +6,7 @@
*/
#include "bcma_private.h"
+#include <linux/slab.h>
#include <linux/bcma/bcma.h>
#include <linux/pci.h>
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 38223e9..58c0e63 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -36,6 +36,7 @@
#include <asm/system.h>
#include <linux/poll.h>
#include <linux/sched.h>
+#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -1896,102 +1897,128 @@
EXPORT_SYMBOL(ipmi_request_supply_msgs);
#ifdef CONFIG_PROC_FS
-static int ipmb_file_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int smi_ipmb_proc_show(struct seq_file *m, void *v)
{
- char *out = (char *) page;
- ipmi_smi_t intf = data;
+ ipmi_smi_t intf = m->private;
int i;
- int rv = 0;
- for (i = 0; i < IPMI_MAX_CHANNELS; i++)
- rv += sprintf(out+rv, "%x ", intf->channels[i].address);
- out[rv-1] = '\n'; /* Replace the final space with a newline */
- out[rv] = '\0';
- rv++;
- return rv;
+ seq_printf(m, "%x", intf->channels[0].address);
+ for (i = 1; i < IPMI_MAX_CHANNELS; i++)
+ seq_printf(m, " %x", intf->channels[i].address);
+ return seq_putc(m, '\n');
}
-static int version_file_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int smi_ipmb_proc_open(struct inode *inode, struct file *file)
{
- char *out = (char *) page;
- ipmi_smi_t intf = data;
+ return single_open(file, smi_ipmb_proc_show, PDE(inode)->data);
+}
- return sprintf(out, "%u.%u\n",
+static const struct file_operations smi_ipmb_proc_ops = {
+ .open = smi_ipmb_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smi_version_proc_show(struct seq_file *m, void *v)
+{
+ ipmi_smi_t intf = m->private;
+
+ return seq_printf(m, "%u.%u\n",
ipmi_version_major(&intf->bmc->id),
ipmi_version_minor(&intf->bmc->id));
}
-static int stat_file_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int smi_version_proc_open(struct inode *inode, struct file *file)
{
- char *out = (char *) page;
- ipmi_smi_t intf = data;
-
- out += sprintf(out, "sent_invalid_commands: %u\n",
- ipmi_get_stat(intf, sent_invalid_commands));
- out += sprintf(out, "sent_local_commands: %u\n",
- ipmi_get_stat(intf, sent_local_commands));
- out += sprintf(out, "handled_local_responses: %u\n",
- ipmi_get_stat(intf, handled_local_responses));
- out += sprintf(out, "unhandled_local_responses: %u\n",
- ipmi_get_stat(intf, unhandled_local_responses));
- out += sprintf(out, "sent_ipmb_commands: %u\n",
- ipmi_get_stat(intf, sent_ipmb_commands));
- out += sprintf(out, "sent_ipmb_command_errs: %u\n",
- ipmi_get_stat(intf, sent_ipmb_command_errs));
- out += sprintf(out, "retransmitted_ipmb_commands: %u\n",
- ipmi_get_stat(intf, retransmitted_ipmb_commands));
- out += sprintf(out, "timed_out_ipmb_commands: %u\n",
- ipmi_get_stat(intf, timed_out_ipmb_commands));
- out += sprintf(out, "timed_out_ipmb_broadcasts: %u\n",
- ipmi_get_stat(intf, timed_out_ipmb_broadcasts));
- out += sprintf(out, "sent_ipmb_responses: %u\n",
- ipmi_get_stat(intf, sent_ipmb_responses));
- out += sprintf(out, "handled_ipmb_responses: %u\n",
- ipmi_get_stat(intf, handled_ipmb_responses));
- out += sprintf(out, "invalid_ipmb_responses: %u\n",
- ipmi_get_stat(intf, invalid_ipmb_responses));
- out += sprintf(out, "unhandled_ipmb_responses: %u\n",
- ipmi_get_stat(intf, unhandled_ipmb_responses));
- out += sprintf(out, "sent_lan_commands: %u\n",
- ipmi_get_stat(intf, sent_lan_commands));
- out += sprintf(out, "sent_lan_command_errs: %u\n",
- ipmi_get_stat(intf, sent_lan_command_errs));
- out += sprintf(out, "retransmitted_lan_commands: %u\n",
- ipmi_get_stat(intf, retransmitted_lan_commands));
- out += sprintf(out, "timed_out_lan_commands: %u\n",
- ipmi_get_stat(intf, timed_out_lan_commands));
- out += sprintf(out, "sent_lan_responses: %u\n",
- ipmi_get_stat(intf, sent_lan_responses));
- out += sprintf(out, "handled_lan_responses: %u\n",
- ipmi_get_stat(intf, handled_lan_responses));
- out += sprintf(out, "invalid_lan_responses: %u\n",
- ipmi_get_stat(intf, invalid_lan_responses));
- out += sprintf(out, "unhandled_lan_responses: %u\n",
- ipmi_get_stat(intf, unhandled_lan_responses));
- out += sprintf(out, "handled_commands: %u\n",
- ipmi_get_stat(intf, handled_commands));
- out += sprintf(out, "invalid_commands: %u\n",
- ipmi_get_stat(intf, invalid_commands));
- out += sprintf(out, "unhandled_commands: %u\n",
- ipmi_get_stat(intf, unhandled_commands));
- out += sprintf(out, "invalid_events: %u\n",
- ipmi_get_stat(intf, invalid_events));
- out += sprintf(out, "events: %u\n",
- ipmi_get_stat(intf, events));
- out += sprintf(out, "failed rexmit LAN msgs: %u\n",
- ipmi_get_stat(intf, dropped_rexmit_lan_commands));
- out += sprintf(out, "failed rexmit IPMB msgs: %u\n",
- ipmi_get_stat(intf, dropped_rexmit_ipmb_commands));
-
- return (out - ((char *) page));
+ return single_open(file, smi_version_proc_show, PDE(inode)->data);
}
+
+static const struct file_operations smi_version_proc_ops = {
+ .open = smi_version_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smi_stats_proc_show(struct seq_file *m, void *v)
+{
+ ipmi_smi_t intf = m->private;
+
+ seq_printf(m, "sent_invalid_commands: %u\n",
+ ipmi_get_stat(intf, sent_invalid_commands));
+ seq_printf(m, "sent_local_commands: %u\n",
+ ipmi_get_stat(intf, sent_local_commands));
+ seq_printf(m, "handled_local_responses: %u\n",
+ ipmi_get_stat(intf, handled_local_responses));
+ seq_printf(m, "unhandled_local_responses: %u\n",
+ ipmi_get_stat(intf, unhandled_local_responses));
+ seq_printf(m, "sent_ipmb_commands: %u\n",
+ ipmi_get_stat(intf, sent_ipmb_commands));
+ seq_printf(m, "sent_ipmb_command_errs: %u\n",
+ ipmi_get_stat(intf, sent_ipmb_command_errs));
+ seq_printf(m, "retransmitted_ipmb_commands: %u\n",
+ ipmi_get_stat(intf, retransmitted_ipmb_commands));
+ seq_printf(m, "timed_out_ipmb_commands: %u\n",
+ ipmi_get_stat(intf, timed_out_ipmb_commands));
+ seq_printf(m, "timed_out_ipmb_broadcasts: %u\n",
+ ipmi_get_stat(intf, timed_out_ipmb_broadcasts));
+ seq_printf(m, "sent_ipmb_responses: %u\n",
+ ipmi_get_stat(intf, sent_ipmb_responses));
+ seq_printf(m, "handled_ipmb_responses: %u\n",
+ ipmi_get_stat(intf, handled_ipmb_responses));
+ seq_printf(m, "invalid_ipmb_responses: %u\n",
+ ipmi_get_stat(intf, invalid_ipmb_responses));
+ seq_printf(m, "unhandled_ipmb_responses: %u\n",
+ ipmi_get_stat(intf, unhandled_ipmb_responses));
+ seq_printf(m, "sent_lan_commands: %u\n",
+ ipmi_get_stat(intf, sent_lan_commands));
+ seq_printf(m, "sent_lan_command_errs: %u\n",
+ ipmi_get_stat(intf, sent_lan_command_errs));
+ seq_printf(m, "retransmitted_lan_commands: %u\n",
+ ipmi_get_stat(intf, retransmitted_lan_commands));
+ seq_printf(m, "timed_out_lan_commands: %u\n",
+ ipmi_get_stat(intf, timed_out_lan_commands));
+ seq_printf(m, "sent_lan_responses: %u\n",
+ ipmi_get_stat(intf, sent_lan_responses));
+ seq_printf(m, "handled_lan_responses: %u\n",
+ ipmi_get_stat(intf, handled_lan_responses));
+ seq_printf(m, "invalid_lan_responses: %u\n",
+ ipmi_get_stat(intf, invalid_lan_responses));
+ seq_printf(m, "unhandled_lan_responses: %u\n",
+ ipmi_get_stat(intf, unhandled_lan_responses));
+ seq_printf(m, "handled_commands: %u\n",
+ ipmi_get_stat(intf, handled_commands));
+ seq_printf(m, "invalid_commands: %u\n",
+ ipmi_get_stat(intf, invalid_commands));
+ seq_printf(m, "unhandled_commands: %u\n",
+ ipmi_get_stat(intf, unhandled_commands));
+ seq_printf(m, "invalid_events: %u\n",
+ ipmi_get_stat(intf, invalid_events));
+ seq_printf(m, "events: %u\n",
+ ipmi_get_stat(intf, events));
+ seq_printf(m, "failed rexmit LAN msgs: %u\n",
+ ipmi_get_stat(intf, dropped_rexmit_lan_commands));
+ seq_printf(m, "failed rexmit IPMB msgs: %u\n",
+ ipmi_get_stat(intf, dropped_rexmit_ipmb_commands));
+ return 0;
+}
+
+static int smi_stats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, smi_stats_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations smi_stats_proc_ops = {
+ .open = smi_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif /* CONFIG_PROC_FS */
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
- read_proc_t *read_proc,
+ const struct file_operations *proc_ops,
void *data)
{
int rv = 0;
@@ -2010,15 +2037,12 @@
}
strcpy(entry->name, name);
- file = create_proc_entry(name, 0, smi->proc_dir);
+ file = proc_create_data(name, 0, smi->proc_dir, proc_ops, data);
if (!file) {
kfree(entry->name);
kfree(entry);
rv = -ENOMEM;
} else {
- file->data = data;
- file->read_proc = read_proc;
-
mutex_lock(&smi->proc_entry_lock);
/* Stick it on the list. */
entry->next = smi->proc_entries;
@@ -2043,17 +2067,17 @@
if (rv == 0)
rv = ipmi_smi_add_proc_entry(smi, "stats",
- stat_file_read_proc,
+ &smi_stats_proc_ops,
smi);
if (rv == 0)
rv = ipmi_smi_add_proc_entry(smi, "ipmb",
- ipmb_file_read_proc,
+ &smi_ipmb_proc_ops,
smi);
if (rv == 0)
rv = ipmi_smi_add_proc_entry(smi, "version",
- version_file_read_proc,
+ &smi_version_proc_ops,
smi);
#endif /* CONFIG_PROC_FS */
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 64c6b85..9397ab4 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -43,6 +43,7 @@
#include <linux/moduleparam.h>
#include <asm/system.h>
#include <linux/sched.h>
+#include <linux/seq_file.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
@@ -2805,54 +2806,73 @@
return rv;
}
-static int type_file_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int smi_type_proc_show(struct seq_file *m, void *v)
{
- struct smi_info *smi = data;
+ struct smi_info *smi = m->private;
- return sprintf(page, "%s\n", si_to_str[smi->si_type]);
+ return seq_printf(m, "%s\n", si_to_str[smi->si_type]);
}
-static int stat_file_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int smi_type_proc_open(struct inode *inode, struct file *file)
{
- char *out = (char *) page;
- struct smi_info *smi = data;
+ return single_open(file, smi_type_proc_show, PDE(inode)->data);
+}
- out += sprintf(out, "interrupts_enabled: %d\n",
+static const struct file_operations smi_type_proc_ops = {
+ .open = smi_type_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smi_si_stats_proc_show(struct seq_file *m, void *v)
+{
+ struct smi_info *smi = m->private;
+
+ seq_printf(m, "interrupts_enabled: %d\n",
smi->irq && !smi->interrupt_disabled);
- out += sprintf(out, "short_timeouts: %u\n",
+ seq_printf(m, "short_timeouts: %u\n",
smi_get_stat(smi, short_timeouts));
- out += sprintf(out, "long_timeouts: %u\n",
+ seq_printf(m, "long_timeouts: %u\n",
smi_get_stat(smi, long_timeouts));
- out += sprintf(out, "idles: %u\n",
+ seq_printf(m, "idles: %u\n",
smi_get_stat(smi, idles));
- out += sprintf(out, "interrupts: %u\n",
+ seq_printf(m, "interrupts: %u\n",
smi_get_stat(smi, interrupts));
- out += sprintf(out, "attentions: %u\n",
+ seq_printf(m, "attentions: %u\n",
smi_get_stat(smi, attentions));
- out += sprintf(out, "flag_fetches: %u\n",
+ seq_printf(m, "flag_fetches: %u\n",
smi_get_stat(smi, flag_fetches));
- out += sprintf(out, "hosed_count: %u\n",
+ seq_printf(m, "hosed_count: %u\n",
smi_get_stat(smi, hosed_count));
- out += sprintf(out, "complete_transactions: %u\n",
+ seq_printf(m, "complete_transactions: %u\n",
smi_get_stat(smi, complete_transactions));
- out += sprintf(out, "events: %u\n",
+ seq_printf(m, "events: %u\n",
smi_get_stat(smi, events));
- out += sprintf(out, "watchdog_pretimeouts: %u\n",
+ seq_printf(m, "watchdog_pretimeouts: %u\n",
smi_get_stat(smi, watchdog_pretimeouts));
- out += sprintf(out, "incoming_messages: %u\n",
+ seq_printf(m, "incoming_messages: %u\n",
smi_get_stat(smi, incoming_messages));
-
- return out - page;
+ return 0;
}
-static int param_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int smi_si_stats_proc_open(struct inode *inode, struct file *file)
{
- struct smi_info *smi = data;
+ return single_open(file, smi_si_stats_proc_show, PDE(inode)->data);
+}
- return sprintf(page,
+static const struct file_operations smi_si_stats_proc_ops = {
+ .open = smi_si_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int smi_params_proc_show(struct seq_file *m, void *v)
+{
+ struct smi_info *smi = m->private;
+
+ return seq_printf(m,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi->si_type],
addr_space_to_str[smi->io.addr_type],
@@ -2864,6 +2884,18 @@
smi->slave_addr);
}
+static int smi_params_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, smi_params_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations smi_params_proc_ops = {
+ .open = smi_params_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/*
* oem_data_avail_to_receive_msg_avail
* @info - smi_info structure with msg_flags set
@@ -3257,7 +3289,7 @@
}
rv = ipmi_smi_add_proc_entry(new_smi->intf, "type",
- type_file_read_proc,
+ &smi_type_proc_ops,
new_smi);
if (rv) {
dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv);
@@ -3265,7 +3297,7 @@
}
rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats",
- stat_file_read_proc,
+ &smi_si_stats_proc_ops,
new_smi);
if (rv) {
dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv);
@@ -3273,7 +3305,7 @@
}
rv = ipmi_smi_add_proc_entry(new_smi->intf, "params",
- param_read_proc,
+ &smi_params_proc_ops,
new_smi);
if (rv) {
dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv);
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index 966a95b..25d139c 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -271,14 +271,13 @@
pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
if (vdata_size <= PAGE_SIZE)
- vdata = kmalloc(vdata_size, GFP_KERNEL);
+ vdata = kzalloc(vdata_size, GFP_KERNEL);
else {
- vdata = vmalloc(vdata_size);
+ vdata = vzalloc(vdata_size);
flags = VMD_VMALLOCED;
}
if (!vdata)
return -ENOMEM;
- memset(vdata, 0, vdata_size);
vdata->vm_start = vma->vm_start;
vdata->vm_end = vma->vm_end;
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index f176dba..3fcf80f 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -457,6 +457,7 @@
return -ENODEV;
modes = port->modes;
+ parport_put_port(port);
if (copy_to_user (argp, &modes, sizeof (modes))) {
return -EFAULT;
}
diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
index d2c75fe..f69f90a6 100644
--- a/drivers/dma/timb_dma.c
+++ b/drivers/dma/timb_dma.c
@@ -27,7 +27,6 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/timb_dma.h>
@@ -685,7 +684,7 @@
static int __devinit td_probe(struct platform_device *pdev)
{
- struct timb_dma_platform_data *pdata = mfd_get_data(pdev);
+ struct timb_dma_platform_data *pdata = pdev->dev.platform_data;
struct timb_dma *td;
struct resource *iomem;
int irq;
diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c
index cace0a7..e47e73b 100644
--- a/drivers/edac/amd76x_edac.c
+++ b/drivers/edac/amd76x_edac.c
@@ -19,7 +19,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define AMD76X_REVISION " Ver: 2.0.2 " __DATE__
+#define AMD76X_REVISION " Ver: 2.0.2"
#define EDAC_MOD_STR "amd76x_edac"
#define amd76x_printk(level, fmt, arg...) \
diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c
index 35b78d0..ddd8900 100644
--- a/drivers/edac/amd8111_edac.c
+++ b/drivers/edac/amd8111_edac.c
@@ -33,7 +33,7 @@
#include "edac_module.h"
#include "amd8111_edac.h"
-#define AMD8111_EDAC_REVISION " Ver: 1.0.0 " __DATE__
+#define AMD8111_EDAC_REVISION " Ver: 1.0.0"
#define AMD8111_EDAC_MOD_STR "amd8111_edac"
#define PCI_DEVICE_ID_AMD_8111_PCI 0x7460
diff --git a/drivers/edac/amd8131_edac.c b/drivers/edac/amd8131_edac.c
index b432d60..a5c6805 100644
--- a/drivers/edac/amd8131_edac.c
+++ b/drivers/edac/amd8131_edac.c
@@ -33,7 +33,7 @@
#include "edac_module.h"
#include "amd8131_edac.h"
-#define AMD8131_EDAC_REVISION " Ver: 1.0.0 " __DATE__
+#define AMD8131_EDAC_REVISION " Ver: 1.0.0"
#define AMD8131_EDAC_MOD_STR "amd8131_edac"
/* Wrapper functions for accessing PCI configuration space */
diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c
index 837ad8f..a687a0d 100644
--- a/drivers/edac/cpc925_edac.c
+++ b/drivers/edac/cpc925_edac.c
@@ -30,7 +30,7 @@
#include "edac_core.h"
#include "edac_module.h"
-#define CPC925_EDAC_REVISION " Ver: 1.0.0 " __DATE__
+#define CPC925_EDAC_REVISION " Ver: 1.0.0"
#define CPC925_EDAC_MOD_STR "cpc925_edac"
#define cpc925_printk(level, fmt, arg...) \
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index ec302d4..1af531a 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -24,7 +24,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define E752X_REVISION " Ver: 2.0.2 " __DATE__
+#define E752X_REVISION " Ver: 2.0.2"
#define EDAC_MOD_STR "e752x_edac"
static int report_non_memory_errors;
diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c
index 1731d72..6ffb6d2 100644
--- a/drivers/edac/e7xxx_edac.c
+++ b/drivers/edac/e7xxx_edac.c
@@ -29,7 +29,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define E7XXX_REVISION " Ver: 2.0.2 " __DATE__
+#define E7XXX_REVISION " Ver: 2.0.2"
#define EDAC_MOD_STR "e7xxx_edac"
#define e7xxx_printk(level, fmt, arg...) \
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index eefa350..55b8278 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -421,10 +421,6 @@
u32 ce_count; /* Total Correctable Errors for this MC */
unsigned long start_time; /* mci load start time (in jiffies) */
- /* this stuff is for safe removal of mc devices from global list while
- * NMI handlers may be traversing list
- */
- struct rcu_head rcu;
struct completion complete;
/* edac sysfs device control */
@@ -620,10 +616,6 @@
unsigned long start_time; /* edac_device load start time (jiffies) */
- /* these are for safe removal of mc devices from global list while
- * NMI handlers may be traversing list
- */
- struct rcu_head rcu;
struct completion removal_complete;
/* sysfs top name under 'edac' directory
@@ -722,10 +714,6 @@
unsigned long start_time; /* edac_pci load start time (jiffies) */
- /* these are for safe removal of devices from global list while
- * NMI handlers may be traversing list
- */
- struct rcu_head rcu;
struct completion complete;
/* sysfs top name under 'edac' directory
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index a7408cf..c3f6743 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -346,30 +346,18 @@
}
/*
- * complete_edac_device_list_del
- *
- * callback function when reference count is zero
- */
-static void complete_edac_device_list_del(struct rcu_head *head)
-{
- struct edac_device_ctl_info *edac_dev;
-
- edac_dev = container_of(head, struct edac_device_ctl_info, rcu);
- INIT_LIST_HEAD(&edac_dev->link);
-}
-
-/*
* del_edac_device_from_global_list
- *
- * remove the RCU, setup for a callback call,
- * then wait for the callback to occur
*/
static void del_edac_device_from_global_list(struct edac_device_ctl_info
*edac_device)
{
list_del_rcu(&edac_device->link);
- call_rcu(&edac_device->rcu, complete_edac_device_list_del);
- rcu_barrier();
+
+ /* these are for safe removal of devices from global list while
+ * NMI handlers may be traversing list
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&edac_device->link);
}
/*
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 1d80560..d69144a 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -447,20 +447,16 @@
return 1;
}
-static void complete_mc_list_del(struct rcu_head *head)
-{
- struct mem_ctl_info *mci;
-
- mci = container_of(head, struct mem_ctl_info, rcu);
- INIT_LIST_HEAD(&mci->link);
-}
-
static void del_mc_from_global_list(struct mem_ctl_info *mci)
{
atomic_dec(&edac_handlers);
list_del_rcu(&mci->link);
- call_rcu(&mci->rcu, complete_mc_list_del);
- rcu_barrier();
+
+ /* these are for safe removal of devices from global list while
+ * NMI handlers may be traversing list
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&mci->link);
}
/**
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index be4b075..5ddaa86 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -15,7 +15,7 @@
#include "edac_core.h"
#include "edac_module.h"
-#define EDAC_VERSION "Ver: 2.1.0 " __DATE__
+#define EDAC_VERSION "Ver: 2.1.0"
#ifdef CONFIG_EDAC_DEBUG
/* Values of 0 to 4 will generate output */
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c
index efb5d56..2b378207 100644
--- a/drivers/edac/edac_pci.c
+++ b/drivers/edac/edac_pci.c
@@ -164,19 +164,6 @@
}
/*
- * complete_edac_pci_list_del
- *
- * RCU completion callback to indicate item is deleted
- */
-static void complete_edac_pci_list_del(struct rcu_head *head)
-{
- struct edac_pci_ctl_info *pci;
-
- pci = container_of(head, struct edac_pci_ctl_info, rcu);
- INIT_LIST_HEAD(&pci->link);
-}
-
-/*
* del_edac_pci_from_global_list
*
* remove the PCI control struct from the global list
@@ -184,8 +171,12 @@
static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
{
list_del_rcu(&pci->link);
- call_rcu(&pci->rcu, complete_edac_pci_list_del);
- rcu_barrier();
+
+ /* these are for safe removal of devices from global list while
+ * NMI handlers may be traversing list
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&pci->link);
}
#if 0
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 87f427c..4dc3ac2 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -27,7 +27,7 @@
/*
* Alter this version for the I5000 module when modifications are made
*/
-#define I5000_REVISION " Ver: 2.0.12 " __DATE__
+#define I5000_REVISION " Ver: 2.0.12"
#define EDAC_MOD_STR "i5000_edac"
#define i5000_printk(level, fmt, arg...) \
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 80a465e..74d6ec34 100644
--- a/drivers/edac/i5400_edac.c
+++ b/drivers/edac/i5400_edac.c
@@ -33,7 +33,7 @@
/*
* Alter this version for the I5400 module when modifications are made
*/
-#define I5400_REVISION " Ver: 1.0.0 " __DATE__
+#define I5400_REVISION " Ver: 1.0.0"
#define EDAC_MOD_STR "i5400_edac"
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 363cc16..a76fe83 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -31,7 +31,7 @@
/*
* Alter this version for the I7300 module when modifications are made
*/
-#define I7300_REVISION " Ver: 1.0.0 " __DATE__
+#define I7300_REVISION " Ver: 1.0.0"
#define EDAC_MOD_STR "i7300_edac"
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 465cbc2..04f1e7c 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -59,7 +59,7 @@
/*
* Alter this version for the module when modifications are made
*/
-#define I7CORE_REVISION " Ver: 1.0.0 " __DATE__
+#define I7CORE_REVISION " Ver: 1.0.0"
#define EDAC_MOD_STR "i7core_edac"
/*
diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c
index b8a95cf..931a057 100644
--- a/drivers/edac/i82860_edac.c
+++ b/drivers/edac/i82860_edac.c
@@ -16,7 +16,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define I82860_REVISION " Ver: 2.0.2 " __DATE__
+#define I82860_REVISION " Ver: 2.0.2"
#define EDAC_MOD_STR "i82860_edac"
#define i82860_printk(level, fmt, arg...) \
diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c
index b2fd1e8..33864c6 100644
--- a/drivers/edac/i82875p_edac.c
+++ b/drivers/edac/i82875p_edac.c
@@ -20,7 +20,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define I82875P_REVISION " Ver: 2.0.2 " __DATE__
+#define I82875P_REVISION " Ver: 2.0.2"
#define EDAC_MOD_STR "i82875p_edac"
#define i82875p_printk(level, fmt, arg...) \
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index 92e65e7..a5da732 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -16,7 +16,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define I82975X_REVISION " Ver: 1.0.0 " __DATE__
+#define I82975X_REVISION " Ver: 1.0.0"
#define EDAC_MOD_STR "i82975x_edac"
#define i82975x_printk(level, fmt, arg...) \
diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h
index cb24df8..932016f 100644
--- a/drivers/edac/mpc85xx_edac.h
+++ b/drivers/edac/mpc85xx_edac.h
@@ -11,7 +11,7 @@
#ifndef _MPC85XX_EDAC_H_
#define _MPC85XX_EDAC_H_
-#define MPC85XX_REVISION " Ver: 2.0.0 " __DATE__
+#define MPC85XX_REVISION " Ver: 2.0.0"
#define EDAC_MOD_STR "MPC85xx_edac"
#define mpc85xx_printk(level, fmt, arg...) \
diff --git a/drivers/edac/mv64x60_edac.h b/drivers/edac/mv64x60_edac.h
index e042e2d..c7f209c 100644
--- a/drivers/edac/mv64x60_edac.h
+++ b/drivers/edac/mv64x60_edac.h
@@ -12,7 +12,7 @@
#ifndef _MV64X60_EDAC_H_
#define _MV64X60_EDAC_H_
-#define MV64x60_REVISION " Ver: 2.0.0 " __DATE__
+#define MV64x60_REVISION " Ver: 2.0.0"
#define EDAC_MOD_STR "MV64x60_edac"
#define mv64x60_printk(level, fmt, arg...) \
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index af8e7b1..0de7d87 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -113,7 +113,7 @@
#define EDAC_OPSTATE_UNKNOWN_STR "unknown"
#define PPC4XX_EDAC_MODULE_NAME "ppc4xx_edac"
-#define PPC4XX_EDAC_MODULE_REVISION "v1.0.0 " __DATE__
+#define PPC4XX_EDAC_MODULE_REVISION "v1.0.0"
#define PPC4XX_EDAC_MESSAGE_SIZE 256
diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c
index 6785137..b153674 100644
--- a/drivers/edac/r82600_edac.c
+++ b/drivers/edac/r82600_edac.c
@@ -22,7 +22,7 @@
#include <linux/edac.h>
#include "edac_core.h"
-#define R82600_REVISION " Ver: 2.0.2 " __DATE__
+#define R82600_REVISION " Ver: 2.0.2"
#define EDAC_MOD_STR "r82600_edac"
#define r82600_printk(level, fmt, arg...) \
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index d3b2953..d213646 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1,5 +1,5 @@
#
-# platform-neutral GPIO infrastructure and expanders
+# GPIO infrastructure and drivers
#
config ARCH_WANT_OPTIONAL_GPIOLIB
@@ -31,7 +31,7 @@
help
This enables GPIO support through the generic GPIO library.
You only need to enable this, if you also want to enable
- one or more of the GPIO expansion card drivers below.
+ one or more of the GPIO drivers below.
If unsure, say N.
@@ -63,21 +63,26 @@
Kernel drivers may also request that a particular GPIO be
exported to userspace; this can be useful when debugging.
-# put expanders in the right section, in alphabetical order
+# put drivers in the right section, in alphabetical order
config GPIO_MAX730X
tristate
-comment "Memory mapped GPIO expanders:"
+comment "Memory mapped GPIO drivers:"
+
+config GPIO_BASIC_MMIO_CORE
+ tristate
+ help
+ Provides core functionality for basic memory-mapped GPIO controllers.
config GPIO_BASIC_MMIO
tristate "Basic memory-mapped GPIO controllers support"
+ select GPIO_BASIC_MMIO_CORE
help
Say yes here to support basic memory-mapped GPIO controllers.
config GPIO_IT8761E
tristate "IT8761E GPIO support"
- depends on GPIOLIB
help
Say yes here to support GPIO functionality of IT8761E super I/O chip.
@@ -101,7 +106,7 @@
config GPIO_SCH
tristate "Intel SCH/TunnelCreek GPIO"
- depends on GPIOLIB && PCI && X86
+ depends on PCI && X86
select MFD_CORE
select LPC_SCH
help
@@ -121,7 +126,7 @@
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
- depends on GPIOLIB && MFD_SUPPORT && PCI
+ depends on MFD_SUPPORT && PCI
select MFD_CORE
select MFD_VX855
help
@@ -347,13 +352,13 @@
config GPIO_TIMBERDALE
bool "Support for timberdale GPIO IP"
- depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM
+ depends on MFD_TIMBERDALE && HAS_IOMEM
---help---
Add support for the GPIO IP in the timberdale FPGA.
config GPIO_RDC321X
tristate "RDC R-321x GPIO support"
- depends on PCI && GPIOLIB
+ depends on PCI
select MFD_SUPPORT
select MFD_CORE
select MFD_RDC321X
@@ -419,4 +424,11 @@
depends on AB8500_CORE && BROKEN
help
Select this to enable the AB8500 IC GPIO driver
+
+config GPIO_TPS65910
+ bool "TPS65910 GPIO"
+ depends on MFD_TPS65910
+ help
+ Select this option to enable GPIO driver for the TPS65910
+ chip family.
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index becef59..6a3387a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -1,8 +1,4 @@
-# generic gpio support: dedicated expander chips, etc
-#
-# NOTE: platform-specific GPIO drivers don't belong in the
-# drivers/gpio directory; put them with other platform setup
-# code, IRQ controllers, board init, etc.
+# generic gpio support: platform drivers, dedicated expander chips, etc
ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
@@ -10,6 +6,7 @@
obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o
obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o
+obj-$(CONFIG_GPIO_BASIC_MMIO_CORE) += basic_mmio_gpio.o
obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o
obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o
obj-$(CONFIG_GPIO_MAX730X) += max730x.o
@@ -43,3 +40,4 @@
obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
obj-$(CONFIG_AB8500_GPIO) += ab8500-gpio.o
+obj-$(CONFIG_GPIO_TPS65910) += tps65910-gpio.o
diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c
index 3addea6..8152e9f5 100644
--- a/drivers/gpio/basic_mmio_gpio.c
+++ b/drivers/gpio/basic_mmio_gpio.c
@@ -45,6 +45,7 @@
*/
#include <linux/init.h>
+#include <linux/err.h>
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -61,103 +62,72 @@
#include <linux/mod_devicetable.h>
#include <linux/basic_mmio_gpio.h>
-struct bgpio_chip {
- struct gpio_chip gc;
- void __iomem *reg_dat;
- void __iomem *reg_set;
- void __iomem *reg_clr;
-
- /* Number of bits (GPIOs): <register width> * 8. */
- int bits;
-
- /*
- * Some GPIO controllers work with the big-endian bits notation,
- * e.g. in a 8-bits register, GPIO7 is the least significant bit.
- */
- int big_endian_bits;
-
- /*
- * Used to lock bgpio_chip->data. Also, this is needed to keep
- * shadowed and real data registers writes together.
- */
- spinlock_t lock;
-
- /* Shadowed data register to clear/set bits safely. */
- unsigned long data;
-};
-
-static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc)
+static void bgpio_write8(void __iomem *reg, unsigned long data)
{
- return container_of(gc, struct bgpio_chip, gc);
+ writeb(data, reg);
}
-static unsigned long bgpio_in(struct bgpio_chip *bgc)
+static unsigned long bgpio_read8(void __iomem *reg)
{
- switch (bgc->bits) {
- case 8:
- return __raw_readb(bgc->reg_dat);
- case 16:
- return __raw_readw(bgc->reg_dat);
- case 32:
- return __raw_readl(bgc->reg_dat);
+ return readb(reg);
+}
+
+static void bgpio_write16(void __iomem *reg, unsigned long data)
+{
+ writew(data, reg);
+}
+
+static unsigned long bgpio_read16(void __iomem *reg)
+{
+ return readw(reg);
+}
+
+static void bgpio_write32(void __iomem *reg, unsigned long data)
+{
+ writel(data, reg);
+}
+
+static unsigned long bgpio_read32(void __iomem *reg)
+{
+ return readl(reg);
+}
+
#if BITS_PER_LONG >= 64
- case 64:
- return __raw_readq(bgc->reg_dat);
-#endif
- }
- return -EINVAL;
+static void bgpio_write64(void __iomem *reg, unsigned long data)
+{
+ writeq(data, reg);
}
-static void bgpio_out(struct bgpio_chip *bgc, void __iomem *reg,
- unsigned long data)
+static unsigned long bgpio_read64(void __iomem *reg)
{
- switch (bgc->bits) {
- case 8:
- __raw_writeb(data, reg);
- return;
- case 16:
- __raw_writew(data, reg);
- return;
- case 32:
- __raw_writel(data, reg);
- return;
-#if BITS_PER_LONG >= 64
- case 64:
- __raw_writeq(data, reg);
- return;
-#endif
- }
+ return readq(reg);
}
+#endif /* BITS_PER_LONG >= 64 */
static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin)
{
- if (bgc->big_endian_bits)
- return 1 << (bgc->bits - 1 - pin);
- else
- return 1 << pin;
+ return 1 << pin;
+}
+
+static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc,
+ unsigned int pin)
+{
+ return 1 << (bgc->bits - 1 - pin);
}
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- return bgpio_in(bgc) & bgpio_pin2mask(bgc, gpio);
+ return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
}
static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- unsigned long mask = bgpio_pin2mask(bgc, gpio);
+ unsigned long mask = bgc->pin2mask(bgc, gpio);
unsigned long flags;
- if (bgc->reg_set) {
- if (val)
- bgpio_out(bgc, bgc->reg_set, mask);
- else
- bgpio_out(bgc, bgc->reg_clr, mask);
- return;
- }
-
spin_lock_irqsave(&bgc->lock, flags);
if (val)
@@ -165,103 +135,382 @@
else
bgc->data &= ~mask;
- bgpio_out(bgc, bgc->reg_dat, bgc->data);
+ bgc->write_reg(bgc->reg_dat, bgc->data);
spin_unlock_irqrestore(&bgc->lock, flags);
}
+static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long mask = bgc->pin2mask(bgc, gpio);
+
+ if (val)
+ bgc->write_reg(bgc->reg_set, mask);
+ else
+ bgc->write_reg(bgc->reg_clr, mask);
+}
+
+static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long mask = bgc->pin2mask(bgc, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ if (val)
+ bgc->data |= mask;
+ else
+ bgc->data &= ~mask;
+
+ bgc->write_reg(bgc->reg_set, bgc->data);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+ return 0;
+}
+
+static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ gc->set(gc, gpio, val);
+
+ return 0;
+}
+
static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir &= ~bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
return 0;
}
static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
- bgpio_set(gc, gpio, val);
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
+
+ gc->set(gc, gpio, val);
+
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir |= bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
return 0;
}
-static int __devinit bgpio_probe(struct platform_device *pdev)
+static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio)
{
- const struct platform_device_id *platid = platform_get_device_id(pdev);
- struct device *dev = &pdev->dev;
- struct bgpio_pdata *pdata = dev_get_platdata(dev);
- struct bgpio_chip *bgc;
- struct resource *res_dat;
- struct resource *res_set;
- struct resource *res_clr;
- resource_size_t dat_sz;
- int bits;
- int ret;
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
- res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
- if (!res_dat)
- return -EINVAL;
+ spin_lock_irqsave(&bgc->lock, flags);
- dat_sz = resource_size(res_dat);
- if (!is_power_of_2(dat_sz))
- return -EINVAL;
+ bgc->dir |= bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
- bits = dat_sz * 8;
- if (bits > BITS_PER_LONG)
- return -EINVAL;
+ spin_unlock_irqrestore(&bgc->lock, flags);
- bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL);
- if (!bgc)
- return -ENOMEM;
+ return 0;
+}
- bgc->reg_dat = devm_ioremap(dev, res_dat->start, dat_sz);
- if (!bgc->reg_dat)
- return -ENOMEM;
+static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long flags;
- res_set = platform_get_resource_byname(pdev, IORESOURCE_MEM, "set");
- res_clr = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clr");
- if (res_set && res_clr) {
- if (resource_size(res_set) != resource_size(res_clr) ||
- resource_size(res_set) != dat_sz)
- return -EINVAL;
+ gc->set(gc, gpio, val);
- bgc->reg_set = devm_ioremap(dev, res_set->start, dat_sz);
- bgc->reg_clr = devm_ioremap(dev, res_clr->start, dat_sz);
- if (!bgc->reg_set || !bgc->reg_clr)
- return -ENOMEM;
- } else if (res_set || res_clr) {
+ spin_lock_irqsave(&bgc->lock, flags);
+
+ bgc->dir &= ~bgc->pin2mask(bgc, gpio);
+ bgc->write_reg(bgc->reg_dir, bgc->dir);
+
+ spin_unlock_irqrestore(&bgc->lock, flags);
+
+ return 0;
+}
+
+static int bgpio_setup_accessors(struct device *dev,
+ struct bgpio_chip *bgc,
+ bool be)
+{
+
+ switch (bgc->bits) {
+ case 8:
+ bgc->read_reg = bgpio_read8;
+ bgc->write_reg = bgpio_write8;
+ break;
+ case 16:
+ bgc->read_reg = bgpio_read16;
+ bgc->write_reg = bgpio_write16;
+ break;
+ case 32:
+ bgc->read_reg = bgpio_read32;
+ bgc->write_reg = bgpio_write32;
+ break;
+#if BITS_PER_LONG >= 64
+ case 64:
+ bgc->read_reg = bgpio_read64;
+ bgc->write_reg = bgpio_write64;
+ break;
+#endif /* BITS_PER_LONG >= 64 */
+ default:
+ dev_err(dev, "unsupported data width %u bits\n", bgc->bits);
return -EINVAL;
}
- spin_lock_init(&bgc->lock);
+ bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask;
- bgc->bits = bits;
- bgc->big_endian_bits = !strcmp(platid->name, "basic-mmio-gpio-be");
- bgc->data = bgpio_in(bgc);
+ return 0;
+}
- bgc->gc.ngpio = bits;
- bgc->gc.direction_input = bgpio_dir_in;
- bgc->gc.direction_output = bgpio_dir_out;
+/*
+ * Create the device and allocate the resources. For setting GPIO's there are
+ * three supported configurations:
+ *
+ * - single input/output register resource (named "dat").
+ * - set/clear pair (named "set" and "clr").
+ * - single output register resource and single input resource ("set" and
+ * dat").
+ *
+ * For the single output register, this drives a 1 by setting a bit and a zero
+ * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit
+ * in the set register and clears it by setting a bit in the clear register.
+ * The configuration is detected by which resources are present.
+ *
+ * For setting the GPIO direction, there are three supported configurations:
+ *
+ * - simple bidirection GPIO that requires no configuration.
+ * - an output direction register (named "dirout") where a 1 bit
+ * indicates the GPIO is an output.
+ * - an input direction register (named "dirin") where a 1 bit indicates
+ * the GPIO is an input.
+ */
+static int bgpio_setup_io(struct bgpio_chip *bgc,
+ void __iomem *dat,
+ void __iomem *set,
+ void __iomem *clr)
+{
+
+ bgc->reg_dat = dat;
+ if (!bgc->reg_dat)
+ return -EINVAL;
+
+ if (set && clr) {
+ bgc->reg_set = set;
+ bgc->reg_clr = clr;
+ bgc->gc.set = bgpio_set_with_clear;
+ } else if (set && !clr) {
+ bgc->reg_set = set;
+ bgc->gc.set = bgpio_set_set;
+ } else {
+ bgc->gc.set = bgpio_set;
+ }
+
bgc->gc.get = bgpio_get;
- bgc->gc.set = bgpio_set;
+
+ return 0;
+}
+
+static int bgpio_setup_direction(struct bgpio_chip *bgc,
+ void __iomem *dirout,
+ void __iomem *dirin)
+{
+ if (dirout && dirin) {
+ return -EINVAL;
+ } else if (dirout) {
+ bgc->reg_dir = dirout;
+ bgc->gc.direction_output = bgpio_dir_out;
+ bgc->gc.direction_input = bgpio_dir_in;
+ } else if (dirin) {
+ bgc->reg_dir = dirin;
+ bgc->gc.direction_output = bgpio_dir_out_inv;
+ bgc->gc.direction_input = bgpio_dir_in_inv;
+ } else {
+ bgc->gc.direction_output = bgpio_simple_dir_out;
+ bgc->gc.direction_input = bgpio_simple_dir_in;
+ }
+
+ return 0;
+}
+
+int __devexit bgpio_remove(struct bgpio_chip *bgc)
+{
+ int err = gpiochip_remove(&bgc->gc);
+
+ kfree(bgc);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(bgpio_remove);
+
+int __devinit bgpio_init(struct bgpio_chip *bgc,
+ struct device *dev,
+ unsigned long sz,
+ void __iomem *dat,
+ void __iomem *set,
+ void __iomem *clr,
+ void __iomem *dirout,
+ void __iomem *dirin,
+ bool big_endian)
+{
+ int ret;
+
+ if (!is_power_of_2(sz))
+ return -EINVAL;
+
+ bgc->bits = sz * 8;
+ if (bgc->bits > BITS_PER_LONG)
+ return -EINVAL;
+
+ spin_lock_init(&bgc->lock);
bgc->gc.dev = dev;
bgc->gc.label = dev_name(dev);
+ bgc->gc.base = -1;
+ bgc->gc.ngpio = bgc->bits;
- if (pdata)
- bgc->gc.base = pdata->base;
- else
- bgc->gc.base = -1;
-
- dev_set_drvdata(dev, bgc);
-
- ret = gpiochip_add(&bgc->gc);
+ ret = bgpio_setup_io(bgc, dat, set, clr);
if (ret)
- dev_err(dev, "gpiochip_add() failed: %d\n", ret);
+ return ret;
+
+ ret = bgpio_setup_accessors(dev, bgc, big_endian);
+ if (ret)
+ return ret;
+
+ ret = bgpio_setup_direction(bgc, dirout, dirin);
+ if (ret)
+ return ret;
+
+ bgc->data = bgc->read_reg(bgc->reg_dat);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(bgpio_init);
+
+#ifdef CONFIG_GPIO_BASIC_MMIO
+
+static void __iomem *bgpio_map(struct platform_device *pdev,
+ const char *name,
+ resource_size_t sane_sz,
+ int *err)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ resource_size_t start;
+ resource_size_t sz;
+ void __iomem *ret;
+
+ *err = 0;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!r)
+ return NULL;
+
+ sz = resource_size(r);
+ if (sz != sane_sz) {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ start = r->start;
+ if (!devm_request_mem_region(dev, start, sz, r->name)) {
+ *err = -EBUSY;
+ return NULL;
+ }
+
+ ret = devm_ioremap(dev, start, sz);
+ if (!ret) {
+ *err = -ENOMEM;
+ return NULL;
+ }
return ret;
}
-static int __devexit bgpio_remove(struct platform_device *pdev)
+static int __devinit bgpio_pdev_probe(struct platform_device *pdev)
{
- struct bgpio_chip *bgc = dev_get_drvdata(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ void __iomem *dat;
+ void __iomem *set;
+ void __iomem *clr;
+ void __iomem *dirout;
+ void __iomem *dirin;
+ unsigned long sz;
+ bool be;
+ int err;
+ struct bgpio_chip *bgc;
+ struct bgpio_pdata *pdata = dev_get_platdata(dev);
- return gpiochip_remove(&bgc->gc);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
+ if (!r)
+ return -EINVAL;
+
+ sz = resource_size(r);
+
+ dat = bgpio_map(pdev, "dat", sz, &err);
+ if (!dat)
+ return err ? err : -EINVAL;
+
+ set = bgpio_map(pdev, "set", sz, &err);
+ if (err)
+ return err;
+
+ clr = bgpio_map(pdev, "clr", sz, &err);
+ if (err)
+ return err;
+
+ dirout = bgpio_map(pdev, "dirout", sz, &err);
+ if (err)
+ return err;
+
+ dirin = bgpio_map(pdev, "dirin", sz, &err);
+ if (err)
+ return err;
+
+ be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be");
+
+ bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
+ if (!bgc)
+ return -ENOMEM;
+
+ err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, be);
+ if (err)
+ return err;
+
+ if (pdata) {
+ bgc->gc.base = pdata->base;
+ if (pdata->ngpio > 0)
+ bgc->gc.ngpio = pdata->ngpio;
+ }
+
+ platform_set_drvdata(pdev, bgc);
+
+ return gpiochip_add(&bgc->gc);
+}
+
+static int __devexit bgpio_pdev_remove(struct platform_device *pdev)
+{
+ struct bgpio_chip *bgc = platform_get_drvdata(pdev);
+
+ return bgpio_remove(bgc);
}
static const struct platform_device_id bgpio_id_table[] = {
@@ -276,21 +525,23 @@
.name = "basic-mmio-gpio",
},
.id_table = bgpio_id_table,
- .probe = bgpio_probe,
- .remove = __devexit_p(bgpio_remove),
+ .probe = bgpio_pdev_probe,
+ .remove = __devexit_p(bgpio_pdev_remove),
};
-static int __init bgpio_init(void)
+static int __init bgpio_platform_init(void)
{
return platform_driver_register(&bgpio_driver);
}
-module_init(bgpio_init);
+module_init(bgpio_platform_init);
-static void __exit bgpio_exit(void)
+static void __exit bgpio_platform_exit(void)
{
platform_driver_unregister(&bgpio_driver);
}
-module_exit(bgpio_exit);
+module_exit(bgpio_platform_exit);
+
+#endif /* CONFIG_GPIO_BASIC_MMIO */
MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers");
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 36a2974..137a8ca 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -12,6 +12,8 @@
#include <linux/idr.h>
#include <linux/slab.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/gpio.h>
/* Optional implementation infrastructure for GPIO interfaces.
*
@@ -1165,6 +1167,7 @@
return chip;
}
+EXPORT_SYMBOL_GPL(gpiochip_find);
/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
@@ -1404,6 +1407,8 @@
status = chip->direction_input(chip, gpio);
if (status == 0)
clear_bit(FLAG_IS_OUT, &desc->flags);
+
+ trace_gpio_direction(chip->base + gpio, 1, status);
lose:
return status;
fail:
@@ -1457,6 +1462,8 @@
status = chip->direction_output(chip, gpio, value);
if (status == 0)
set_bit(FLAG_IS_OUT, &desc->flags);
+ trace_gpio_value(chip->base + gpio, 0, value);
+ trace_gpio_direction(chip->base + gpio, 0, status);
lose:
return status;
fail:
@@ -1546,10 +1553,13 @@
int __gpio_get_value(unsigned gpio)
{
struct gpio_chip *chip;
+ int value;
chip = gpio_to_chip(gpio);
WARN_ON(chip->can_sleep);
- return chip->get ? chip->get(chip, gpio - chip->base) : 0;
+ value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
+ trace_gpio_value(gpio, 1, value);
+ return value;
}
EXPORT_SYMBOL_GPL(__gpio_get_value);
@@ -1568,6 +1578,7 @@
chip = gpio_to_chip(gpio);
WARN_ON(chip->can_sleep);
+ trace_gpio_value(gpio, 0, value);
chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(__gpio_set_value);
@@ -1618,10 +1629,13 @@
int gpio_get_value_cansleep(unsigned gpio)
{
struct gpio_chip *chip;
+ int value;
might_sleep_if(extra_checks);
chip = gpio_to_chip(gpio);
- return chip->get ? chip->get(chip, gpio - chip->base) : 0;
+ value = chip->get ? chip->get(chip, gpio - chip->base) : 0;
+ trace_gpio_value(gpio, 1, value);
+ return value;
}
EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
@@ -1631,6 +1645,7 @@
might_sleep_if(extra_checks);
chip = gpio_to_chip(gpio);
+ trace_gpio_value(gpio, 0, value);
chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c
index 2514fb0..813ac07 100644
--- a/drivers/gpio/janz-ttl.c
+++ b/drivers/gpio/janz-ttl.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/slab.h>
@@ -150,7 +149,7 @@
struct resource *res;
int ret;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(dev, "no platform data\n");
ret = -ENXIO;
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index 7630ab7b..78a8439 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -397,7 +397,7 @@
irq_set_chip_data(irq, chip);
irq_set_chip_and_handler(irq, &pca953x_irq_chip,
- handle_edge_irq);
+ handle_simple_irq);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c
index a9bda88..2762698 100644
--- a/drivers/gpio/rdc321x-gpio.c
+++ b/drivers/gpio/rdc321x-gpio.c
@@ -27,7 +27,6 @@
#include <linux/pci.h>
#include <linux/gpio.h>
#include <linux/mfd/rdc321x.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
struct rdc321x_gpio {
@@ -136,7 +135,7 @@
struct rdc321x_gpio *rdc321x_gpio_dev;
struct rdc321x_gpio_pdata *pdata;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data supplied\n");
return -ENODEV;
diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c
index edbe1ea..0265872 100644
--- a/drivers/gpio/timbgpio.c
+++ b/drivers/gpio/timbgpio.c
@@ -23,7 +23,6 @@
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/timb_gpio.h>
@@ -229,7 +228,7 @@
struct gpio_chip *gc;
struct timbgpio *tgpio;
struct resource *iomem;
- struct timbgpio_platform_data *pdata = mfd_get_data(pdev);
+ struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
int irq = platform_get_irq(pdev, 0);
if (!pdata || pdata->nr_pins > 32) {
@@ -320,13 +319,14 @@
static int __devexit timbgpio_remove(struct platform_device *pdev)
{
int err;
+ struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
struct timbgpio *tgpio = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
if (irq >= 0 && tgpio->irq_base > 0) {
int i;
- for (i = 0; i < tgpio->gpio.ngpio; i++) {
+ for (i = 0; i < pdata->nr_pins; i++) {
irq_set_chip(tgpio->irq_base + i, NULL);
irq_set_chip_data(tgpio->irq_base + i, NULL);
}
diff --git a/drivers/gpio/tps65910-gpio.c b/drivers/gpio/tps65910-gpio.c
new file mode 100644
index 0000000..8d1ddfd
--- /dev/null
+++ b/drivers/gpio/tps65910-gpio.c
@@ -0,0 +1,100 @@
+/*
+ * tps65910-gpio.c -- TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/tps65910.h>
+
+static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+ uint8_t val;
+
+ tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val);
+
+ if (val & GPIO_STS_MASK)
+ return 1;
+
+ return 0;
+}
+
+static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+
+ if (value)
+ tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_SET_MASK);
+ else
+ tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_SET_MASK);
+}
+
+static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+
+ /* Set the initial value */
+ tps65910_gpio_set(gc, 0, value);
+
+ return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_CFG_MASK);
+}
+
+static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
+
+ return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset,
+ GPIO_CFG_MASK);
+}
+
+void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base)
+{
+ int ret;
+
+ if (!gpio_base)
+ return;
+
+ tps65910->gpio.owner = THIS_MODULE;
+ tps65910->gpio.label = tps65910->i2c_client->name;
+ tps65910->gpio.dev = tps65910->dev;
+ tps65910->gpio.base = gpio_base;
+
+ switch(tps65910_chip_id(tps65910)) {
+ case TPS65910:
+ tps65910->gpio.ngpio = 6;
+ case TPS65911:
+ tps65910->gpio.ngpio = 9;
+ default:
+ return;
+ }
+ tps65910->gpio.can_sleep = 1;
+
+ tps65910->gpio.direction_input = tps65910_gpio_input;
+ tps65910->gpio.direction_output = tps65910_gpio_output;
+ tps65910->gpio.set = tps65910_gpio_set;
+ tps65910->gpio.get = tps65910_gpio_get;
+
+ ret = gpiochip_add(&tps65910->gpio);
+
+ if (ret)
+ dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret);
+}
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 9577c43..de3d246 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -350,6 +350,7 @@
static int create_name_attr(struct platform_data *pdata, struct device *dev)
{
+ sysfs_attr_init(&pdata->name_attr.attr);
pdata->name_attr.attr.name = "name";
pdata->name_attr.attr.mode = S_IRUGO;
pdata->name_attr.show = show_name;
@@ -372,6 +373,7 @@
for (i = 0; i < MAX_ATTRS; i++) {
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
attr_no);
+ sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
@@ -422,7 +424,7 @@
}
}
-static int chk_ucode_version(struct platform_device *pdev)
+static int __devinit chk_ucode_version(struct platform_device *pdev)
{
struct cpuinfo_x86 *c = &cpu_data(pdev->id);
int err;
@@ -509,8 +511,8 @@
/*
* Provide a single set of attributes for all HT siblings of a core
* to avoid duplicate sensors (the processor ID and core ID of all
- * HT siblings of a core is the same).
- * Skip if a HT sibling of this core is already online.
+ * HT siblings of a core are the same).
+ * Skip if a HT sibling of this core is already registered.
* This is not an error.
*/
if (pdata->core_data[attr_no] != NULL)
@@ -770,10 +772,10 @@
coretemp_remove_core(pdata, &pdev->dev, indx);
/*
- * If a core is taken offline, but a HT sibling of the same core is
- * still online, register the alternate sibling. This ensures that
- * exactly one set of attributes is provided as long as at least one
- * HT sibling of a core is online.
+ * If a HT sibling of a core is taken offline, but another HT sibling
+ * of the same core is still online, register the alternate sibling.
+ * This ensures that exactly one set of attributes is provided as long
+ * as at least one HT sibling of a core is online.
*/
for_each_sibling(i, cpu) {
if (i != cpu) {
diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus_core.c
index 98799ba..354770e 100644
--- a/drivers/hwmon/pmbus_core.c
+++ b/drivers/hwmon/pmbus_core.c
@@ -707,6 +707,7 @@
struct sensor_device_attribute *a \
= &data->_type##s[data->num_##_type##s].attribute; \
BUG_ON(data->num_attributes >= data->max_attributes); \
+ sysfs_attr_init(&a->dev_attr.attr); \
a->dev_attr.attr.name = _name; \
a->dev_attr.attr.mode = _mode; \
a->dev_attr.show = _show; \
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index fee1a26..1b46a9d 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -49,7 +49,6 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
@@ -306,7 +305,7 @@
return -EIO;
}
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (pdata) {
i2c->regstep = pdata->regstep;
i2c->clock_khz = pdata->clock_khz;
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index e9d5ff4..4bb68f3 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -34,7 +34,6 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
@@ -705,7 +704,7 @@
if (irq < 0)
goto resource_missing;
- pdata = mfd_get_data(pdev);
+ pdata = (struct xiic_i2c_platform_data *) pdev->dev.platform_data;
if (!pdata)
return -EINVAL;
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index 6e35ecc..0f9a84c 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -2,6 +2,7 @@
tristate "InfiniBand support"
depends on PCI || BROKEN
depends on HAS_IOMEM
+ depends on NET
---help---
Core support for InfiniBand (IB). Make sure to also select
any protocols you wish to use as well as drivers for your
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
index cb1ab3e..c8bbaef 100644
--- a/drivers/infiniband/core/Makefile
+++ b/drivers/infiniband/core/Makefile
@@ -8,7 +8,7 @@
$(user_access-y)
ib_core-y := packer.o ud_header.o verbs.o sysfs.o \
- device.o fmr_pool.o cache.o
+ device.o fmr_pool.o cache.o netlink.o
ib_core-$(CONFIG_INFINIBAND_USER_MEM) += umem.o
ib_mad-y := mad.o smi.o agent.o mad_rmpp.o
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index f804e28..f62f52f 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -3639,8 +3639,16 @@
.release = cm_release_port_obj
};
+static char *cm_devnode(struct device *dev, mode_t *mode)
+{
+ *mode = 0666;
+ return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev));
+}
+
struct class cm_class = {
+ .owner = THIS_MODULE,
.name = "infiniband_cm",
+ .devnode = cm_devnode,
};
EXPORT_SYMBOL(cm_class);
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 99dde87..b6a33b3 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -47,6 +47,7 @@
#include <rdma/rdma_cm.h>
#include <rdma/rdma_cm_ib.h>
+#include <rdma/rdma_netlink.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_cm.h>
#include <rdma/ib_sa.h>
@@ -89,20 +90,6 @@
struct list_head id_list;
};
-enum cma_state {
- CMA_IDLE,
- CMA_ADDR_QUERY,
- CMA_ADDR_RESOLVED,
- CMA_ROUTE_QUERY,
- CMA_ROUTE_RESOLVED,
- CMA_CONNECT,
- CMA_DISCONNECT,
- CMA_ADDR_BOUND,
- CMA_LISTEN,
- CMA_DEVICE_REMOVAL,
- CMA_DESTROYING
-};
-
struct rdma_bind_list {
struct idr *ps;
struct hlist_head owners;
@@ -126,7 +113,7 @@
struct list_head mc_list;
int internal_id;
- enum cma_state state;
+ enum rdma_cm_state state;
spinlock_t lock;
struct mutex qp_mutex;
@@ -146,6 +133,7 @@
u32 seq_num;
u32 qkey;
u32 qp_num;
+ pid_t owner;
u8 srq;
u8 tos;
u8 reuseaddr;
@@ -165,8 +153,8 @@
struct cma_work {
struct work_struct work;
struct rdma_id_private *id;
- enum cma_state old_state;
- enum cma_state new_state;
+ enum rdma_cm_state old_state;
+ enum rdma_cm_state new_state;
struct rdma_cm_event event;
};
@@ -217,7 +205,7 @@
#define CMA_VERSION 0x00
#define SDP_MAJ_VERSION 0x2
-static int cma_comp(struct rdma_id_private *id_priv, enum cma_state comp)
+static int cma_comp(struct rdma_id_private *id_priv, enum rdma_cm_state comp)
{
unsigned long flags;
int ret;
@@ -229,7 +217,7 @@
}
static int cma_comp_exch(struct rdma_id_private *id_priv,
- enum cma_state comp, enum cma_state exch)
+ enum rdma_cm_state comp, enum rdma_cm_state exch)
{
unsigned long flags;
int ret;
@@ -241,11 +229,11 @@
return ret;
}
-static enum cma_state cma_exch(struct rdma_id_private *id_priv,
- enum cma_state exch)
+static enum rdma_cm_state cma_exch(struct rdma_id_private *id_priv,
+ enum rdma_cm_state exch)
{
unsigned long flags;
- enum cma_state old;
+ enum rdma_cm_state old;
spin_lock_irqsave(&id_priv->lock, flags);
old = id_priv->state;
@@ -279,11 +267,6 @@
hh->ip_version = (ip_ver << 4) | (hh->ip_version & 0xF);
}
-static inline int cma_is_ud_ps(enum rdma_port_space ps)
-{
- return (ps == RDMA_PS_UDP || ps == RDMA_PS_IPOIB);
-}
-
static void cma_attach_to_dev(struct rdma_id_private *id_priv,
struct cma_device *cma_dev)
{
@@ -413,7 +396,7 @@
}
static int cma_disable_callback(struct rdma_id_private *id_priv,
- enum cma_state state)
+ enum rdma_cm_state state)
{
mutex_lock(&id_priv->handler_mutex);
if (id_priv->state != state) {
@@ -429,7 +412,8 @@
}
struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler,
- void *context, enum rdma_port_space ps)
+ void *context, enum rdma_port_space ps,
+ enum ib_qp_type qp_type)
{
struct rdma_id_private *id_priv;
@@ -437,10 +421,12 @@
if (!id_priv)
return ERR_PTR(-ENOMEM);
- id_priv->state = CMA_IDLE;
+ id_priv->owner = task_pid_nr(current);
+ id_priv->state = RDMA_CM_IDLE;
id_priv->id.context = context;
id_priv->id.event_handler = event_handler;
id_priv->id.ps = ps;
+ id_priv->id.qp_type = qp_type;
spin_lock_init(&id_priv->lock);
mutex_init(&id_priv->qp_mutex);
init_completion(&id_priv->comp);
@@ -508,7 +494,7 @@
if (IS_ERR(qp))
return PTR_ERR(qp);
- if (cma_is_ud_ps(id_priv->id.ps))
+ if (id->qp_type == IB_QPT_UD)
ret = cma_init_ud_qp(id_priv, qp);
else
ret = cma_init_conn_qp(id_priv, qp);
@@ -636,7 +622,7 @@
qp_attr->port_num = id_priv->id.port_num;
*qp_attr_mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_PORT;
- if (cma_is_ud_ps(id_priv->id.ps)) {
+ if (id_priv->id.qp_type == IB_QPT_UD) {
ret = cma_set_qkey(id_priv);
if (ret)
return ret;
@@ -659,7 +645,7 @@
id_priv = container_of(id, struct rdma_id_private, id);
switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (!id_priv->cm_id.ib || cma_is_ud_ps(id_priv->id.ps))
+ if (!id_priv->cm_id.ib || (id_priv->id.qp_type == IB_QPT_UD))
ret = cma_ib_init_qp_attr(id_priv, qp_attr, qp_attr_mask);
else
ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, qp_attr,
@@ -858,16 +844,16 @@
}
static void cma_cancel_operation(struct rdma_id_private *id_priv,
- enum cma_state state)
+ enum rdma_cm_state state)
{
switch (state) {
- case CMA_ADDR_QUERY:
+ case RDMA_CM_ADDR_QUERY:
rdma_addr_cancel(&id_priv->id.route.addr.dev_addr);
break;
- case CMA_ROUTE_QUERY:
+ case RDMA_CM_ROUTE_QUERY:
cma_cancel_route(id_priv);
break;
- case CMA_LISTEN:
+ case RDMA_CM_LISTEN:
if (cma_any_addr((struct sockaddr *) &id_priv->id.route.addr.src_addr)
&& !id_priv->cma_dev)
cma_cancel_listens(id_priv);
@@ -918,10 +904,10 @@
void rdma_destroy_id(struct rdma_cm_id *id)
{
struct rdma_id_private *id_priv;
- enum cma_state state;
+ enum rdma_cm_state state;
id_priv = container_of(id, struct rdma_id_private, id);
- state = cma_exch(id_priv, CMA_DESTROYING);
+ state = cma_exch(id_priv, RDMA_CM_DESTROYING);
cma_cancel_operation(id_priv, state);
/*
@@ -1015,9 +1001,9 @@
int ret = 0;
if ((ib_event->event != IB_CM_TIMEWAIT_EXIT &&
- cma_disable_callback(id_priv, CMA_CONNECT)) ||
+ cma_disable_callback(id_priv, RDMA_CM_CONNECT)) ||
(ib_event->event == IB_CM_TIMEWAIT_EXIT &&
- cma_disable_callback(id_priv, CMA_DISCONNECT)))
+ cma_disable_callback(id_priv, RDMA_CM_DISCONNECT)))
return 0;
memset(&event, 0, sizeof event);
@@ -1048,7 +1034,8 @@
event.status = -ETIMEDOUT; /* fall through */
case IB_CM_DREQ_RECEIVED:
case IB_CM_DREP_RECEIVED:
- if (!cma_comp_exch(id_priv, CMA_CONNECT, CMA_DISCONNECT))
+ if (!cma_comp_exch(id_priv, RDMA_CM_CONNECT,
+ RDMA_CM_DISCONNECT))
goto out;
event.event = RDMA_CM_EVENT_DISCONNECTED;
break;
@@ -1075,7 +1062,7 @@
if (ret) {
/* Destroy the CM ID by returning a non-zero value. */
id_priv->cm_id.ib = NULL;
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
mutex_unlock(&id_priv->handler_mutex);
rdma_destroy_id(&id_priv->id);
return ret;
@@ -1101,7 +1088,7 @@
goto err;
id = rdma_create_id(listen_id->event_handler, listen_id->context,
- listen_id->ps);
+ listen_id->ps, ib_event->param.req_rcvd.qp_type);
if (IS_ERR(id))
goto err;
@@ -1132,7 +1119,7 @@
rdma_addr_set_dgid(&rt->addr.dev_addr, &rt->path_rec[0].dgid);
id_priv = container_of(id, struct rdma_id_private, id);
- id_priv->state = CMA_CONNECT;
+ id_priv->state = RDMA_CM_CONNECT;
return id_priv;
destroy_id:
@@ -1152,7 +1139,7 @@
int ret;
id = rdma_create_id(listen_id->event_handler, listen_id->context,
- listen_id->ps);
+ listen_id->ps, IB_QPT_UD);
if (IS_ERR(id))
return NULL;
@@ -1172,7 +1159,7 @@
}
id_priv = container_of(id, struct rdma_id_private, id);
- id_priv->state = CMA_CONNECT;
+ id_priv->state = RDMA_CM_CONNECT;
return id_priv;
err:
rdma_destroy_id(id);
@@ -1201,13 +1188,13 @@
int offset, ret;
listen_id = cm_id->context;
- if (cma_disable_callback(listen_id, CMA_LISTEN))
+ if (cma_disable_callback(listen_id, RDMA_CM_LISTEN))
return -ECONNABORTED;
memset(&event, 0, sizeof event);
offset = cma_user_data_offset(listen_id->id.ps);
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
- if (cma_is_ud_ps(listen_id->id.ps)) {
+ if (listen_id->id.qp_type == IB_QPT_UD) {
conn_id = cma_new_udp_id(&listen_id->id, ib_event);
event.param.ud.private_data = ib_event->private_data + offset;
event.param.ud.private_data_len =
@@ -1243,8 +1230,7 @@
* while we're accessing the cm_id.
*/
mutex_lock(&lock);
- if (cma_comp(conn_id, CMA_CONNECT) &&
- !cma_is_ud_ps(conn_id->id.ps))
+ if (cma_comp(conn_id, RDMA_CM_CONNECT) && (conn_id->id.qp_type != IB_QPT_UD))
ib_send_cm_mra(cm_id, CMA_CM_MRA_SETTING, NULL, 0);
mutex_unlock(&lock);
mutex_unlock(&conn_id->handler_mutex);
@@ -1257,7 +1243,7 @@
conn_id->cm_id.ib = NULL;
release_conn_id:
- cma_exch(conn_id, CMA_DESTROYING);
+ cma_exch(conn_id, RDMA_CM_DESTROYING);
mutex_unlock(&conn_id->handler_mutex);
rdma_destroy_id(&conn_id->id);
@@ -1328,7 +1314,7 @@
struct sockaddr_in *sin;
int ret = 0;
- if (cma_disable_callback(id_priv, CMA_CONNECT))
+ if (cma_disable_callback(id_priv, RDMA_CM_CONNECT))
return 0;
memset(&event, 0, sizeof event);
@@ -1371,7 +1357,7 @@
if (ret) {
/* Destroy the CM ID by returning a non-zero value. */
id_priv->cm_id.iw = NULL;
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
mutex_unlock(&id_priv->handler_mutex);
rdma_destroy_id(&id_priv->id);
return ret;
@@ -1393,20 +1379,20 @@
struct ib_device_attr attr;
listen_id = cm_id->context;
- if (cma_disable_callback(listen_id, CMA_LISTEN))
+ if (cma_disable_callback(listen_id, RDMA_CM_LISTEN))
return -ECONNABORTED;
/* Create a new RDMA id for the new IW CM ID */
new_cm_id = rdma_create_id(listen_id->id.event_handler,
listen_id->id.context,
- RDMA_PS_TCP);
+ RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(new_cm_id)) {
ret = -ENOMEM;
goto out;
}
conn_id = container_of(new_cm_id, struct rdma_id_private, id);
mutex_lock_nested(&conn_id->handler_mutex, SINGLE_DEPTH_NESTING);
- conn_id->state = CMA_CONNECT;
+ conn_id->state = RDMA_CM_CONNECT;
dev = ip_dev_find(&init_net, iw_event->local_addr.sin_addr.s_addr);
if (!dev) {
@@ -1461,7 +1447,7 @@
if (ret) {
/* User wants to destroy the CM ID */
conn_id->cm_id.iw = NULL;
- cma_exch(conn_id, CMA_DESTROYING);
+ cma_exch(conn_id, RDMA_CM_DESTROYING);
mutex_unlock(&conn_id->handler_mutex);
cma_deref_id(conn_id);
rdma_destroy_id(&conn_id->id);
@@ -1548,13 +1534,14 @@
struct rdma_cm_id *id;
int ret;
- id = rdma_create_id(cma_listen_handler, id_priv, id_priv->id.ps);
+ id = rdma_create_id(cma_listen_handler, id_priv, id_priv->id.ps,
+ id_priv->id.qp_type);
if (IS_ERR(id))
return;
dev_id_priv = container_of(id, struct rdma_id_private, id);
- dev_id_priv->state = CMA_ADDR_BOUND;
+ dev_id_priv->state = RDMA_CM_ADDR_BOUND;
memcpy(&id->route.addr.src_addr, &id_priv->id.route.addr.src_addr,
ip_addr_size((struct sockaddr *) &id_priv->id.route.addr.src_addr));
@@ -1601,8 +1588,8 @@
route->num_paths = 1;
*route->path_rec = *path_rec;
} else {
- work->old_state = CMA_ROUTE_QUERY;
- work->new_state = CMA_ADDR_RESOLVED;
+ work->old_state = RDMA_CM_ROUTE_QUERY;
+ work->new_state = RDMA_CM_ADDR_RESOLVED;
work->event.event = RDMA_CM_EVENT_ROUTE_ERROR;
work->event.status = status;
}
@@ -1660,7 +1647,7 @@
goto out;
if (id_priv->id.event_handler(&id_priv->id, &work->event)) {
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
destroy = 1;
}
out:
@@ -1678,12 +1665,12 @@
int destroy = 0;
mutex_lock(&id_priv->handler_mutex);
- if (id_priv->state == CMA_DESTROYING ||
- id_priv->state == CMA_DEVICE_REMOVAL)
+ if (id_priv->state == RDMA_CM_DESTROYING ||
+ id_priv->state == RDMA_CM_DEVICE_REMOVAL)
goto out;
if (id_priv->id.event_handler(&id_priv->id, &work->event)) {
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
destroy = 1;
}
@@ -1707,8 +1694,8 @@
work->id = id_priv;
INIT_WORK(&work->work, cma_work_handler);
- work->old_state = CMA_ROUTE_QUERY;
- work->new_state = CMA_ROUTE_RESOLVED;
+ work->old_state = RDMA_CM_ROUTE_QUERY;
+ work->new_state = RDMA_CM_ROUTE_RESOLVED;
work->event.event = RDMA_CM_EVENT_ROUTE_RESOLVED;
route->path_rec = kmalloc(sizeof *route->path_rec, GFP_KERNEL);
@@ -1737,7 +1724,8 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_comp_exch(id_priv, CMA_ADDR_RESOLVED, CMA_ROUTE_RESOLVED))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED,
+ RDMA_CM_ROUTE_RESOLVED))
return -EINVAL;
id->route.path_rec = kmemdup(path_rec, sizeof *path_rec * num_paths,
@@ -1750,7 +1738,7 @@
id->route.num_paths = num_paths;
return 0;
err:
- cma_comp_exch(id_priv, CMA_ROUTE_RESOLVED, CMA_ADDR_RESOLVED);
+ cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_ADDR_RESOLVED);
return ret;
}
EXPORT_SYMBOL(rdma_set_ib_paths);
@@ -1765,8 +1753,8 @@
work->id = id_priv;
INIT_WORK(&work->work, cma_work_handler);
- work->old_state = CMA_ROUTE_QUERY;
- work->new_state = CMA_ROUTE_RESOLVED;
+ work->old_state = RDMA_CM_ROUTE_QUERY;
+ work->new_state = RDMA_CM_ROUTE_RESOLVED;
work->event.event = RDMA_CM_EVENT_ROUTE_RESOLVED;
queue_work(cma_wq, &work->work);
return 0;
@@ -1830,8 +1818,8 @@
goto err2;
}
- work->old_state = CMA_ROUTE_QUERY;
- work->new_state = CMA_ROUTE_RESOLVED;
+ work->old_state = RDMA_CM_ROUTE_QUERY;
+ work->new_state = RDMA_CM_ROUTE_RESOLVED;
work->event.event = RDMA_CM_EVENT_ROUTE_RESOLVED;
work->event.status = 0;
@@ -1853,7 +1841,7 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_comp_exch(id_priv, CMA_ADDR_RESOLVED, CMA_ROUTE_QUERY))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED, RDMA_CM_ROUTE_QUERY))
return -EINVAL;
atomic_inc(&id_priv->refcount);
@@ -1882,7 +1870,7 @@
return 0;
err:
- cma_comp_exch(id_priv, CMA_ROUTE_QUERY, CMA_ADDR_RESOLVED);
+ cma_comp_exch(id_priv, RDMA_CM_ROUTE_QUERY, RDMA_CM_ADDR_RESOLVED);
cma_deref_id(id_priv);
return ret;
}
@@ -1941,14 +1929,16 @@
memset(&event, 0, sizeof event);
mutex_lock(&id_priv->handler_mutex);
- if (!cma_comp_exch(id_priv, CMA_ADDR_QUERY, CMA_ADDR_RESOLVED))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_QUERY,
+ RDMA_CM_ADDR_RESOLVED))
goto out;
if (!status && !id_priv->cma_dev)
status = cma_acquire_dev(id_priv);
if (status) {
- if (!cma_comp_exch(id_priv, CMA_ADDR_RESOLVED, CMA_ADDR_BOUND))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_RESOLVED,
+ RDMA_CM_ADDR_BOUND))
goto out;
event.event = RDMA_CM_EVENT_ADDR_ERROR;
event.status = status;
@@ -1959,7 +1949,7 @@
}
if (id_priv->id.event_handler(&id_priv->id, &event)) {
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
mutex_unlock(&id_priv->handler_mutex);
cma_deref_id(id_priv);
rdma_destroy_id(&id_priv->id);
@@ -2004,8 +1994,8 @@
work->id = id_priv;
INIT_WORK(&work->work, cma_work_handler);
- work->old_state = CMA_ADDR_QUERY;
- work->new_state = CMA_ADDR_RESOLVED;
+ work->old_state = RDMA_CM_ADDR_QUERY;
+ work->new_state = RDMA_CM_ADDR_RESOLVED;
work->event.event = RDMA_CM_EVENT_ADDR_RESOLVED;
queue_work(cma_wq, &work->work);
return 0;
@@ -2034,13 +2024,13 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (id_priv->state == CMA_IDLE) {
+ if (id_priv->state == RDMA_CM_IDLE) {
ret = cma_bind_addr(id, src_addr, dst_addr);
if (ret)
return ret;
}
- if (!cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_ADDR_QUERY))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_ADDR_QUERY))
return -EINVAL;
atomic_inc(&id_priv->refcount);
@@ -2056,7 +2046,7 @@
return 0;
err:
- cma_comp_exch(id_priv, CMA_ADDR_QUERY, CMA_ADDR_BOUND);
+ cma_comp_exch(id_priv, RDMA_CM_ADDR_QUERY, RDMA_CM_ADDR_BOUND);
cma_deref_id(id_priv);
return ret;
}
@@ -2070,7 +2060,7 @@
id_priv = container_of(id, struct rdma_id_private, id);
spin_lock_irqsave(&id_priv->lock, flags);
- if (id_priv->state == CMA_IDLE) {
+ if (id_priv->state == RDMA_CM_IDLE) {
id_priv->reuseaddr = reuse;
ret = 0;
} else {
@@ -2177,7 +2167,7 @@
if (id_priv == cur_id)
continue;
- if ((cur_id->state == CMA_LISTEN) ||
+ if ((cur_id->state == RDMA_CM_LISTEN) ||
!reuseaddr || !cur_id->reuseaddr) {
cur_addr = (struct sockaddr *) &cur_id->id.route.addr.src_addr;
if (cma_any_addr(cur_addr))
@@ -2280,14 +2270,14 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (id_priv->state == CMA_IDLE) {
+ if (id_priv->state == RDMA_CM_IDLE) {
((struct sockaddr *) &id->route.addr.src_addr)->sa_family = AF_INET;
ret = rdma_bind_addr(id, (struct sockaddr *) &id->route.addr.src_addr);
if (ret)
return ret;
}
- if (!cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_LISTEN))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_LISTEN))
return -EINVAL;
if (id_priv->reuseaddr) {
@@ -2319,7 +2309,7 @@
return 0;
err:
id_priv->backlog = 0;
- cma_comp_exch(id_priv, CMA_LISTEN, CMA_ADDR_BOUND);
+ cma_comp_exch(id_priv, RDMA_CM_LISTEN, RDMA_CM_ADDR_BOUND);
return ret;
}
EXPORT_SYMBOL(rdma_listen);
@@ -2333,7 +2323,7 @@
return -EAFNOSUPPORT;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_comp_exch(id_priv, CMA_IDLE, CMA_ADDR_BOUND))
+ if (!cma_comp_exch(id_priv, RDMA_CM_IDLE, RDMA_CM_ADDR_BOUND))
return -EINVAL;
ret = cma_check_linklocal(&id->route.addr.dev_addr, addr);
@@ -2360,7 +2350,7 @@
if (id_priv->cma_dev)
cma_release_dev(id_priv);
err1:
- cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_IDLE);
+ cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_IDLE);
return ret;
}
EXPORT_SYMBOL(rdma_bind_addr);
@@ -2433,7 +2423,7 @@
struct ib_cm_sidr_rep_event_param *rep = &ib_event->param.sidr_rep_rcvd;
int ret = 0;
- if (cma_disable_callback(id_priv, CMA_CONNECT))
+ if (cma_disable_callback(id_priv, RDMA_CM_CONNECT))
return 0;
memset(&event, 0, sizeof event);
@@ -2479,7 +2469,7 @@
if (ret) {
/* Destroy the CM ID by returning a non-zero value. */
id_priv->cm_id.ib = NULL;
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
mutex_unlock(&id_priv->handler_mutex);
rdma_destroy_id(&id_priv->id);
return ret;
@@ -2645,7 +2635,7 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_comp_exch(id_priv, CMA_ROUTE_RESOLVED, CMA_CONNECT))
+ if (!cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_CONNECT))
return -EINVAL;
if (!id->qp) {
@@ -2655,7 +2645,7 @@
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (cma_is_ud_ps(id->ps))
+ if (id->qp_type == IB_QPT_UD)
ret = cma_resolve_ib_udp(id_priv, conn_param);
else
ret = cma_connect_ib(id_priv, conn_param);
@@ -2672,7 +2662,7 @@
return 0;
err:
- cma_comp_exch(id_priv, CMA_CONNECT, CMA_ROUTE_RESOLVED);
+ cma_comp_exch(id_priv, RDMA_CM_CONNECT, RDMA_CM_ROUTE_RESOLVED);
return ret;
}
EXPORT_SYMBOL(rdma_connect);
@@ -2758,7 +2748,10 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_comp(id_priv, CMA_CONNECT))
+
+ id_priv->owner = task_pid_nr(current);
+
+ if (!cma_comp(id_priv, RDMA_CM_CONNECT))
return -EINVAL;
if (!id->qp && conn_param) {
@@ -2768,7 +2761,7 @@
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (cma_is_ud_ps(id->ps))
+ if (id->qp_type == IB_QPT_UD)
ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
conn_param->private_data,
conn_param->private_data_len);
@@ -2829,7 +2822,7 @@
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
- if (cma_is_ud_ps(id->ps))
+ if (id->qp_type == IB_QPT_UD)
ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT,
private_data, private_data_len);
else
@@ -2887,8 +2880,8 @@
int ret;
id_priv = mc->id_priv;
- if (cma_disable_callback(id_priv, CMA_ADDR_BOUND) &&
- cma_disable_callback(id_priv, CMA_ADDR_RESOLVED))
+ if (cma_disable_callback(id_priv, RDMA_CM_ADDR_BOUND) &&
+ cma_disable_callback(id_priv, RDMA_CM_ADDR_RESOLVED))
return 0;
mutex_lock(&id_priv->qp_mutex);
@@ -2912,7 +2905,7 @@
ret = id_priv->id.event_handler(&id_priv->id, &event);
if (ret) {
- cma_exch(id_priv, CMA_DESTROYING);
+ cma_exch(id_priv, RDMA_CM_DESTROYING);
mutex_unlock(&id_priv->handler_mutex);
rdma_destroy_id(&id_priv->id);
return 0;
@@ -3095,8 +3088,8 @@
int ret;
id_priv = container_of(id, struct rdma_id_private, id);
- if (!cma_comp(id_priv, CMA_ADDR_BOUND) &&
- !cma_comp(id_priv, CMA_ADDR_RESOLVED))
+ if (!cma_comp(id_priv, RDMA_CM_ADDR_BOUND) &&
+ !cma_comp(id_priv, RDMA_CM_ADDR_RESOLVED))
return -EINVAL;
mc = kmalloc(sizeof *mc, GFP_KERNEL);
@@ -3261,19 +3254,19 @@
static int cma_remove_id_dev(struct rdma_id_private *id_priv)
{
struct rdma_cm_event event;
- enum cma_state state;
+ enum rdma_cm_state state;
int ret = 0;
/* Record that we want to remove the device */
- state = cma_exch(id_priv, CMA_DEVICE_REMOVAL);
- if (state == CMA_DESTROYING)
+ state = cma_exch(id_priv, RDMA_CM_DEVICE_REMOVAL);
+ if (state == RDMA_CM_DESTROYING)
return 0;
cma_cancel_operation(id_priv, state);
mutex_lock(&id_priv->handler_mutex);
/* Check for destruction from another callback. */
- if (!cma_comp(id_priv, CMA_DEVICE_REMOVAL))
+ if (!cma_comp(id_priv, RDMA_CM_DEVICE_REMOVAL))
goto out;
memset(&event, 0, sizeof event);
@@ -3328,6 +3321,100 @@
kfree(cma_dev);
}
+static int cma_get_id_stats(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct nlmsghdr *nlh;
+ struct rdma_cm_id_stats *id_stats;
+ struct rdma_id_private *id_priv;
+ struct rdma_cm_id *id = NULL;
+ struct cma_device *cma_dev;
+ int i_dev = 0, i_id = 0;
+
+ /*
+ * We export all of the IDs as a sequence of messages. Each
+ * ID gets its own netlink message.
+ */
+ mutex_lock(&lock);
+
+ list_for_each_entry(cma_dev, &dev_list, list) {
+ if (i_dev < cb->args[0]) {
+ i_dev++;
+ continue;
+ }
+
+ i_id = 0;
+ list_for_each_entry(id_priv, &cma_dev->id_list, list) {
+ if (i_id < cb->args[1]) {
+ i_id++;
+ continue;
+ }
+
+ id_stats = ibnl_put_msg(skb, &nlh, cb->nlh->nlmsg_seq,
+ sizeof *id_stats, RDMA_NL_RDMA_CM,
+ RDMA_NL_RDMA_CM_ID_STATS);
+ if (!id_stats)
+ goto out;
+
+ memset(id_stats, 0, sizeof *id_stats);
+ id = &id_priv->id;
+ id_stats->node_type = id->route.addr.dev_addr.dev_type;
+ id_stats->port_num = id->port_num;
+ id_stats->bound_dev_if =
+ id->route.addr.dev_addr.bound_dev_if;
+
+ if (id->route.addr.src_addr.ss_family == AF_INET) {
+ if (ibnl_put_attr(skb, nlh,
+ sizeof(struct sockaddr_in),
+ &id->route.addr.src_addr,
+ RDMA_NL_RDMA_CM_ATTR_SRC_ADDR)) {
+ goto out;
+ }
+ if (ibnl_put_attr(skb, nlh,
+ sizeof(struct sockaddr_in),
+ &id->route.addr.dst_addr,
+ RDMA_NL_RDMA_CM_ATTR_DST_ADDR)) {
+ goto out;
+ }
+ } else if (id->route.addr.src_addr.ss_family == AF_INET6) {
+ if (ibnl_put_attr(skb, nlh,
+ sizeof(struct sockaddr_in6),
+ &id->route.addr.src_addr,
+ RDMA_NL_RDMA_CM_ATTR_SRC_ADDR)) {
+ goto out;
+ }
+ if (ibnl_put_attr(skb, nlh,
+ sizeof(struct sockaddr_in6),
+ &id->route.addr.dst_addr,
+ RDMA_NL_RDMA_CM_ATTR_DST_ADDR)) {
+ goto out;
+ }
+ }
+
+ id_stats->pid = id_priv->owner;
+ id_stats->port_space = id->ps;
+ id_stats->cm_state = id_priv->state;
+ id_stats->qp_num = id_priv->qp_num;
+ id_stats->qp_type = id->qp_type;
+
+ i_id++;
+ }
+
+ cb->args[1] = 0;
+ i_dev++;
+ }
+
+out:
+ mutex_unlock(&lock);
+ cb->args[0] = i_dev;
+ cb->args[1] = i_id;
+
+ return skb->len;
+}
+
+static const struct ibnl_client_cbs cma_cb_table[] = {
+ [RDMA_NL_RDMA_CM_ID_STATS] = { .dump = cma_get_id_stats },
+};
+
static int __init cma_init(void)
{
int ret;
@@ -3343,6 +3430,10 @@
ret = ib_register_client(&cma_client);
if (ret)
goto err;
+
+ if (ibnl_add_client(RDMA_NL_RDMA_CM, RDMA_NL_RDMA_CM_NUM_OPS, cma_cb_table))
+ printk(KERN_WARNING "RDMA CMA: failed to add netlink callback\n");
+
return 0;
err:
@@ -3355,6 +3446,7 @@
static void __exit cma_cleanup(void)
{
+ ibnl_remove_client(RDMA_NL_RDMA_CM);
ib_unregister_client(&cma_client);
unregister_netdevice_notifier(&cma_nb);
rdma_addr_unregister_client(&addr_client);
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index f793bf2..4007f72 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -38,6 +38,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <rdma/rdma_netlink.h>
#include "core_priv.h"
@@ -725,22 +726,40 @@
return -ENOMEM;
ret = ib_sysfs_setup();
- if (ret)
+ if (ret) {
printk(KERN_WARNING "Couldn't create InfiniBand device class\n");
+ goto err;
+ }
+
+ ret = ibnl_init();
+ if (ret) {
+ printk(KERN_WARNING "Couldn't init IB netlink interface\n");
+ goto err_sysfs;
+ }
ret = ib_cache_setup();
if (ret) {
printk(KERN_WARNING "Couldn't set up InfiniBand P_Key/GID cache\n");
- ib_sysfs_cleanup();
- destroy_workqueue(ib_wq);
+ goto err_nl;
}
+ return 0;
+
+err_nl:
+ ibnl_cleanup();
+
+err_sysfs:
+ ib_sysfs_cleanup();
+
+err:
+ destroy_workqueue(ib_wq);
return ret;
}
static void __exit ib_core_cleanup(void)
{
ib_cache_cleanup();
+ ibnl_cleanup();
ib_sysfs_cleanup();
/* Make sure that any pending umem accounting work is done. */
destroy_workqueue(ib_wq);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 822cfdc..b4d8672 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -276,6 +276,13 @@
goto error1;
}
+ /* Verify the QP requested is supported. For example, Ethernet devices
+ * will not have QP0 */
+ if (!port_priv->qp_info[qpn].qp) {
+ ret = ERR_PTR(-EPROTONOSUPPORT);
+ goto error1;
+ }
+
/* Allocate structures */
mad_agent_priv = kzalloc(sizeof *mad_agent_priv, GFP_KERNEL);
if (!mad_agent_priv) {
diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c
new file mode 100644
index 0000000..4a5abaf
--- /dev/null
+++ b/drivers/infiniband/core/netlink.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2010 Voltaire Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
+
+#include <net/netlink.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <rdma/rdma_netlink.h>
+
+struct ibnl_client {
+ struct list_head list;
+ int index;
+ int nops;
+ const struct ibnl_client_cbs *cb_table;
+};
+
+static DEFINE_MUTEX(ibnl_mutex);
+static struct sock *nls;
+static LIST_HEAD(client_list);
+
+int ibnl_add_client(int index, int nops,
+ const struct ibnl_client_cbs cb_table[])
+{
+ struct ibnl_client *cur;
+ struct ibnl_client *nl_client;
+
+ nl_client = kmalloc(sizeof *nl_client, GFP_KERNEL);
+ if (!nl_client)
+ return -ENOMEM;
+
+ nl_client->index = index;
+ nl_client->nops = nops;
+ nl_client->cb_table = cb_table;
+
+ mutex_lock(&ibnl_mutex);
+
+ list_for_each_entry(cur, &client_list, list) {
+ if (cur->index == index) {
+ pr_warn("Client for %d already exists\n", index);
+ mutex_unlock(&ibnl_mutex);
+ kfree(nl_client);
+ return -EINVAL;
+ }
+ }
+
+ list_add_tail(&nl_client->list, &client_list);
+
+ mutex_unlock(&ibnl_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ibnl_add_client);
+
+int ibnl_remove_client(int index)
+{
+ struct ibnl_client *cur, *next;
+
+ mutex_lock(&ibnl_mutex);
+ list_for_each_entry_safe(cur, next, &client_list, list) {
+ if (cur->index == index) {
+ list_del(&(cur->list));
+ mutex_unlock(&ibnl_mutex);
+ kfree(cur);
+ return 0;
+ }
+ }
+ pr_warn("Can't remove callback for client idx %d. Not found\n", index);
+ mutex_unlock(&ibnl_mutex);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ibnl_remove_client);
+
+void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
+ int len, int client, int op)
+{
+ unsigned char *prev_tail;
+
+ prev_tail = skb_tail_pointer(skb);
+ *nlh = NLMSG_NEW(skb, 0, seq, RDMA_NL_GET_TYPE(client, op),
+ len, NLM_F_MULTI);
+ (*nlh)->nlmsg_len = skb_tail_pointer(skb) - prev_tail;
+ return NLMSG_DATA(*nlh);
+
+nlmsg_failure:
+ nlmsg_trim(skb, prev_tail);
+ return NULL;
+}
+EXPORT_SYMBOL(ibnl_put_msg);
+
+int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
+ int len, void *data, int type)
+{
+ unsigned char *prev_tail;
+
+ prev_tail = skb_tail_pointer(skb);
+ NLA_PUT(skb, type, len, data);
+ nlh->nlmsg_len += skb_tail_pointer(skb) - prev_tail;
+ return 0;
+
+nla_put_failure:
+ nlmsg_trim(skb, prev_tail - nlh->nlmsg_len);
+ return -EMSGSIZE;
+}
+EXPORT_SYMBOL(ibnl_put_attr);
+
+static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ struct ibnl_client *client;
+ int type = nlh->nlmsg_type;
+ int index = RDMA_NL_GET_CLIENT(type);
+ int op = RDMA_NL_GET_OP(type);
+
+ list_for_each_entry(client, &client_list, list) {
+ if (client->index == index) {
+ if (op < 0 || op >= client->nops ||
+ !client->cb_table[RDMA_NL_GET_OP(op)].dump)
+ return -EINVAL;
+ return netlink_dump_start(nls, skb, nlh,
+ client->cb_table[op].dump,
+ NULL);
+ }
+ }
+
+ pr_info("Index %d wasn't found in client list\n", index);
+ return -EINVAL;
+}
+
+static void ibnl_rcv(struct sk_buff *skb)
+{
+ mutex_lock(&ibnl_mutex);
+ netlink_rcv_skb(skb, &ibnl_rcv_msg);
+ mutex_unlock(&ibnl_mutex);
+}
+
+int __init ibnl_init(void)
+{
+ nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 0, ibnl_rcv,
+ NULL, THIS_MODULE);
+ if (!nls) {
+ pr_warn("Failed to create netlink socket\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void ibnl_cleanup(void)
+{
+ struct ibnl_client *cur, *next;
+
+ mutex_lock(&ibnl_mutex);
+ list_for_each_entry_safe(cur, next, &client_list, list) {
+ list_del(&(cur->list));
+ kfree(cur);
+ }
+ mutex_unlock(&ibnl_mutex);
+
+ netlink_kernel_release(nls);
+}
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index b3fa798..71be5ee 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -367,13 +367,28 @@
return ret;
}
-static ssize_t ucma_create_id(struct ucma_file *file,
- const char __user *inbuf,
- int in_len, int out_len)
+static int ucma_get_qp_type(struct rdma_ucm_create_id *cmd, enum ib_qp_type *qp_type)
+{
+ switch (cmd->ps) {
+ case RDMA_PS_TCP:
+ *qp_type = IB_QPT_RC;
+ return 0;
+ case RDMA_PS_UDP:
+ case RDMA_PS_IPOIB:
+ *qp_type = IB_QPT_UD;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
{
struct rdma_ucm_create_id cmd;
struct rdma_ucm_create_id_resp resp;
struct ucma_context *ctx;
+ enum ib_qp_type qp_type;
int ret;
if (out_len < sizeof(resp))
@@ -382,6 +397,10 @@
if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
return -EFAULT;
+ ret = ucma_get_qp_type(&cmd, &qp_type);
+ if (ret)
+ return ret;
+
mutex_lock(&file->mut);
ctx = ucma_alloc_ctx(file);
mutex_unlock(&file->mut);
@@ -389,7 +408,7 @@
return -ENOMEM;
ctx->uid = cmd.uid;
- ctx->cm_id = rdma_create_id(ucma_event_handler, ctx, cmd.ps);
+ ctx->cm_id = rdma_create_id(ucma_event_handler, ctx, cmd.ps, qp_type);
if (IS_ERR(ctx->cm_id)) {
ret = PTR_ERR(ctx->cm_id);
goto err1;
@@ -1338,9 +1357,11 @@
};
static struct miscdevice ucma_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "rdma_cm",
- .fops = &ucma_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "rdma_cm",
+ .nodename = "infiniband/rdma_cm",
+ .mode = 0666,
+ .fops = &ucma_fops,
};
static ssize_t show_abi_version(struct device *dev,
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index cd1996d..8d261b6 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -1176,6 +1176,11 @@
kref_put(&umad_dev->ref, ib_umad_release_dev);
}
+static char *umad_devnode(struct device *dev, mode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev));
+}
+
static int __init ib_umad_init(void)
{
int ret;
@@ -1194,6 +1199,8 @@
goto out_chrdev;
}
+ umad_class->devnode = umad_devnode;
+
ret = class_create_file(umad_class, &class_attr_abi_version.attr);
if (ret) {
printk(KERN_ERR "user_mad: couldn't create abi_version attribute\n");
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index ec83e9f..e49a85f 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -824,6 +824,12 @@
kfree(uverbs_dev);
}
+static char *uverbs_devnode(struct device *dev, mode_t *mode)
+{
+ *mode = 0666;
+ return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev));
+}
+
static int __init ib_uverbs_init(void)
{
int ret;
@@ -842,6 +848,8 @@
goto out_chrdev;
}
+ uverbs_class->devnode = uverbs_devnode;
+
ret = class_create_file(uverbs_class, &class_attr_abi_version.attr);
if (ret) {
printk(KERN_ERR "user_verbs: couldn't create abi_version attribute\n");
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 2391841..0a5008f 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -914,7 +914,7 @@
goto err;
if (peer2peer && iwch_rqes_posted(ep->com.qp) == 0) {
- iwch_post_zb_read(ep->com.qp);
+ iwch_post_zb_read(ep);
}
goto out;
@@ -1078,6 +1078,8 @@
struct iwch_ep *ep = ctx;
struct cpl_wr_ack *hdr = cplhdr(skb);
unsigned int credits = ntohs(hdr->credits);
+ unsigned long flags;
+ int post_zb = 0;
PDBG("%s ep %p credits %u\n", __func__, ep, credits);
@@ -1087,28 +1089,34 @@
return CPL_RET_BUF_DONE;
}
+ spin_lock_irqsave(&ep->com.lock, flags);
BUG_ON(credits != 1);
dst_confirm(ep->dst);
if (!ep->mpa_skb) {
PDBG("%s rdma_init wr_ack ep %p state %u\n",
- __func__, ep, state_read(&ep->com));
+ __func__, ep, ep->com.state);
if (ep->mpa_attr.initiator) {
PDBG("%s initiator ep %p state %u\n",
- __func__, ep, state_read(&ep->com));
- if (peer2peer)
- iwch_post_zb_read(ep->com.qp);
+ __func__, ep, ep->com.state);
+ if (peer2peer && ep->com.state == FPDU_MODE)
+ post_zb = 1;
} else {
PDBG("%s responder ep %p state %u\n",
- __func__, ep, state_read(&ep->com));
- ep->com.rpl_done = 1;
- wake_up(&ep->com.waitq);
+ __func__, ep, ep->com.state);
+ if (ep->com.state == MPA_REQ_RCVD) {
+ ep->com.rpl_done = 1;
+ wake_up(&ep->com.waitq);
+ }
}
} else {
PDBG("%s lsm ack ep %p state %u freeing skb\n",
- __func__, ep, state_read(&ep->com));
+ __func__, ep, ep->com.state);
kfree_skb(ep->mpa_skb);
ep->mpa_skb = NULL;
}
+ spin_unlock_irqrestore(&ep->com.lock, flags);
+ if (post_zb)
+ iwch_post_zb_read(ep);
return CPL_RET_BUF_DONE;
}
diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.h b/drivers/infiniband/hw/cxgb3/iwch_provider.h
index c5406da..9a342c9 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_provider.h
+++ b/drivers/infiniband/hw/cxgb3/iwch_provider.h
@@ -332,7 +332,7 @@
struct ib_mw_bind *mw_bind);
int iwch_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
int iwch_post_terminate(struct iwch_qp *qhp, struct respQ_msg_t *rsp_msg);
-int iwch_post_zb_read(struct iwch_qp *qhp);
+int iwch_post_zb_read(struct iwch_ep *ep);
int iwch_register_device(struct iwch_dev *dev);
void iwch_unregister_device(struct iwch_dev *dev);
void stop_read_rep_timer(struct iwch_qp *qhp);
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index 1b4cd09..ecd313f 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -738,7 +738,7 @@
}
}
-int iwch_post_zb_read(struct iwch_qp *qhp)
+int iwch_post_zb_read(struct iwch_ep *ep)
{
union t3_wr *wqe;
struct sk_buff *skb;
@@ -761,10 +761,10 @@
wqe->read.local_len = cpu_to_be32(0);
wqe->read.local_to = cpu_to_be64(1);
wqe->send.wrh.op_seop_flags = cpu_to_be32(V_FW_RIWR_OP(T3_WR_READ));
- wqe->send.wrh.gen_tid_len = cpu_to_be32(V_FW_RIWR_TID(qhp->ep->hwtid)|
+ wqe->send.wrh.gen_tid_len = cpu_to_be32(V_FW_RIWR_TID(ep->hwtid)|
V_FW_RIWR_LEN(flit_cnt));
skb->priority = CPL_PRIORITY_DATA;
- return iwch_cxgb3_ofld_send(qhp->rhp->rdev.t3cdev_p, skb);
+ return iwch_cxgb3_ofld_send(ep->com.qp->rhp->rdev.t3cdev_p, skb);
}
/*
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 35d2a5d..4f04537 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -35,7 +35,7 @@
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/idr.h>
-#include <linux/workqueue.h>
+#include <linux/completion.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
#include <linux/pci.h>
@@ -131,28 +131,21 @@
#define C4IW_WR_TO (10*HZ)
-enum {
- REPLY_READY = 0,
-};
-
struct c4iw_wr_wait {
- wait_queue_head_t wait;
- unsigned long status;
+ struct completion completion;
int ret;
};
static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp)
{
wr_waitp->ret = 0;
- wr_waitp->status = 0;
- init_waitqueue_head(&wr_waitp->wait);
+ init_completion(&wr_waitp->completion);
}
static inline void c4iw_wake_up(struct c4iw_wr_wait *wr_waitp, int ret)
{
wr_waitp->ret = ret;
- set_bit(REPLY_READY, &wr_waitp->status);
- wake_up(&wr_waitp->wait);
+ complete(&wr_waitp->completion);
}
static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev,
@@ -164,8 +157,7 @@
int ret;
do {
- ret = wait_event_timeout(wr_waitp->wait,
- test_and_clear_bit(REPLY_READY, &wr_waitp->status), to);
+ ret = wait_for_completion_timeout(&wr_waitp->completion, to);
if (!ret) {
printk(KERN_ERR MOD "%s - Device %s not responding - "
"tid %u qpid %u\n", func,
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
index 13de119..2d668c6 100644
--- a/drivers/infiniband/hw/nes/nes.c
+++ b/drivers/infiniband/hw/nes/nes.c
@@ -1138,7 +1138,9 @@
u32 i = 0;
struct nes_device *nesdev;
- strict_strtoul(buf, 0, &wqm_quanta_value);
+ if (kstrtoul(buf, 0, &wqm_quanta_value) < 0)
+ return -EINVAL;
+
list_for_each_entry(nesdev, &nes_dev_list, list) {
if (i == ee_flsh_adapter) {
nesdev->nesadapter->wqm_quanta = wqm_quanta_value;
diff --git a/drivers/infiniband/hw/qib/Kconfig b/drivers/infiniband/hw/qib/Kconfig
index 7c03a70..8349f9c 100644
--- a/drivers/infiniband/hw/qib/Kconfig
+++ b/drivers/infiniband/hw/qib/Kconfig
@@ -1,6 +1,6 @@
config INFINIBAND_QIB
tristate "QLogic PCIe HCA support"
- depends on 64BIT && NET
+ depends on 64BIT
---help---
This is a low-level driver for QLogic PCIe QLE InfiniBand host
channel adapters. This driver does not support the QLogic
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index 98768657..ede1475 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -548,7 +548,7 @@
iser_conn_get(ib_conn); /* ref ib conn's cma id */
ib_conn->cma_id = rdma_create_id(iser_cma_handler,
(void *)ib_conn,
- RDMA_PS_TCP);
+ RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(ib_conn->cma_id)) {
err = PTR_ERR(ib_conn->cma_id);
iser_err("rdma_create_id failed: %d\n", err);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 376d640..ee165fd 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1147,7 +1147,7 @@
static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
{
struct ib_device *dev = target->srp_host->srp_dev->dev;
- struct srp_iu *iu = (struct srp_iu *) wc->wr_id;
+ struct srp_iu *iu = (struct srp_iu *) (uintptr_t) wc->wr_id;
int res;
u8 opcode;
@@ -1231,7 +1231,7 @@
break;
}
- iu = (struct srp_iu *) wc.wr_id;
+ iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
list_add(&iu->list, &target->free_tx);
}
}
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 69badb4..b4dee9d 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -412,6 +412,17 @@
To compile this driver as a module, choose M here: the
module will be called pxa930_rotary.
+config KEYBOARD_PMIC8XXX
+ tristate "Qualcomm PMIC8XXX keypad support"
+ depends on MFD_PM8XXX
+ help
+ Say Y here if you want to enable the driver for the PMIC8XXX
+ keypad provided as a reference design from Qualcomm. This is intended
+ to support upto 18x8 matrix based keypad design.
+
+ To compile this driver as a module, choose M here: the module will
+ be called pmic8xxx-keypad.
+
config KEYBOARD_SAMSUNG
tristate "Samsung keypad support"
depends on SAMSUNG_DEV_KEYPAD
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index c49cf8e..ddde0fd 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -34,6 +34,7 @@
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
+obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c
new file mode 100644
index 0000000..40b02ae
--- /dev/null
+++ b/drivers/input/keyboard/pmic8xxx-keypad.c
@@ -0,0 +1,799 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/gpio.h>
+#include <linux/input/pmic8xxx-keypad.h>
+
+#define PM8XXX_MAX_ROWS 18
+#define PM8XXX_MAX_COLS 8
+#define PM8XXX_ROW_SHIFT 3
+#define PM8XXX_MATRIX_MAX_SIZE (PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS)
+
+#define PM8XXX_MIN_ROWS 5
+#define PM8XXX_MIN_COLS 5
+
+#define MAX_SCAN_DELAY 128
+#define MIN_SCAN_DELAY 1
+
+/* in nanoseconds */
+#define MAX_ROW_HOLD_DELAY 122000
+#define MIN_ROW_HOLD_DELAY 30500
+
+#define MAX_DEBOUNCE_TIME 20
+#define MIN_DEBOUNCE_TIME 5
+
+#define KEYP_CTRL 0x148
+
+#define KEYP_CTRL_EVNTS BIT(0)
+#define KEYP_CTRL_EVNTS_MASK 0x3
+
+#define KEYP_CTRL_SCAN_COLS_SHIFT 5
+#define KEYP_CTRL_SCAN_COLS_MIN 5
+#define KEYP_CTRL_SCAN_COLS_BITS 0x3
+
+#define KEYP_CTRL_SCAN_ROWS_SHIFT 2
+#define KEYP_CTRL_SCAN_ROWS_MIN 5
+#define KEYP_CTRL_SCAN_ROWS_BITS 0x7
+
+#define KEYP_CTRL_KEYP_EN BIT(7)
+
+#define KEYP_SCAN 0x149
+
+#define KEYP_SCAN_READ_STATE BIT(0)
+#define KEYP_SCAN_DBOUNCE_SHIFT 1
+#define KEYP_SCAN_PAUSE_SHIFT 3
+#define KEYP_SCAN_ROW_HOLD_SHIFT 6
+
+#define KEYP_TEST 0x14A
+
+#define KEYP_TEST_CLEAR_RECENT_SCAN BIT(6)
+#define KEYP_TEST_CLEAR_OLD_SCAN BIT(5)
+#define KEYP_TEST_READ_RESET BIT(4)
+#define KEYP_TEST_DTEST_EN BIT(3)
+#define KEYP_TEST_ABORT_READ BIT(0)
+
+#define KEYP_TEST_DBG_SELECT_SHIFT 1
+
+/* bits of these registers represent
+ * '0' for key press
+ * '1' for key release
+ */
+#define KEYP_RECENT_DATA 0x14B
+#define KEYP_OLD_DATA 0x14C
+
+#define KEYP_CLOCK_FREQ 32768
+
+/**
+ * struct pmic8xxx_kp - internal keypad data structure
+ * @pdata - keypad platform data pointer
+ * @input - input device pointer for keypad
+ * @key_sense_irq - key press/release irq number
+ * @key_stuck_irq - key stuck notification irq number
+ * @keycodes - array to hold the key codes
+ * @dev - parent device pointer
+ * @keystate - present key press/release state
+ * @stuckstate - present state when key stuck irq
+ * @ctrl_reg - control register value
+ */
+struct pmic8xxx_kp {
+ const struct pm8xxx_keypad_platform_data *pdata;
+ struct input_dev *input;
+ int key_sense_irq;
+ int key_stuck_irq;
+
+ unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE];
+
+ struct device *dev;
+ u16 keystate[PM8XXX_MAX_ROWS];
+ u16 stuckstate[PM8XXX_MAX_ROWS];
+
+ u8 ctrl_reg;
+};
+
+static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp,
+ u8 data, u16 reg)
+{
+ int rc;
+
+ rc = pm8xxx_writeb(kp->dev->parent, reg, data);
+ return rc;
+}
+
+static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp,
+ u8 *data, u16 reg, unsigned num_bytes)
+{
+ int rc;
+
+ rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes);
+ return rc;
+}
+
+static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp,
+ u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = pmic8xxx_kp_read(kp, data, reg, 1);
+ return rc;
+}
+
+static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col)
+{
+ /* all keys pressed on that particular row? */
+ if (col == 0x00)
+ return 1 << kp->pdata->num_cols;
+ else
+ return col & ((1 << kp->pdata->num_cols) - 1);
+}
+
+/*
+ * Synchronous read protocol for RevB0 onwards:
+ *
+ * 1. Write '1' to ReadState bit in KEYP_SCAN register
+ * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode
+ * synchronously
+ * 3. Read rows in old array first if events are more than one
+ * 4. Read rows in recent array
+ * 5. Wait 4*32KHz clocks
+ * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can
+ * synchronously exit read mode.
+ */
+static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp)
+{
+ int rc;
+ u8 scan_val;
+
+ rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ scan_val |= 0x1;
+
+ rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* 2 * 32KHz clocks */
+ udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+ return rc;
+}
+
+static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state,
+ u16 data_reg, int read_rows)
+{
+ int rc, row;
+ u8 new_data[PM8XXX_MAX_ROWS];
+
+ rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows);
+ if (rc)
+ return rc;
+
+ for (row = 0; row < kp->pdata->num_rows; row++) {
+ dev_dbg(kp->dev, "new_data[%d] = %d\n", row,
+ new_data[row]);
+ state[row] = pmic8xxx_col_state(kp, new_data[row]);
+ }
+
+ return rc;
+}
+
+static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
+ u16 *old_state)
+{
+ int rc, read_rows;
+ u8 scan_val;
+
+ if (kp->pdata->num_rows < PM8XXX_MIN_ROWS)
+ read_rows = PM8XXX_MIN_ROWS;
+ else
+ read_rows = kp->pdata->num_rows;
+
+ pmic8xxx_chk_sync_read(kp);
+
+ if (old_state) {
+ rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA,
+ read_rows);
+ if (rc < 0) {
+ dev_err(kp->dev,
+ "Error reading KEYP_OLD_DATA, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA,
+ read_rows);
+ if (rc < 0) {
+ dev_err(kp->dev,
+ "Error reading KEYP_RECENT_DATA, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* 4 * 32KHz clocks */
+ udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+ rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ scan_val &= 0xFE;
+ rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ if (rc < 0)
+ dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+
+ return rc;
+}
+
+static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state,
+ u16 *old_state)
+{
+ int row, col, code;
+
+ for (row = 0; row < kp->pdata->num_rows; row++) {
+ int bits_changed = new_state[row] ^ old_state[row];
+
+ if (!bits_changed)
+ continue;
+
+ for (col = 0; col < kp->pdata->num_cols; col++) {
+ if (!(bits_changed & (1 << col)))
+ continue;
+
+ dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col,
+ !(new_state[row] & (1 << col)) ?
+ "pressed" : "released");
+
+ code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT);
+
+ input_event(kp->input, EV_MSC, MSC_SCAN, code);
+ input_report_key(kp->input,
+ kp->keycodes[code],
+ !(new_state[row] & (1 << col)));
+
+ input_sync(kp->input);
+ }
+ }
+}
+
+static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state)
+{
+ int row, found_first = -1;
+ u16 check, row_state;
+
+ check = 0;
+ for (row = 0; row < kp->pdata->num_rows; row++) {
+ row_state = (~new_state[row]) &
+ ((1 << kp->pdata->num_cols) - 1);
+
+ if (hweight16(row_state) > 1) {
+ if (found_first == -1)
+ found_first = row;
+ if (check & row_state) {
+ dev_dbg(kp->dev, "detected ghost key on row[%d]"
+ " and row[%d]\n", found_first, row);
+ return true;
+ }
+ }
+ check |= row_state;
+ }
+ return false;
+}
+
+static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int events)
+{
+ u16 new_state[PM8XXX_MAX_ROWS];
+ u16 old_state[PM8XXX_MAX_ROWS];
+ int rc;
+
+ switch (events) {
+ case 0x1:
+ rc = pmic8xxx_kp_read_matrix(kp, new_state, NULL);
+ if (rc < 0)
+ return rc;
+
+ /* detecting ghost key is not an error */
+ if (pmic8xxx_detect_ghost_keys(kp, new_state))
+ return 0;
+ __pmic8xxx_kp_scan_matrix(kp, new_state, kp->keystate);
+ memcpy(kp->keystate, new_state, sizeof(new_state));
+ break;
+ case 0x3: /* two events - eventcounter is gray-coded */
+ rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
+ if (rc < 0)
+ return rc;
+
+ __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate);
+ __pmic8xxx_kp_scan_matrix(kp, new_state, old_state);
+ memcpy(kp->keystate, new_state, sizeof(new_state));
+ break;
+ case 0x2:
+ dev_dbg(kp->dev, "Some key events were lost\n");
+ rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
+ if (rc < 0)
+ return rc;
+ __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate);
+ __pmic8xxx_kp_scan_matrix(kp, new_state, old_state);
+ memcpy(kp->keystate, new_state, sizeof(new_state));
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/*
+ * NOTE: We are reading recent and old data registers blindly
+ * whenever key-stuck interrupt happens, because events counter doesn't
+ * get updated when this interrupt happens due to key stuck doesn't get
+ * considered as key state change.
+ *
+ * We are not using old data register contents after they are being read
+ * because it might report the key which was pressed before the key being stuck
+ * as stuck key because it's pressed status is stored in the old data
+ * register.
+ */
+static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data)
+{
+ u16 new_state[PM8XXX_MAX_ROWS];
+ u16 old_state[PM8XXX_MAX_ROWS];
+ int rc;
+ struct pmic8xxx_kp *kp = data;
+
+ rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state);
+ if (rc < 0) {
+ dev_err(kp->dev, "failed to read keypad matrix\n");
+ return IRQ_HANDLED;
+ }
+
+ __pmic8xxx_kp_scan_matrix(kp, new_state, kp->stuckstate);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pmic8xxx_kp_irq(int irq, void *data)
+{
+ struct pmic8xxx_kp *kp = data;
+ u8 ctrl_val, events;
+ int rc;
+
+ rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1);
+ if (rc < 0) {
+ dev_err(kp->dev, "failed to read keyp_ctrl register\n");
+ return IRQ_HANDLED;
+ }
+
+ events = ctrl_val & KEYP_CTRL_EVNTS_MASK;
+
+ rc = pmic8xxx_kp_scan_matrix(kp, events);
+ if (rc < 0)
+ dev_err(kp->dev, "failed to scan matrix\n");
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit pmic8xxx_kpd_init(struct pmic8xxx_kp *kp)
+{
+ int bits, rc, cycles;
+ u8 scan_val = 0, ctrl_val = 0;
+ static const u8 row_bits[] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
+ };
+
+ /* Find column bits */
+ if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
+ bits = 0;
+ else
+ bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
+ ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) <<
+ KEYP_CTRL_SCAN_COLS_SHIFT;
+
+ /* Find row bits */
+ if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
+ bits = 0;
+ else
+ bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
+
+ ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
+
+ rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL);
+ if (rc < 0) {
+ dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
+ return rc;
+ }
+
+ bits = (kp->pdata->debounce_ms / 5) - 1;
+
+ scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT);
+
+ bits = fls(kp->pdata->scan_delay_ms) - 1;
+ scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT);
+
+ /* Row hold time is a multiple of 32KHz cycles. */
+ cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
+
+ scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
+
+ rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN);
+ if (rc)
+ dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc);
+
+ return rc;
+
+}
+
+static int __devinit pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios,
+ struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config)
+{
+ int rc, i;
+
+ if (gpio_start < 0 || num_gpios < 0)
+ return -EINVAL;
+
+ for (i = 0; i < num_gpios; i++) {
+ rc = pm8xxx_gpio_config(gpio_start + i, gpio_config);
+ if (rc) {
+ dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():"
+ "for PM GPIO [%d] rc=%d.\n",
+ __func__, gpio_start + i, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp)
+{
+ int rc;
+
+ kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
+
+ rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+ if (rc < 0)
+ dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp)
+{
+ int rc;
+
+ kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
+
+ rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+ if (rc < 0)
+ return rc;
+
+ return rc;
+}
+
+static int pmic8xxx_kp_open(struct input_dev *dev)
+{
+ struct pmic8xxx_kp *kp = input_get_drvdata(dev);
+
+ return pmic8xxx_kp_enable(kp);
+}
+
+static void pmic8xxx_kp_close(struct input_dev *dev)
+{
+ struct pmic8xxx_kp *kp = input_get_drvdata(dev);
+
+ pmic8xxx_kp_disable(kp);
+}
+
+/*
+ * keypad controller should be initialized in the following sequence
+ * only, otherwise it might get into FSM stuck state.
+ *
+ * - Initialize keypad control parameters, like no. of rows, columns,
+ * timing values etc.,
+ * - configure rows and column gpios pull up/down.
+ * - set irq edge type.
+ * - enable the keypad controller.
+ */
+static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev)
+{
+ const struct pm8xxx_keypad_platform_data *pdata = mfd_get_data(pdev);
+ const struct matrix_keymap_data *keymap_data;
+ struct pmic8xxx_kp *kp;
+ int rc;
+ u8 ctrl_val;
+
+ struct pm_gpio kypd_drv = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN,
+ .output_value = 0,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S3,
+ .out_strength = PM_GPIO_STRENGTH_LOW,
+ .function = PM_GPIO_FUNC_1,
+ .inv_int_pol = 1,
+ };
+
+ struct pm_gpio kypd_sns = {
+ .direction = PM_GPIO_DIR_IN,
+ .pull = PM_GPIO_PULL_UP_31P5,
+ .vin_sel = PM_GPIO_VIN_S3,
+ .out_strength = PM_GPIO_STRENGTH_NO,
+ .function = PM_GPIO_FUNC_NORMAL,
+ .inv_int_pol = 1,
+ };
+
+
+ if (!pdata || !pdata->num_cols || !pdata->num_rows ||
+ pdata->num_cols > PM8XXX_MAX_COLS ||
+ pdata->num_rows > PM8XXX_MAX_ROWS ||
+ pdata->num_cols < PM8XXX_MIN_COLS) {
+ dev_err(&pdev->dev, "invalid platform data\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->scan_delay_ms ||
+ pdata->scan_delay_ms > MAX_SCAN_DELAY ||
+ pdata->scan_delay_ms < MIN_SCAN_DELAY ||
+ !is_power_of_2(pdata->scan_delay_ms)) {
+ dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->row_hold_ns ||
+ pdata->row_hold_ns > MAX_ROW_HOLD_DELAY ||
+ pdata->row_hold_ns < MIN_ROW_HOLD_DELAY ||
+ ((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
+ dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->debounce_ms ||
+ ((pdata->debounce_ms % 5) != 0) ||
+ pdata->debounce_ms > MAX_DEBOUNCE_TIME ||
+ pdata->debounce_ms < MIN_DEBOUNCE_TIME) {
+ dev_err(&pdev->dev, "invalid debounce time supplied\n");
+ return -EINVAL;
+ }
+
+ keymap_data = pdata->keymap_data;
+ if (!keymap_data) {
+ dev_err(&pdev->dev, "no keymap data supplied\n");
+ return -EINVAL;
+ }
+
+ kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+ if (!kp)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, kp);
+
+ kp->pdata = pdata;
+ kp->dev = &pdev->dev;
+
+ kp->input = input_allocate_device();
+ if (!kp->input) {
+ dev_err(&pdev->dev, "unable to allocate input device\n");
+ rc = -ENOMEM;
+ goto err_alloc_device;
+ }
+
+ kp->key_sense_irq = platform_get_irq(pdev, 0);
+ if (kp->key_sense_irq < 0) {
+ dev_err(&pdev->dev, "unable to get keypad sense irq\n");
+ rc = -ENXIO;
+ goto err_get_irq;
+ }
+
+ kp->key_stuck_irq = platform_get_irq(pdev, 1);
+ if (kp->key_stuck_irq < 0) {
+ dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
+ rc = -ENXIO;
+ goto err_get_irq;
+ }
+
+ kp->input->name = pdata->input_name ? : "PMIC8XXX keypad";
+ kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0";
+
+ kp->input->dev.parent = &pdev->dev;
+
+ kp->input->id.bustype = BUS_I2C;
+ kp->input->id.version = 0x0001;
+ kp->input->id.product = 0x0001;
+ kp->input->id.vendor = 0x0001;
+
+ kp->input->evbit[0] = BIT_MASK(EV_KEY);
+
+ if (pdata->rep)
+ __set_bit(EV_REP, kp->input->evbit);
+
+ kp->input->keycode = kp->keycodes;
+ kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE;
+ kp->input->keycodesize = sizeof(kp->keycodes);
+ kp->input->open = pmic8xxx_kp_open;
+ kp->input->close = pmic8xxx_kp_close;
+
+ matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT,
+ kp->input->keycode, kp->input->keybit);
+
+ input_set_capability(kp->input, EV_MSC, MSC_SCAN);
+ input_set_drvdata(kp->input, kp);
+
+ /* initialize keypad state */
+ memset(kp->keystate, 0xff, sizeof(kp->keystate));
+ memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate));
+
+ rc = pmic8xxx_kpd_init(kp);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "unable to initialize keypad controller\n");
+ goto err_get_irq;
+ }
+
+ rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start,
+ pdata->num_cols, kp, &kypd_sns);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "unable to configure keypad sense lines\n");
+ goto err_gpio_config;
+ }
+
+ rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start,
+ pdata->num_rows, kp, &kypd_drv);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "unable to configure keypad drive lines\n");
+ goto err_gpio_config;
+ }
+
+ rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq,
+ IRQF_TRIGGER_RISING, "pmic-keypad", kp);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to request keypad sense irq\n");
+ goto err_get_irq;
+ }
+
+ rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq,
+ IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
+ goto err_req_stuck_irq;
+ }
+
+ rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
+ goto err_pmic_reg_read;
+ }
+
+ kp->ctrl_reg = ctrl_val;
+
+ rc = input_register_device(kp->input);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "unable to register keypad input device\n");
+ goto err_pmic_reg_read;
+ }
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+ return 0;
+
+err_pmic_reg_read:
+ free_irq(kp->key_stuck_irq, NULL);
+err_req_stuck_irq:
+ free_irq(kp->key_sense_irq, NULL);
+err_gpio_config:
+err_get_irq:
+ input_free_device(kp->input);
+err_alloc_device:
+ platform_set_drvdata(pdev, NULL);
+ kfree(kp);
+ return rc;
+}
+
+static int __devexit pmic8xxx_kp_remove(struct platform_device *pdev)
+{
+ struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+ free_irq(kp->key_stuck_irq, NULL);
+ free_irq(kp->key_sense_irq, NULL);
+ input_unregister_device(kp->input);
+ kfree(kp);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pmic8xxx_kp_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = kp->input;
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(kp->key_sense_irq);
+ } else {
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ pmic8xxx_kp_disable(kp);
+
+ mutex_unlock(&input_dev->mutex);
+ }
+
+ return 0;
+}
+
+static int pmic8xxx_kp_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pmic8xxx_kp *kp = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = kp->input;
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(kp->key_sense_irq);
+ } else {
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ pmic8xxx_kp_enable(kp);
+
+ mutex_unlock(&input_dev->mutex);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops,
+ pmic8xxx_kp_suspend, pmic8xxx_kp_resume);
+
+static struct platform_driver pmic8xxx_kp_driver = {
+ .probe = pmic8xxx_kp_probe,
+ .remove = __devexit_p(pmic8xxx_kp_remove),
+ .driver = {
+ .name = PM8XXX_KEYPAD_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pm8xxx_kp_pm_ops,
+ },
+};
+
+static int __init pmic8xxx_kp_init(void)
+{
+ return platform_driver_register(&pmic8xxx_kp_driver);
+}
+module_init(pmic8xxx_kp_init);
+
+static void __exit pmic8xxx_kp_exit(void)
+{
+ platform_driver_unregister(&pmic8xxx_kp_driver);
+}
+module_exit(pmic8xxx_kp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8XXX keypad driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8xxx_keypad");
+MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index f9cf088..45dc6aa 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -330,6 +330,17 @@
To compile this driver as a module, choose M here: the module will be
called pwm-beeper.
+config INPUT_PMIC8XXX_PWRKEY
+ tristate "PMIC8XXX power key support"
+ depends on MFD_PM8XXX
+ help
+ Say Y here if you want support for the PMIC8XXX power key.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pmic8xxx-pwrkey.
+
config INPUT_GPIO_ROTARY_ENCODER
tristate "Rotary encoders connected to GPIO pins"
depends on GPIOLIB && GENERIC_GPIO
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index e3f7984..38efb2c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
+obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
new file mode 100644
index 0000000..97e07e7
--- /dev/null
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -0,0 +1,231 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/log2.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/input/pmic8xxx-pwrkey.h>
+
+#define PON_CNTL_1 0x1C
+#define PON_CNTL_PULL_UP BIT(7)
+#define PON_CNTL_TRIG_DELAY_MASK (0x7)
+
+/**
+ * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
+ * @key_press_irq: key press irq number
+ */
+struct pmic8xxx_pwrkey {
+ struct input_dev *pwr;
+ int key_press_irq;
+};
+
+static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)
+{
+ struct pmic8xxx_pwrkey *pwrkey = _pwrkey;
+
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ input_sync(pwrkey->pwr);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey)
+{
+ struct pmic8xxx_pwrkey *pwrkey = _pwrkey;
+
+ input_report_key(pwrkey->pwr, KEY_POWER, 0);
+ input_sync(pwrkey->pwr);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pmic8xxx_pwrkey_suspend(struct device *dev)
+{
+ struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(pwrkey->key_press_irq);
+
+ return 0;
+}
+
+static int pmic8xxx_pwrkey_resume(struct device *dev)
+{
+ struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(pwrkey->key_press_irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
+ pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
+
+static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev)
+{
+ struct input_dev *pwr;
+ int key_release_irq = platform_get_irq(pdev, 0);
+ int key_press_irq = platform_get_irq(pdev, 1);
+ int err;
+ unsigned int delay;
+ u8 pon_cntl;
+ struct pmic8xxx_pwrkey *pwrkey;
+ const struct pm8xxx_pwrkey_platform_data *pdata = mfd_get_data(pdev);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "power key platform data not supplied\n");
+ return -EINVAL;
+ }
+
+ if (pdata->kpd_trigger_delay_us > 62500) {
+ dev_err(&pdev->dev, "invalid power key trigger delay\n");
+ return -EINVAL;
+ }
+
+ pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);
+ if (!pwrkey)
+ return -ENOMEM;
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ err = -ENOMEM;
+ goto free_pwrkey;
+ }
+
+ input_set_capability(pwr, EV_KEY, KEY_POWER);
+
+ pwr->name = "pmic8xxx_pwrkey";
+ pwr->phys = "pmic8xxx_pwrkey/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
+ delay = 1 + ilog2(delay);
+
+ err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
+ goto free_input_dev;
+ }
+
+ pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
+ pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
+ if (pdata->pull_up)
+ pon_cntl |= PON_CNTL_PULL_UP;
+ else
+ pon_cntl &= ~PON_CNTL_PULL_UP;
+
+ err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
+ goto free_input_dev;
+ }
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power key: %d\n", err);
+ goto free_input_dev;
+ }
+
+ pwrkey->key_press_irq = key_press_irq;
+ pwrkey->pwr = pwr;
+
+ platform_set_drvdata(pdev, pwrkey);
+
+ err = request_irq(key_press_irq, pwrkey_press_irq,
+ IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);
+ if (err < 0) {
+ dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+ key_press_irq, err);
+ goto unreg_input_dev;
+ }
+
+ err = request_irq(key_release_irq, pwrkey_release_irq,
+ IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey);
+ if (err < 0) {
+ dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+ key_release_irq, err);
+
+ goto free_press_irq;
+ }
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+ return 0;
+
+free_press_irq:
+ free_irq(key_press_irq, NULL);
+unreg_input_dev:
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_device(pwr);
+ pwr = NULL;
+free_input_dev:
+ input_free_device(pwr);
+free_pwrkey:
+ kfree(pwrkey);
+ return err;
+}
+
+static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev)
+{
+ struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev);
+ int key_release_irq = platform_get_irq(pdev, 0);
+ int key_press_irq = platform_get_irq(pdev, 1);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ free_irq(key_press_irq, pwrkey);
+ free_irq(key_release_irq, pwrkey);
+ input_unregister_device(pwrkey->pwr);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pwrkey);
+
+ return 0;
+}
+
+static struct platform_driver pmic8xxx_pwrkey_driver = {
+ .probe = pmic8xxx_pwrkey_probe,
+ .remove = __devexit_p(pmic8xxx_pwrkey_remove),
+ .driver = {
+ .name = PM8XXX_PWRKEY_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pm8xxx_pwr_key_pm_ops,
+ },
+};
+
+static int __init pmic8xxx_pwrkey_init(void)
+{
+ return platform_driver_register(&pmic8xxx_pwrkey_driver);
+}
+module_init(pmic8xxx_pwrkey_init);
+
+static void __exit pmic8xxx_pwrkey_exit(void)
+{
+ platform_driver_unregister(&pmic8xxx_pwrkey_driver);
+}
+module_exit(pmic8xxx_pwrkey_exit);
+
+MODULE_ALIAS("platform:pmic8xxx_pwrkey");
+MODULE_DESCRIPTION("PMIC8XXX Power Key driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index 6a11694..014dd4a 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -29,7 +29,6 @@
#include <linux/workqueue.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/twl4030-codec.h>
-#include <linux/mfd/core.h>
#include <linux/input.h>
#include <linux/slab.h>
@@ -197,7 +196,7 @@
static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
{
- struct twl4030_codec_vibra_data *pdata = mfd_get_data(pdev);
+ struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;
struct vibra_info *info;
int ret;
diff --git a/drivers/isdn/hardware/eicon/divasfunc.c b/drivers/isdn/hardware/eicon/divasfunc.c
index d36a4c0..0bbee78 100644
--- a/drivers/isdn/hardware/eicon/divasfunc.c
+++ b/drivers/isdn/hardware/eicon/divasfunc.c
@@ -113,9 +113,8 @@
static void start_dbg(void)
{
DbgRegister("DIVAS", DRIVERRELEASE_DIVAS, (debugmask) ? debugmask : DBG_DEFAULT);
- DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s]-%s-%s)",
- DIVA_BUILD, diva_xdi_common_code_build, __DATE__,
- __TIME__))
+ DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s])",
+ DIVA_BUILD, diva_xdi_common_code_build))
}
/*
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1d027b4..23f0d5e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -389,6 +389,16 @@
and 5Big Network v2 boards. The LEDs are wired to a CPLD and are
controlled through a GPIO extension bus.
+config LEDS_ASIC3
+ bool "LED support for the HTC ASIC3"
+ depends on MFD_ASIC3
+ default y
+ help
+ This option enables support for the LEDs on the HTC ASIC3. The HTC
+ ASIC3 LED GPIOs are inputs, not outputs, thus the leds-gpio driver
+ cannot be used. This driver supports hardware blinking with an on+off
+ period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700.
+
config LEDS_TRIGGERS
bool "LED Trigger support"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index bccb96c..bbfd2e3 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -42,6 +42,7 @@
obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
+obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 416def8..0d4c166 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -17,7 +17,6 @@
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/mfd/core.h>
#include <linux/mfd/88pm860x.h>
#define LED_PWM_SHIFT (3)
@@ -171,7 +170,6 @@
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_led_pdata *pdata;
struct pm860x_led *data;
- struct mfd_cell *cell;
struct resource *res;
int ret;
@@ -181,10 +179,7 @@
return -EINVAL;
}
- cell = pdev->dev.platform_data;
- if (cell == NULL)
- return -ENODEV;
- pdata = cell->mfd_data;
+ pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data!\n");
return -EINVAL;
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c
new file mode 100644
index 0000000..22f847c
--- /dev/null
+++ b/drivers/leds/leds-asic3.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Paul Parsons <lost.distance@yahoo.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/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/asic3.h>
+#include <linux/mfd/core.h>
+
+/*
+ * The HTC ASIC3 LED GPIOs are inputs, not outputs.
+ * Hence we turn the LEDs on/off via the TimeBase register.
+ */
+
+/*
+ * When TimeBase is 4 the clock resolution is about 32Hz.
+ * This driver supports hardware blinking with an on+off
+ * period from 62ms (2 clocks) to 125s (4000 clocks).
+ */
+#define MS_TO_CLK(ms) DIV_ROUND_CLOSEST(((ms)*1024), 32000)
+#define CLK_TO_MS(clk) (((clk)*32000)/1024)
+#define MAX_CLK 4000 /* Fits into 12-bit Time registers */
+#define MAX_MS CLK_TO_MS(MAX_CLK)
+
+static const unsigned int led_n_base[ASIC3_NUM_LEDS] = {
+ [0] = ASIC3_LED_0_Base,
+ [1] = ASIC3_LED_1_Base,
+ [2] = ASIC3_LED_2_Base,
+};
+
+static void brightness_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct platform_device *pdev = to_platform_device(cdev->dev->parent);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+ u32 timebase;
+ unsigned int base;
+
+ timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4);
+
+ base = led_n_base[cell->id];
+ asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32);
+ asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32);
+ asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0);
+ asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase);
+}
+
+static int blink_set(struct led_classdev *cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct platform_device *pdev = to_platform_device(cdev->dev->parent);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+ u32 on;
+ u32 off;
+ unsigned int base;
+
+ if (*delay_on > MAX_MS || *delay_off > MAX_MS)
+ return -EINVAL;
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ /* If both are zero then a sensible default should be chosen */
+ on = MS_TO_CLK(500);
+ off = MS_TO_CLK(500);
+ } else {
+ on = MS_TO_CLK(*delay_on);
+ off = MS_TO_CLK(*delay_off);
+ if ((on + off) > MAX_CLK)
+ return -EINVAL;
+ }
+
+ base = led_n_base[cell->id];
+ asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off));
+ asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on);
+ asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0);
+ asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4));
+
+ *delay_on = CLK_TO_MS(on);
+ *delay_off = CLK_TO_MS(off);
+
+ return 0;
+}
+
+static int __devinit asic3_led_probe(struct platform_device *pdev)
+{
+ struct asic3_led *led = pdev->dev.platform_data;
+ int ret;
+
+ ret = mfd_cell_enable(pdev);
+ if (ret < 0)
+ goto ret0;
+
+ led->cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
+ if (!led->cdev) {
+ ret = -ENOMEM;
+ goto ret1;
+ }
+
+ led->cdev->name = led->name;
+ led->cdev->default_trigger = led->default_trigger;
+ led->cdev->brightness_set = brightness_set;
+ led->cdev->blink_set = blink_set;
+
+ ret = led_classdev_register(&pdev->dev, led->cdev);
+ if (ret < 0)
+ goto ret2;
+
+ return 0;
+
+ret2:
+ kfree(led->cdev);
+ret1:
+ (void) mfd_cell_disable(pdev);
+ret0:
+ return ret;
+}
+
+static int __devexit asic3_led_remove(struct platform_device *pdev)
+{
+ struct asic3_led *led = pdev->dev.platform_data;
+
+ led_classdev_unregister(led->cdev);
+
+ kfree(led->cdev);
+
+ return mfd_cell_disable(pdev);
+}
+
+static struct platform_driver asic3_led_driver = {
+ .probe = asic3_led_probe,
+ .remove = __devexit_p(asic3_led_remove),
+ .driver = {
+ .name = "leds-asic3",
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_ALIAS("platform:leds-asic3");
+
+static int __init asic3_led_init(void)
+{
+ return platform_driver_register(&asic3_led_driver);
+}
+
+static void __exit asic3_led_exit(void)
+{
+ platform_driver_unregister(&asic3_led_driver);
+}
+
+module_init(asic3_led_init);
+module_exit(asic3_led_exit);
+
+MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
+MODULE_DESCRIPTION("HTC ASIC3 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index 126ca79..f369e56 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -22,7 +22,6 @@
#include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/mc13783.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
struct mc13783_led {
@@ -184,7 +183,7 @@
static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
int ret = 0;
int reg = 0;
@@ -265,7 +264,7 @@
static int __devinit mc13783_led_probe(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct mc13783_led_platform_data *led_cur;
struct mc13783_led *led, *led_dat;
int ret, i;
@@ -352,7 +351,7 @@
static int __devexit mc13783_led_remove(struct platform_device *pdev)
{
- struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct mc13783_led *led = platform_get_drvdata(pdev);
struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
int i;
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index 2d8b404..b2b0c45 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -20,6 +20,7 @@
*/
#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -49,11 +50,12 @@
#define UNSET (-1U)
-#define DM1105_BOARD_NOAUTO UNSET
-#define DM1105_BOARD_UNKNOWN 0
-#define DM1105_BOARD_DVBWORLD_2002 1
-#define DM1105_BOARD_DVBWORLD_2004 2
-#define DM1105_BOARD_AXESS_DM05 3
+#define DM1105_BOARD_NOAUTO UNSET
+#define DM1105_BOARD_UNKNOWN 0
+#define DM1105_BOARD_DVBWORLD_2002 1
+#define DM1105_BOARD_DVBWORLD_2004 2
+#define DM1105_BOARD_AXESS_DM05 3
+#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4
/* ----------------------------------------------- */
/*
@@ -157,22 +159,38 @@
#define DM1105_MAX 0x04
#define DRIVER_NAME "dm1105"
+#define DM1105_I2C_GPIO_NAME "dm1105-gpio"
#define DM1105_DMA_PACKETS 47
#define DM1105_DMA_PACKET_LENGTH (128*4)
#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS)
+/* */
+#define GPIO08 (1 << 8)
+#define GPIO13 (1 << 13)
+#define GPIO14 (1 << 14)
+#define GPIO15 (1 << 15)
+#define GPIO16 (1 << 16)
+#define GPIO17 (1 << 17)
+#define GPIO_ALL 0x03ffff
+
/* GPIO's for LNB power control */
-#define DM1105_LNB_MASK 0x00000000
-#define DM1105_LNB_OFF 0x00020000
-#define DM1105_LNB_13V 0x00010100
-#define DM1105_LNB_18V 0x00000100
+#define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13))
+#define DM1105_LNB_OFF GPIO17
+#define DM1105_LNB_13V (GPIO16 | GPIO08)
+#define DM1105_LNB_18V GPIO08
/* GPIO's for LNB power control for Axess DM05 */
-#define DM05_LNB_MASK 0x00000000
-#define DM05_LNB_OFF 0x00020000/* actually 13v */
-#define DM05_LNB_13V 0x00020000
-#define DM05_LNB_18V 0x00030000
+#define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13))
+#define DM05_LNB_OFF GPIO17/* actually 13v */
+#define DM05_LNB_13V GPIO17
+#define DM05_LNB_18V (GPIO17 | GPIO16)
+
+/* GPIO's for LNB power control for unbranded with I2C on GPIO */
+#define UNBR_LNB_MASK (GPIO17 | GPIO16)
+#define UNBR_LNB_OFF 0
+#define UNBR_LNB_13V GPIO17
+#define UNBR_LNB_18V (GPIO17 | GPIO16)
static unsigned int card[] = {[0 ... 3] = UNSET };
module_param_array(card, int, NULL, 0444);
@@ -187,7 +205,11 @@
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct dm1105_board {
- char *name;
+ char *name;
+ struct {
+ u32 mask, off, v13, v18;
+ } lnb;
+ u32 gpio_scl, gpio_sda;
};
struct dm1105_subid {
@@ -199,15 +221,50 @@
static const struct dm1105_board dm1105_boards[] = {
[DM1105_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
+ .lnb = {
+ .mask = DM1105_LNB_MASK,
+ .off = DM1105_LNB_OFF,
+ .v13 = DM1105_LNB_13V,
+ .v18 = DM1105_LNB_18V,
+ },
},
[DM1105_BOARD_DVBWORLD_2002] = {
.name = "DVBWorld PCI 2002",
+ .lnb = {
+ .mask = DM1105_LNB_MASK,
+ .off = DM1105_LNB_OFF,
+ .v13 = DM1105_LNB_13V,
+ .v18 = DM1105_LNB_18V,
+ },
},
[DM1105_BOARD_DVBWORLD_2004] = {
.name = "DVBWorld PCI 2004",
+ .lnb = {
+ .mask = DM1105_LNB_MASK,
+ .off = DM1105_LNB_OFF,
+ .v13 = DM1105_LNB_13V,
+ .v18 = DM1105_LNB_18V,
+ },
},
[DM1105_BOARD_AXESS_DM05] = {
.name = "Axess/EasyTv DM05",
+ .lnb = {
+ .mask = DM05_LNB_MASK,
+ .off = DM05_LNB_OFF,
+ .v13 = DM05_LNB_13V,
+ .v18 = DM05_LNB_18V,
+ },
+ },
+ [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = {
+ .name = "Unbranded DM1105 with i2c on GPIOs",
+ .lnb = {
+ .mask = UNBR_LNB_MASK,
+ .off = UNBR_LNB_OFF,
+ .v13 = UNBR_LNB_13V,
+ .v18 = UNBR_LNB_18V,
+ },
+ .gpio_scl = GPIO14,
+ .gpio_sda = GPIO13,
},
};
@@ -293,6 +350,8 @@
/* i2c */
struct i2c_adapter i2c_adap;
+ struct i2c_adapter i2c_bb_adap;
+ struct i2c_algo_bit_data i2c_bit;
/* irq */
struct work_struct work;
@@ -328,6 +387,103 @@
#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit))
#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0)
+/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines,
+ so we can use only 3 GPIO's from GPIO15 to GPIO17.
+ Here I don't check whether HOST is enebled as it is not implemented yet.
+ */
+static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask)
+{
+ if (mask & 0xfffc0000)
+ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+ if (mask & 0x0003ffff)
+ dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff);
+
+}
+
+static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask)
+{
+ if (mask & 0xfffc0000)
+ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+ if (mask & 0x0003ffff)
+ dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff);
+
+}
+
+static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val)
+{
+ if (mask & 0xfffc0000)
+ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+ if (mask & 0x0003ffff)
+ dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val);
+
+}
+
+static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask)
+{
+ if (mask & 0xfffc0000)
+ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+ if (mask & 0x0003ffff)
+ return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff;
+
+ return 0;
+}
+
+static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput)
+{
+ if (mask & 0xfffc0000)
+ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__);
+
+ if ((mask & 0x0003ffff) && asoutput)
+ dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff);
+ else if ((mask & 0x0003ffff) && !asoutput)
+ dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff);
+
+}
+
+static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state)
+{
+ if (state)
+ dm1105_gpio_enable(dev, line, 0);
+ else {
+ dm1105_gpio_enable(dev, line, 1);
+ dm1105_gpio_clear(dev, line);
+ }
+}
+
+static void dm1105_setsda(void *data, int state)
+{
+ struct dm1105_dev *dev = data;
+
+ dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state);
+}
+
+static void dm1105_setscl(void *data, int state)
+{
+ struct dm1105_dev *dev = data;
+
+ dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state);
+}
+
+static int dm1105_getsda(void *data)
+{
+ struct dm1105_dev *dev = data;
+
+ return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda)
+ ? 1 : 0;
+}
+
+static int dm1105_getscl(void *data)
+{
+ struct dm1105_dev *dev = data;
+
+ return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl)
+ ? 1 : 0;
+}
+
static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, int num)
{
@@ -436,31 +592,20 @@
static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
struct dm1105_dev *dev = frontend_to_dm1105_dev(fe);
- u32 lnb_mask, lnb_13v, lnb_18v, lnb_off;
- switch (dev->boardnr) {
- case DM1105_BOARD_AXESS_DM05:
- lnb_mask = DM05_LNB_MASK;
- lnb_off = DM05_LNB_OFF;
- lnb_13v = DM05_LNB_13V;
- lnb_18v = DM05_LNB_18V;
- break;
- case DM1105_BOARD_DVBWORLD_2002:
- case DM1105_BOARD_DVBWORLD_2004:
- default:
- lnb_mask = DM1105_LNB_MASK;
- lnb_off = DM1105_LNB_OFF;
- lnb_13v = DM1105_LNB_13V;
- lnb_18v = DM1105_LNB_18V;
- }
-
- dm_writel(DM1105_GPIOCTR, lnb_mask);
+ dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1);
if (voltage == SEC_VOLTAGE_18)
- dm_writel(DM1105_GPIOVAL, lnb_18v);
+ dm1105_gpio_andor(dev,
+ dm1105_boards[dev->boardnr].lnb.mask,
+ dm1105_boards[dev->boardnr].lnb.v18);
else if (voltage == SEC_VOLTAGE_13)
- dm_writel(DM1105_GPIOVAL, lnb_13v);
+ dm1105_gpio_andor(dev,
+ dm1105_boards[dev->boardnr].lnb.mask,
+ dm1105_boards[dev->boardnr].lnb.v13);
else
- dm_writel(DM1105_GPIOVAL, lnb_off);
+ dm1105_gpio_andor(dev,
+ dm1105_boards[dev->boardnr].lnb.mask,
+ dm1105_boards[dev->boardnr].lnb.off);
return 0;
}
@@ -708,6 +853,38 @@
int ret;
switch (dev->boardnr) {
+ case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO:
+ dm1105_gpio_enable(dev, GPIO15, 1);
+ dm1105_gpio_clear(dev, GPIO15);
+ msleep(100);
+ dm1105_gpio_set(dev, GPIO15);
+ msleep(200);
+ dev->fe = dvb_attach(
+ stv0299_attach, &sharp_z0194a_config,
+ &dev->i2c_bb_adap);
+ if (dev->fe) {
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
+ dvb_attach(dvb_pll_attach, dev->fe, 0x60,
+ &dev->i2c_bb_adap, DVB_PLL_OPERA1);
+ break;
+ }
+
+ dev->fe = dvb_attach(
+ stv0288_attach, &earda_config,
+ &dev->i2c_bb_adap);
+ if (dev->fe) {
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
+ dvb_attach(stb6000_attach, dev->fe, 0x61,
+ &dev->i2c_bb_adap);
+ break;
+ }
+
+ dev->fe = dvb_attach(
+ si21xx_attach, &serit_config,
+ &dev->i2c_bb_adap);
+ if (dev->fe)
+ dev->fe->ops.set_voltage = dm1105_set_voltage;
+ break;
case DM1105_BOARD_DVBWORLD_2004:
dev->fe = dvb_attach(
cx24116_attach, &serit_sp2633_config,
@@ -870,11 +1047,32 @@
if (ret < 0)
goto err_dm1105_hw_exit;
+ i2c_set_adapdata(&dev->i2c_bb_adap, dev);
+ strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME);
+ dev->i2c_bb_adap.owner = THIS_MODULE;
+ dev->i2c_bb_adap.dev.parent = &pdev->dev;
+ dev->i2c_bb_adap.algo_data = &dev->i2c_bit;
+ dev->i2c_bit.data = dev;
+ dev->i2c_bit.setsda = dm1105_setsda;
+ dev->i2c_bit.setscl = dm1105_setscl;
+ dev->i2c_bit.getsda = dm1105_getsda;
+ dev->i2c_bit.getscl = dm1105_getscl;
+ dev->i2c_bit.udelay = 10;
+ dev->i2c_bit.timeout = 10;
+
+ /* Raise SCL and SDA */
+ dm1105_setsda(dev, 1);
+ dm1105_setscl(dev, 1);
+
+ ret = i2c_bit_add_bus(&dev->i2c_bb_adap);
+ if (ret < 0)
+ goto err_i2c_del_adapter;
+
/* dvb */
ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME,
THIS_MODULE, &pdev->dev, adapter_nr);
if (ret < 0)
- goto err_i2c_del_adapter;
+ goto err_i2c_del_adapters;
dvb_adapter = &dev->dvb_adapter;
@@ -952,6 +1150,8 @@
dvb_dmx_release(dvbdemux);
err_dvb_unregister_adapter:
dvb_unregister_adapter(dvb_adapter);
+err_i2c_del_adapters:
+ i2c_del_adapter(&dev->i2c_bb_adap);
err_i2c_del_adapter:
i2c_del_adapter(&dev->i2c_adap);
err_dm1105_hw_exit:
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
index f36f471..37b1469 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -207,17 +207,6 @@
rbuff, sizeof(rbuff));
return ret;
}
-static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u32 keypress)
-{
- struct dvb_usb_device *d = adap->dev;
-
- deb_info(1, "INT Key Keypress =%04x", keypress);
-
- if (keypress > 0)
- rc_keydown(d->rc_dev, keypress, 0);
-
- return 0;
-}
static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out)
{
@@ -256,6 +245,7 @@
struct lme2510_state *st = adap->dev->priv;
static u8 *ibuf, *rbuf;
int i = 0, offset;
+ u32 key;
switch (lme_urb->status) {
case 0:
@@ -282,10 +272,16 @@
switch (ibuf[0]) {
case 0xaa:
- debug_data_snipet(1, "INT Remote data snipet in", ibuf);
- lme2510_remote_keypress(adap,
- (u32)(ibuf[2] << 24) + (ibuf[3] << 16) +
- (ibuf[4] << 8) + ibuf[5]);
+ debug_data_snipet(1, "INT Remote data snipet", ibuf);
+ if ((ibuf[4] + ibuf[5]) == 0xff) {
+ key = ibuf[5];
+ key += (ibuf[3] > 0)
+ ? (ibuf[3] ^ 0xff) << 8 : 0;
+ key += (ibuf[2] ^ 0xff) << 16;
+ deb_info(1, "INT Key =%08x", key);
+ if (adap->dev->rc_dev != NULL)
+ rc_keydown(adap->dev->rc_dev, key, 0);
+ }
break;
case 0xbb:
switch (st->tuner_config) {
@@ -691,45 +687,6 @@
return (ret < 0) ? -ENODEV : 0;
}
-static int lme2510_int_service(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct rc_dev *rc;
- int ret;
-
- info("STA Configuring Remote");
-
- rc = rc_allocate_device();
- if (!rc)
- return -ENOMEM;
-
- usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
- strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
-
- rc->input_name = "LME2510 Remote Control";
- rc->input_phys = d->rc_phys;
- rc->map_name = RC_MAP_LME2510;
- rc->driver_name = "LME 2510";
- usb_to_input_id(d->udev, &rc->input_id);
-
- ret = rc_register_device(rc);
- if (ret) {
- rc_free_device(rc);
- return ret;
- }
- d->rc_dev = rc;
-
- /* Start the Interrupt */
- ret = lme2510_int_read(adap);
- if (ret < 0) {
- rc_unregister_device(rc);
- info("INT Unable to start Interrupt Service");
- return -ENODEV;
- }
-
- return 0;
-}
-
static u8 check_sum(u8 *p, u8 len)
{
u8 sum = 0;
@@ -831,7 +788,7 @@
cold_fw = !cold;
- if (udev->descriptor.idProduct == 0x1122) {
+ if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) {
switch (dvb_usb_lme2510_firmware) {
default:
dvb_usb_lme2510_firmware = TUNER_S0194;
@@ -1053,8 +1010,11 @@
end: if (ret) {
- kfree(adap->fe);
- adap->fe = NULL;
+ if (adap->fe) {
+ dvb_frontend_detach(adap->fe);
+ adap->fe = NULL;
+ }
+ adap->dev->props.rc.core.rc_codes = NULL;
return -ENODEV;
}
@@ -1097,8 +1057,12 @@
return -ENODEV;
}
- /* Start the Interrupt & Remote*/
- ret = lme2510_int_service(adap);
+ /* Start the Interrupt*/
+ ret = lme2510_int_read(adap);
+ if (ret < 0) {
+ info("INT Unable to start Interrupt Service");
+ return -ENODEV;
+ }
return ret;
}
@@ -1204,6 +1168,12 @@
}
}
},
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "LME2510 Remote Control",
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_LME2510,
+ },
.power_ctrl = lme2510_powerup,
.identify_state = lme2510_identify_state,
.i2c_algo = &lme2510_i2c_algo,
@@ -1246,6 +1216,12 @@
}
}
},
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "LME2510 Remote Control",
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_LME2510,
+ },
.power_ctrl = lme2510_powerup,
.identify_state = lme2510_identify_state,
.i2c_algo = &lme2510_i2c_algo,
@@ -1269,19 +1245,21 @@
adap->feedcount = 0;
}
- if (st->lme_urb != NULL) {
+ if (st->usb_buffer != NULL) {
st->i2c_talk_onoff = 1;
st->signal_lock = 0;
st->signal_level = 0;
st->signal_sn = 0;
buffer = st->usb_buffer;
+ }
+
+ if (st->lme_urb != NULL) {
usb_kill_urb(st->lme_urb);
usb_free_coherent(d->udev, 5000, st->buffer,
st->lme_urb->transfer_dma);
info("Interrupt Service Stopped");
- rc_unregister_device(d->rc_dev);
- info("Remote Stopped");
}
+
return buffer;
}
@@ -1293,7 +1271,8 @@
if (d != NULL) {
usb_buffer = lme2510_exit_int(d);
dvb_usb_device_exit(intf);
- kfree(usb_buffer);
+ if (usb_buffer != NULL)
+ kfree(usb_buffer);
}
}
@@ -1327,5 +1306,5 @@
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
-MODULE_VERSION("1.86");
+MODULE_VERSION("1.88");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c
index 2da55ec..d70eee0 100644
--- a/drivers/media/dvb/frontends/stb0899_algo.c
+++ b/drivers/media/dvb/frontends/stb0899_algo.c
@@ -23,7 +23,7 @@
#include "stb0899_priv.h"
#include "stb0899_reg.h"
-inline u32 stb0899_do_div(u64 n, u32 d)
+static inline u32 stb0899_do_div(u64 n, u32 d)
{
/* wrap do_div() for ease of use */
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c
index 1742056..53c7d8f 100644
--- a/drivers/media/dvb/frontends/tda8261.c
+++ b/drivers/media/dvb/frontends/tda8261.c
@@ -224,7 +224,6 @@
}
EXPORT_SYMBOL(tda8261_attach);
-MODULE_PARM_DESC(verbose, "Set verbosity level");
MODULE_AUTHOR("Manu Abraham");
MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index 5c2a905..e83e840 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -412,8 +412,7 @@
goto err_out_free_region;
}
- v4l2_info(v4l2_dev, "version " DRIVER_VERSION
- " time " __TIME__ " " __DATE__ "\n");
+ v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n",
dev->io);
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index 1e3a8dd..a185610 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -21,7 +21,6 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2c.h>
@@ -149,7 +148,7 @@
static int __devinit timbradio_probe(struct platform_device *pdev)
{
- struct timb_radio_platform_data *pdata = mfd_get_data(pdev);
+ struct timb_radio_platform_data *pdata = pdev->dev.platform_data;
struct timbradio *tr;
int err;
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index e2550dc..459f727 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1382,7 +1382,7 @@
switch (ctrl->id) {
case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- ctrl->val = wl1273_fm_get_tx_ctune(radio);
+ ctrl->cur.val = wl1273_fm_get_tx_ctune(radio);
break;
default:
@@ -1990,7 +1990,7 @@
static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
{
- struct wl1273_core **core = mfd_get_data(pdev);
+ struct wl1273_core **core = pdev->dev.platform_data;
struct wl1273_device *radio;
struct v4l2_ctrl *ctrl;
int r = 0;
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index d50e5ac..8701072 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -191,7 +191,7 @@
switch (ctrl->id) {
case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- ctrl->val = fm_tx_get_tune_cap_val(fmdev);
+ ctrl->cur.val = fm_tx_get_tune_cap_val(fmdev);
break;
default:
fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id);
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 154c337..7d4bbc2 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -148,6 +148,18 @@
To compile this driver as a module, choose M here: the
module will be called ite-cir.
+config IR_FINTEK
+ tristate "Fintek Consumer Infrared Transceiver"
+ depends on PNP
+ depends on RC_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receiver
+ /transciever made by Fintek. This chip is found on assorted
+ Jetway motherboards (and of course, possibly others).
+
+ To compile this driver as a module, choose M here: the
+ module will be called fintek-cir.
+
config IR_NUVOTON
tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
depends on PNP
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 1f90a21..52830e5 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -16,6 +16,7 @@
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
obj-$(CONFIG_IR_REDRAT3) += redrat3.o
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
new file mode 100644
index 0000000..8fa539d
--- /dev/null
+++ b/drivers/media/rc/fintek-cir.c
@@ -0,0 +1,684 @@
+/*
+ * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
+ *
+ * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
+ *
+ * Special thanks to Fintek for providing hardware and spec sheets.
+ * This driver is based upon the nuvoton, ite and ene drivers for
+ * similar hardware.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include <linux/pci_ids.h>
+
+#include "fintek-cir.h"
+
+/* write val to config reg */
+static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+ fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",
+ __func__, reg, val, fintek->cr_ip, fintek->cr_dp);
+ outb(reg, fintek->cr_ip);
+ outb(val, fintek->cr_dp);
+}
+
+/* read val from config reg */
+static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg)
+{
+ u8 val;
+
+ outb(reg, fintek->cr_ip);
+ val = inb(fintek->cr_dp);
+
+ fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",
+ __func__, reg, val, fintek->cr_ip, fintek->cr_dp);
+ return val;
+}
+
+/* update config register bit without changing other bits */
+static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+ u8 tmp = fintek_cr_read(fintek, reg) | val;
+ fintek_cr_write(fintek, tmp, reg);
+}
+
+/* clear config register bit without changing other bits */
+static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+ u8 tmp = fintek_cr_read(fintek, reg) & ~val;
+ fintek_cr_write(fintek, tmp, reg);
+}
+
+/* enter config mode */
+static inline void fintek_config_mode_enable(struct fintek_dev *fintek)
+{
+ /* Enabling Config Mode explicitly requires writing 2x */
+ outb(CONFIG_REG_ENABLE, fintek->cr_ip);
+ outb(CONFIG_REG_ENABLE, fintek->cr_ip);
+}
+
+/* exit config mode */
+static inline void fintek_config_mode_disable(struct fintek_dev *fintek)
+{
+ outb(CONFIG_REG_DISABLE, fintek->cr_ip);
+}
+
+/*
+ * When you want to address a specific logical device, write its logical
+ * device number to GCR_LOGICAL_DEV_NO
+ */
+static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev)
+{
+ fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO);
+}
+
+/* write val to cir config register */
+static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset)
+{
+ outb(val, fintek->cir_addr + offset);
+}
+
+/* read val from cir config register */
+static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
+{
+ u8 val;
+
+ val = inb(fintek->cir_addr + offset);
+
+ return val;
+}
+
+#define pr_reg(text, ...) \
+ printk(KERN_INFO KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+/* dump current cir register contents */
+static void cir_dump_regs(struct fintek_dev *fintek)
+{
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+
+ pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
+ pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
+ (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
+ fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));
+ pr_reg(" * CR CIR IRQ NUM: 0x%x\n",
+ fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
+
+ fintek_config_mode_disable(fintek);
+
+ pr_reg("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
+ pr_reg(" * STATUS: 0x%x\n", fintek_cir_reg_read(fintek, CIR_STATUS));
+ pr_reg(" * CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_CONTROL));
+ pr_reg(" * RX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_RX_DATA));
+ pr_reg(" * TX_CONTROL: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
+ pr_reg(" * TX_DATA: 0x%x\n", fintek_cir_reg_read(fintek, CIR_TX_DATA));
+}
+
+/* detect hardware features */
+static int fintek_hw_detect(struct fintek_dev *fintek)
+{
+ unsigned long flags;
+ u8 chip_major, chip_minor;
+ u8 vendor_major, vendor_minor;
+ u8 portsel, ir_class;
+ u16 vendor;
+ int ret = 0;
+
+ fintek_config_mode_enable(fintek);
+
+ /* Check if we're using config port 0x4e or 0x2e */
+ portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
+ if (portsel == 0xff) {
+ fit_pr(KERN_INFO, "first portsel read was bunk, trying alt");
+ fintek_config_mode_disable(fintek);
+ fintek->cr_ip = CR_INDEX_PORT2;
+ fintek->cr_dp = CR_DATA_PORT2;
+ fintek_config_mode_enable(fintek);
+ portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
+ }
+ fit_dbg("portsel reg: 0x%02x", portsel);
+
+ ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS);
+ fit_dbg("ir_class reg: 0x%02x", ir_class);
+
+ switch (ir_class) {
+ case CLASS_RX_2TX:
+ case CLASS_RX_1TX:
+ fintek->hw_tx_capable = true;
+ break;
+ case CLASS_RX_ONLY:
+ default:
+ fintek->hw_tx_capable = false;
+ break;
+ }
+
+ chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
+ chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
+
+ vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
+ vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
+ vendor = vendor_major << 8 | vendor_minor;
+
+ if (vendor != VENDOR_ID_FINTEK)
+ fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor);
+ else
+ fit_dbg("Read Fintek vendor ID from chip");
+
+ fintek_config_mode_disable(fintek);
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ fintek->chip_major = chip_major;
+ fintek->chip_minor = chip_minor;
+ fintek->chip_vendor = vendor;
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ return ret;
+}
+
+static void fintek_cir_ldev_init(struct fintek_dev *fintek)
+{
+ /* Select CIR logical device and enable */
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+ /* Write allocated CIR address and IRQ information to hardware */
+ fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI);
+ fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO);
+
+ fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL);
+
+ fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)",
+ fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len);
+}
+
+/* enable CIR interrupts */
+static void fintek_enable_cir_irq(struct fintek_dev *fintek)
+{
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
+}
+
+static void fintek_cir_regs_init(struct fintek_dev *fintek)
+{
+ /* clear any and all stray interrupts */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+ /* and finally, enable interrupts */
+ fintek_enable_cir_irq(fintek);
+}
+
+static void fintek_enable_wake(struct fintek_dev *fintek)
+{
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI);
+
+ /* Allow CIR PME's to wake system */
+ fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG);
+ /* Enable CIR PME's */
+ fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG);
+ /* Clear CIR PME status register */
+ fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG);
+ /* Save state */
+ fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG);
+
+ fintek_config_mode_disable(fintek);
+}
+
+static int fintek_cmdsize(u8 cmd, u8 subcmd)
+{
+ int datasize = 0;
+
+ switch (cmd) {
+ case BUF_COMMAND_NULL:
+ if (subcmd == BUF_HW_CMD_HEADER)
+ datasize = 1;
+ break;
+ case BUF_HW_CMD_HEADER:
+ if (subcmd == BUF_CMD_G_REVISION)
+ datasize = 2;
+ break;
+ case BUF_COMMAND_HEADER:
+ switch (subcmd) {
+ case BUF_CMD_S_CARRIER:
+ case BUF_CMD_S_TIMEOUT:
+ case BUF_RSP_PULSE_COUNT:
+ datasize = 2;
+ break;
+ case BUF_CMD_SIG_END:
+ case BUF_CMD_S_TXMASK:
+ case BUF_CMD_S_RXSENSOR:
+ datasize = 1;
+ break;
+ }
+ }
+
+ return datasize;
+}
+
+/* process ir data stored in driver buffer */
+static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ u8 sample;
+ int i;
+
+ for (i = 0; i < fintek->pkts; i++) {
+ sample = fintek->buf[i];
+ switch (fintek->parser_state) {
+ case CMD_HEADER:
+ fintek->cmd = sample;
+ if ((fintek->cmd == BUF_COMMAND_HEADER) ||
+ ((fintek->cmd & BUF_COMMAND_MASK) !=
+ BUF_PULSE_BIT)) {
+ fintek->parser_state = SUBCMD;
+ continue;
+ }
+ fintek->rem = (fintek->cmd & BUF_LEN_MASK);
+ fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem);
+ if (fintek->rem)
+ fintek->parser_state = PARSE_IRDATA;
+ else
+ ir_raw_event_reset(fintek->rdev);
+ break;
+ case SUBCMD:
+ fintek->rem = fintek_cmdsize(fintek->cmd, sample);
+ fintek->parser_state = CMD_DATA;
+ break;
+ case CMD_DATA:
+ fintek->rem--;
+ break;
+ case PARSE_IRDATA:
+ fintek->rem--;
+ init_ir_raw_event(&rawir);
+ rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
+ rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK)
+ * CIR_SAMPLE_PERIOD);
+
+ fit_dbg("Storing %s with duration %d",
+ rawir.pulse ? "pulse" : "space",
+ rawir.duration);
+ ir_raw_event_store_with_filter(fintek->rdev, &rawir);
+ break;
+ }
+
+ if ((fintek->parser_state != CMD_HEADER) && !fintek->rem)
+ fintek->parser_state = CMD_HEADER;
+ }
+
+ fintek->pkts = 0;
+
+ fit_dbg("Calling ir_raw_event_handle");
+ ir_raw_event_handle(fintek->rdev);
+}
+
+/* copy data from hardware rx register into driver buffer */
+static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs)
+{
+ unsigned long flags;
+ u8 sample, status;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+
+ /*
+ * We must read data from CIR_RX_DATA until the hardware IR buffer
+ * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in
+ * the CIR_STATUS register
+ */
+ do {
+ sample = fintek_cir_reg_read(fintek, CIR_RX_DATA);
+ fit_dbg("%s: sample: 0x%02x", __func__, sample);
+
+ fintek->buf[fintek->pkts] = sample;
+ fintek->pkts++;
+
+ status = fintek_cir_reg_read(fintek, CIR_STATUS);
+ if (!(status & CIR_STATUS_IRQ_EN))
+ break;
+ } while (status & rx_irqs);
+
+ fintek_process_rx_ir_data(fintek);
+
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+}
+
+static void fintek_cir_log_irqs(u8 status)
+{
+ fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status,
+ status & CIR_STATUS_IRQ_EN ? " IRQEN" : "",
+ status & CIR_STATUS_TX_FINISH ? " TXF" : "",
+ status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "",
+ status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "",
+ status & CIR_STATUS_RX_RECEIVE ? " RXOK" : "");
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t fintek_cir_isr(int irq, void *data)
+{
+ struct fintek_dev *fintek = data;
+ u8 status, rx_irqs;
+
+ fit_dbg_verbose("%s firing", __func__);
+
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_config_mode_disable(fintek);
+
+ /*
+ * Get IR Status register contents. Write 1 to ack/clear
+ *
+ * bit: reg name - description
+ * 3: TX_FINISH - TX is finished
+ * 2: TX_UNDERRUN - TX underrun
+ * 1: RX_TIMEOUT - RX data timeout
+ * 0: RX_RECEIVE - RX data received
+ */
+ status = fintek_cir_reg_read(fintek, CIR_STATUS);
+ if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) {
+ fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status);
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+ return IRQ_RETVAL(IRQ_NONE);
+ }
+
+ if (debug)
+ fintek_cir_log_irqs(status);
+
+ rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT);
+ if (rx_irqs)
+ fintek_get_rx_ir_data(fintek, rx_irqs);
+
+ /* ack/clear all irq flags we've got */
+ fintek_cir_reg_write(fintek, status, CIR_STATUS);
+
+ fit_dbg_verbose("%s done", __func__);
+ return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void fintek_enable_cir(struct fintek_dev *fintek)
+{
+ /* set IRQ enabled */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
+
+ fintek_config_mode_enable(fintek);
+
+ /* enable the CIR logical device */
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+
+ /* clear all pending interrupts */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+ /* enable interrupts */
+ fintek_enable_cir_irq(fintek);
+}
+
+static void fintek_disable_cir(struct fintek_dev *fintek)
+{
+ fintek_config_mode_enable(fintek);
+
+ /* disable the CIR logical device */
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+}
+
+static int fintek_open(struct rc_dev *dev)
+{
+ struct fintek_dev *fintek = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ fintek_enable_cir(fintek);
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ return 0;
+}
+
+static void fintek_close(struct rc_dev *dev)
+{
+ struct fintek_dev *fintek = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ fintek_disable_cir(fintek);
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+}
+
+/* Allocate memory, probe hardware, and initialize everything */
+static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+ struct fintek_dev *fintek;
+ struct rc_dev *rdev;
+ int ret = -ENOMEM;
+
+ fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL);
+ if (!fintek)
+ return ret;
+
+ /* input device for IR remote (and tx) */
+ rdev = rc_allocate_device();
+ if (!rdev)
+ goto failure;
+
+ ret = -ENODEV;
+ /* validate pnp resources */
+ if (!pnp_port_valid(pdev, 0)) {
+ dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+ goto failure;
+ }
+
+ if (!pnp_irq_valid(pdev, 0)) {
+ dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");
+ goto failure;
+ }
+
+ fintek->cir_addr = pnp_port_start(pdev, 0);
+ fintek->cir_irq = pnp_irq(pdev, 0);
+ fintek->cir_port_len = pnp_port_len(pdev, 0);
+
+ fintek->cr_ip = CR_INDEX_PORT;
+ fintek->cr_dp = CR_DATA_PORT;
+
+ spin_lock_init(&fintek->fintek_lock);
+
+ ret = -EBUSY;
+ /* now claim resources */
+ if (!request_region(fintek->cir_addr,
+ fintek->cir_port_len, FINTEK_DRIVER_NAME))
+ goto failure;
+
+ if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
+ FINTEK_DRIVER_NAME, (void *)fintek))
+ goto failure;
+
+ pnp_set_drvdata(pdev, fintek);
+ fintek->pdev = pdev;
+
+ ret = fintek_hw_detect(fintek);
+ if (ret)
+ goto failure;
+
+ /* Initialize CIR & CIR Wake Logical Devices */
+ fintek_config_mode_enable(fintek);
+ fintek_cir_ldev_init(fintek);
+ fintek_config_mode_disable(fintek);
+
+ /* Initialize CIR & CIR Wake Config Registers */
+ fintek_cir_regs_init(fintek);
+
+ /* Set up the rc device */
+ rdev->priv = fintek;
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+ rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->open = fintek_open;
+ rdev->close = fintek_close;
+ rdev->input_name = FINTEK_DESCRIPTION;
+ rdev->input_phys = "fintek/cir0";
+ rdev->input_id.bustype = BUS_HOST;
+ rdev->input_id.vendor = VENDOR_ID_FINTEK;
+ rdev->input_id.product = fintek->chip_major;
+ rdev->input_id.version = fintek->chip_minor;
+ rdev->dev.parent = &pdev->dev;
+ rdev->driver_name = FINTEK_DRIVER_NAME;
+ rdev->map_name = RC_MAP_RC6_MCE;
+ rdev->timeout = US_TO_NS(1000);
+ /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
+
+ ret = rc_register_device(rdev);
+ if (ret)
+ goto failure;
+
+ device_init_wakeup(&pdev->dev, true);
+ fintek->rdev = rdev;
+ fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+ if (debug)
+ cir_dump_regs(fintek);
+
+ return 0;
+
+failure:
+ if (fintek->cir_irq)
+ free_irq(fintek->cir_irq, fintek);
+ if (fintek->cir_addr)
+ release_region(fintek->cir_addr, fintek->cir_port_len);
+
+ rc_free_device(rdev);
+ kfree(fintek);
+
+ return ret;
+}
+
+static void __devexit fintek_remove(struct pnp_dev *pdev)
+{
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fintek->fintek_lock, flags);
+ /* disable CIR */
+ fintek_disable_cir(fintek);
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+ /* enable CIR Wake (for IR power-on) */
+ fintek_enable_wake(fintek);
+ spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+ /* free resources */
+ free_irq(fintek->cir_irq, fintek);
+ release_region(fintek->cir_addr, fintek->cir_port_len);
+
+ rc_unregister_device(fintek->rdev);
+
+ kfree(fintek);
+}
+
+static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+
+ fit_dbg("%s called", __func__);
+
+ /* disable all CIR interrupts */
+ fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+ fintek_config_mode_enable(fintek);
+
+ /* disable cir logical dev */
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+
+ /* make sure wake is enabled */
+ fintek_enable_wake(fintek);
+
+ return 0;
+}
+
+static int fintek_resume(struct pnp_dev *pdev)
+{
+ int ret = 0;
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+
+ fit_dbg("%s called", __func__);
+
+ /* open interrupt */
+ fintek_enable_cir_irq(fintek);
+
+ /* Enable CIR logical device */
+ fintek_config_mode_enable(fintek);
+ fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+ fintek_config_mode_disable(fintek);
+
+ fintek_cir_regs_init(fintek);
+
+ return ret;
+}
+
+static void fintek_shutdown(struct pnp_dev *pdev)
+{
+ struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+ fintek_enable_wake(fintek);
+}
+
+static const struct pnp_device_id fintek_ids[] = {
+ { "FIT0002", 0 }, /* CIR */
+ { "", 0 },
+};
+
+static struct pnp_driver fintek_driver = {
+ .name = FINTEK_DRIVER_NAME,
+ .id_table = fintek_ids,
+ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+ .probe = fintek_probe,
+ .remove = __devexit_p(fintek_remove),
+ .suspend = fintek_suspend,
+ .resume = fintek_resume,
+ .shutdown = fintek_shutdown,
+};
+
+int fintek_init(void)
+{
+ return pnp_register_driver(&fintek_driver);
+}
+
+void fintek_exit(void)
+{
+ pnp_unregister_driver(&fintek_driver);
+}
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+MODULE_DEVICE_TABLE(pnp, fintek_ids);
+MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver");
+
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_init(fintek_init);
+module_exit(fintek_exit);
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
new file mode 100644
index 0000000..1b10b20
--- /dev/null
+++ b/drivers/media/rc/fintek-cir.h
@@ -0,0 +1,243 @@
+/*
+ * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
+ *
+ * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
+ *
+ * Special thanks to Fintek for providing hardware and spec sheets.
+ * This driver is based upon the nuvoton, ite and ene drivers for
+ * similar hardware.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <linux/spinlock.h>
+#include <linux/ioctl.h>
+
+/* platform driver name to register */
+#define FINTEK_DRIVER_NAME "fintek-cir"
+#define FINTEK_DESCRIPTION "Fintek LPC SuperIO Consumer IR Transceiver"
+#define VENDOR_ID_FINTEK 0x1934
+
+
+/* debugging module parameter */
+static int debug;
+
+#define fit_pr(level, text, ...) \
+ printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+#define fit_dbg(text, ...) \
+ if (debug) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define fit_dbg_verbose(text, ...) \
+ if (debug > 1) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define fit_dbg_wake(text, ...) \
+ if (debug > 2) \
+ printk(KERN_DEBUG \
+ KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+
+#define TX_BUF_LEN 256
+#define RX_BUF_LEN 32
+
+struct fintek_dev {
+ struct pnp_dev *pdev;
+ struct rc_dev *rdev;
+
+ spinlock_t fintek_lock;
+
+ /* for rx */
+ u8 buf[RX_BUF_LEN];
+ unsigned int pkts;
+
+ struct {
+ spinlock_t lock;
+ u8 buf[TX_BUF_LEN];
+ unsigned int buf_count;
+ unsigned int cur_buf_num;
+ wait_queue_head_t queue;
+ } tx;
+
+ /* Config register index/data port pair */
+ u8 cr_ip;
+ u8 cr_dp;
+
+ /* hardware I/O settings */
+ unsigned long cir_addr;
+ int cir_irq;
+ int cir_port_len;
+
+ /* hardware id */
+ u8 chip_major;
+ u8 chip_minor;
+ u16 chip_vendor;
+
+ /* hardware features */
+ bool hw_learning_capable;
+ bool hw_tx_capable;
+
+ /* rx settings */
+ bool learning_enabled;
+ bool carrier_detect_enabled;
+
+ enum {
+ CMD_HEADER = 0,
+ SUBCMD,
+ CMD_DATA,
+ PARSE_IRDATA,
+ } parser_state;
+
+ u8 cmd, rem;
+
+ /* carrier period = 1 / frequency */
+ u32 carrier;
+};
+
+/* buffer packet constants, largely identical to mceusb.c */
+#define BUF_PULSE_BIT 0x80
+#define BUF_LEN_MASK 0x1f
+#define BUF_SAMPLE_MASK 0x7f
+
+#define BUF_COMMAND_HEADER 0x9f
+#define BUF_COMMAND_MASK 0xe0
+#define BUF_COMMAND_NULL 0x00
+#define BUF_HW_CMD_HEADER 0xff
+#define BUF_CMD_G_REVISION 0x0b
+#define BUF_CMD_S_CARRIER 0x06
+#define BUF_CMD_S_TIMEOUT 0x0c
+#define BUF_CMD_SIG_END 0x01
+#define BUF_CMD_S_TXMASK 0x08
+#define BUF_CMD_S_RXSENSOR 0x14
+#define BUF_RSP_PULSE_COUNT 0x15
+
+#define CIR_SAMPLE_PERIOD 50
+
+/*
+ * Configuration Register:
+ * Index Port
+ * Data Port
+ */
+#define CR_INDEX_PORT 0x2e
+#define CR_DATA_PORT 0x2f
+
+/* Possible alternate values, depends on how the chip is wired */
+#define CR_INDEX_PORT2 0x4e
+#define CR_DATA_PORT2 0x4f
+
+/*
+ * GCR_CONFIG_PORT_SEL bit 4 specifies which Index Port value is
+ * active. 1 = 0x4e, 0 = 0x2e
+ */
+#define PORT_SEL_PORT_4E_EN 0x10
+
+/* Extended Function Mode enable/disable magic values */
+#define CONFIG_REG_ENABLE 0x87
+#define CONFIG_REG_DISABLE 0xaa
+
+/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
+#define CHIP_ID_HIGH_F71809U 0x04
+#define CHIP_ID_LOW_F71809U 0x08
+
+/*
+ * Global control regs we need to care about:
+ * Global Control def.
+ * Register name addr val. */
+#define GCR_SOFTWARE_RESET 0x02 /* 0x00 */
+#define GCR_LOGICAL_DEV_NO 0x07 /* 0x00 */
+#define GCR_CHIP_ID_HI 0x20 /* 0x04 */
+#define GCR_CHIP_ID_LO 0x21 /* 0x08 */
+#define GCR_VENDOR_ID_HI 0x23 /* 0x19 */
+#define GCR_VENDOR_ID_LO 0x24 /* 0x34 */
+#define GCR_CONFIG_PORT_SEL 0x25 /* 0x01 */
+#define GCR_KBMOUSE_WAKEUP 0x27
+
+#define LOGICAL_DEV_DISABLE 0x00
+#define LOGICAL_DEV_ENABLE 0x01
+
+/* Logical device number of the CIR function */
+#define LOGICAL_DEV_CIR 0x05
+
+/* CIR Logical Device (LDN 0x08) config registers */
+#define CIR_CR_COMMAND_INDEX 0x04
+#define CIR_CR_IRCS 0x05 /* Before host writes command to IR, host
+ must set to 1. When host finshes write
+ command to IR, host must clear to 0. */
+#define CIR_CR_COMMAND_DATA 0x06 /* Host read or write comand data */
+#define CIR_CR_CLASS 0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx,
+ 0x33 = rx + 1 tx */
+#define CIR_CR_DEV_EN 0x30 /* bit0 = 1 enables CIR */
+#define CIR_CR_BASE_ADDR_HI 0x60 /* MSB of CIR IO base addr */
+#define CIR_CR_BASE_ADDR_LO 0x61 /* LSB of CIR IO base addr */
+#define CIR_CR_IRQ_SEL 0x70 /* bits3-0 store CIR IRQ */
+#define CIR_CR_PSOUT_STATUS 0xf1
+#define CIR_CR_WAKE_KEY3_ADDR 0xf8
+#define CIR_CR_WAKE_KEY3_CODE 0xf9
+#define CIR_CR_WAKE_KEY3_DC 0xfa
+#define CIR_CR_WAKE_CONTROL 0xfb
+#define CIR_CR_WAKE_KEY12_ADDR 0xfc
+#define CIR_CR_WAKE_KEY4_ADDR 0xfd
+#define CIR_CR_WAKE_KEY5_ADDR 0xfe
+
+#define CLASS_RX_ONLY 0xff
+#define CLASS_RX_2TX 0x66
+#define CLASS_RX_1TX 0x33
+
+/* CIR device registers */
+#define CIR_STATUS 0x00
+#define CIR_RX_DATA 0x01
+#define CIR_TX_CONTROL 0x02
+#define CIR_TX_DATA 0x03
+#define CIR_CONTROL 0x04
+
+/* Bits to enable CIR wake */
+#define LOGICAL_DEV_ACPI 0x01
+#define LDEV_ACPI_WAKE_EN_REG 0xe8
+#define ACPI_WAKE_EN_CIR_BIT 0x04
+
+#define LDEV_ACPI_PME_EN_REG 0xf0
+#define LDEV_ACPI_PME_CLR_REG 0xf1
+#define ACPI_PME_CIR_BIT 0x02
+
+#define LDEV_ACPI_STATE_REG 0xf4
+#define ACPI_STATE_CIR_BIT 0x20
+
+/*
+ * CIR status register (0x00):
+ * 7 - CIR_IRQ_EN (1 = enable CIR IRQ, 0 = disable)
+ * 3 - TX_FINISH (1 when TX finished, write 1 to clear)
+ * 2 - TX_UNDERRUN (1 on TX underrun, write 1 to clear)
+ * 1 - RX_TIMEOUT (1 on RX timeout, write 1 to clear)
+ * 0 - RX_RECEIVE (1 on RX receive, write 1 to clear)
+ */
+#define CIR_STATUS_IRQ_EN 0x80
+#define CIR_STATUS_TX_FINISH 0x08
+#define CIR_STATUS_TX_UNDERRUN 0x04
+#define CIR_STATUS_RX_TIMEOUT 0x02
+#define CIR_STATUS_RX_RECEIVE 0x01
+#define CIR_STATUS_IRQ_MASK 0x0f
+
+/*
+ * CIR TX control register (0x02):
+ * 7 - TX_START (1 to indicate TX start, auto-cleared when done)
+ * 6 - TX_END (1 to indicate TX data written to TX fifo)
+ */
+#define CIR_TX_CONTROL_TX_START 0x80
+#define CIR_TX_CONTROL_TX_END 0x40
+
diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c
index afae14f..129d3f9 100644
--- a/drivers/media/rc/keymaps/rc-lme2510.c
+++ b/drivers/media/rc/keymaps/rc-lme2510.c
@@ -14,81 +14,81 @@
static struct rc_map_table lme2510_rc[] = {
/* Type 1 - 26 buttons */
- { 0xef12ba45, KEY_0 },
- { 0xef12a05f, KEY_1 },
- { 0xef12af50, KEY_2 },
- { 0xef12a25d, KEY_3 },
- { 0xef12be41, KEY_4 },
- { 0xef12f50a, KEY_5 },
- { 0xef12bd42, KEY_6 },
- { 0xef12b847, KEY_7 },
- { 0xef12b649, KEY_8 },
- { 0xef12fa05, KEY_9 },
- { 0xef12bc43, KEY_POWER },
- { 0xef12b946, KEY_SUBTITLE },
- { 0xef12f906, KEY_PAUSE },
- { 0xef12fc03, KEY_MEDIA_REPEAT},
- { 0xef12fd02, KEY_PAUSE },
- { 0xef12a15e, KEY_VOLUMEUP },
- { 0xef12a35c, KEY_VOLUMEDOWN },
- { 0xef12f609, KEY_CHANNELUP },
- { 0xef12e51a, KEY_CHANNELDOWN },
- { 0xef12e11e, KEY_PLAY },
- { 0xef12e41b, KEY_ZOOM },
- { 0xef12a659, KEY_MUTE },
- { 0xef12a55a, KEY_TV },
- { 0xef12e718, KEY_RECORD },
- { 0xef12f807, KEY_EPG },
- { 0xef12fe01, KEY_STOP },
+ { 0x10ed45, KEY_0 },
+ { 0x10ed5f, KEY_1 },
+ { 0x10ed50, KEY_2 },
+ { 0x10ed5d, KEY_3 },
+ { 0x10ed41, KEY_4 },
+ { 0x10ed0a, KEY_5 },
+ { 0x10ed42, KEY_6 },
+ { 0x10ed47, KEY_7 },
+ { 0x10ed49, KEY_8 },
+ { 0x10ed05, KEY_9 },
+ { 0x10ed43, KEY_POWER },
+ { 0x10ed46, KEY_SUBTITLE },
+ { 0x10ed06, KEY_PAUSE },
+ { 0x10ed03, KEY_MEDIA_REPEAT},
+ { 0x10ed02, KEY_PAUSE },
+ { 0x10ed5e, KEY_VOLUMEUP },
+ { 0x10ed5c, KEY_VOLUMEDOWN },
+ { 0x10ed09, KEY_CHANNELUP },
+ { 0x10ed1a, KEY_CHANNELDOWN },
+ { 0x10ed1e, KEY_PLAY },
+ { 0x10ed1b, KEY_ZOOM },
+ { 0x10ed59, KEY_MUTE },
+ { 0x10ed5a, KEY_TV },
+ { 0x10ed18, KEY_RECORD },
+ { 0x10ed07, KEY_EPG },
+ { 0x10ed01, KEY_STOP },
/* Type 2 - 20 buttons */
- { 0xff40ea15, KEY_0 },
- { 0xff40f708, KEY_1 },
- { 0xff40f609, KEY_2 },
- { 0xff40f50a, KEY_3 },
- { 0xff40f30c, KEY_4 },
- { 0xff40f20d, KEY_5 },
- { 0xff40f10e, KEY_6 },
- { 0xff40ef10, KEY_7 },
- { 0xff40ee11, KEY_8 },
- { 0xff40ed12, KEY_9 },
- { 0xff40ff00, KEY_POWER },
- { 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */
- { 0xff40e51a, KEY_PAUSE }, /* Timeshift */
- { 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
- { 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
- { 0xff40fe01, KEY_CHANNELUP },
- { 0xff40fa05, KEY_CHANNELDOWN },
- { 0xff40eb14, KEY_ZOOM },
- { 0xff40e718, KEY_RECORD },
- { 0xff40e916, KEY_STOP },
+ { 0xbf15, KEY_0 },
+ { 0xbf08, KEY_1 },
+ { 0xbf09, KEY_2 },
+ { 0xbf0a, KEY_3 },
+ { 0xbf0c, KEY_4 },
+ { 0xbf0d, KEY_5 },
+ { 0xbf0e, KEY_6 },
+ { 0xbf10, KEY_7 },
+ { 0xbf11, KEY_8 },
+ { 0xbf12, KEY_9 },
+ { 0xbf00, KEY_POWER },
+ { 0xbf04, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0xbf1a, KEY_PAUSE }, /* Timeshift */
+ { 0xbf02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0xbf06, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0xbf01, KEY_CHANNELUP },
+ { 0xbf05, KEY_CHANNELDOWN },
+ { 0xbf14, KEY_ZOOM },
+ { 0xbf18, KEY_RECORD },
+ { 0xbf16, KEY_STOP },
/* Type 3 - 20 buttons */
- { 0xff00e31c, KEY_0 },
- { 0xff00f807, KEY_1 },
- { 0xff00ea15, KEY_2 },
- { 0xff00f609, KEY_3 },
- { 0xff00e916, KEY_4 },
- { 0xff00e619, KEY_5 },
- { 0xff00f20d, KEY_6 },
- { 0xff00f30c, KEY_7 },
- { 0xff00e718, KEY_8 },
- { 0xff00a15e, KEY_9 },
- { 0xff00ba45, KEY_POWER },
- { 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */
- { 0xff00b54a, KEY_PAUSE }, /* Timeshift */
- { 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
- { 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
- { 0xff00b946, KEY_CHANNELUP },
- { 0xff00bf40, KEY_CHANNELDOWN },
- { 0xff00f708, KEY_ZOOM },
- { 0xff00bd42, KEY_RECORD },
- { 0xff00a55a, KEY_STOP },
+ { 0x1c, KEY_0 },
+ { 0x07, KEY_1 },
+ { 0x15, KEY_2 },
+ { 0x09, KEY_3 },
+ { 0x16, KEY_4 },
+ { 0x19, KEY_5 },
+ { 0x0d, KEY_6 },
+ { 0x0c, KEY_7 },
+ { 0x18, KEY_8 },
+ { 0x5e, KEY_9 },
+ { 0x45, KEY_POWER },
+ { 0x44, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0x4a, KEY_PAUSE }, /* Timeshift */
+ { 0x47, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0x43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0x46, KEY_CHANNELUP },
+ { 0x40, KEY_CHANNELDOWN },
+ { 0x08, KEY_ZOOM },
+ { 0x42, KEY_RECORD },
+ { 0x5a, KEY_STOP },
};
static struct rc_map_list lme2510_map = {
.map = {
.scan = lme2510_rc,
.size = ARRAY_SIZE(lme2510_rc),
- .rc_type = RC_TYPE_UNKNOWN,
+ .rc_type = RC_TYPE_NEC,
.name = RC_MAP_LME2510,
}
};
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 3be180b..bb53de7 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -687,7 +687,7 @@
config VIDEO_TIMBERDALE
tristate "Support for timberdale Video In/LogiWIN"
- depends on VIDEO_V4L2 && I2C
+ depends on VIDEO_V4L2 && I2C && DMADEVICES
select DMA_ENGINE
select TIMB_DMA
select VIDEO_ADV7180
@@ -757,6 +757,8 @@
---help---
This driver supports NOON010PC30 CIF camera from Siliconfile
+source "drivers/media/video/m5mols/Kconfig"
+
config VIDEO_OMAP3
tristate "OMAP 3 Camera support (EXPERIMENTAL)"
select OMAP_IOMMU
@@ -952,7 +954,7 @@
config VIDEO_S5P_MIPI_CSIS
tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
- depends on VIDEO_V4L2 && PM_RUNTIME && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API
---help---
This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver.
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 9519160..f0fecd6 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 0073a8c..40eb632 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -438,7 +438,7 @@
strcat(vc->card, " (676/");
break;
default:
- strcat(vc->card, " (???/");
+ strcat(vc->card, " (XXX/");
break;
}
switch (cam->params.version.sensor_flags) {
@@ -458,7 +458,7 @@
strcat(vc->card, "500)");
break;
default:
- strcat(vc->card, "???)");
+ strcat(vc->card, "XXX)");
break;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index 280df43..8d78134 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -1354,7 +1354,7 @@
{
u8 value[4] = { 0, 0, 0, 0 };
int status = 0;
- cx231xx_info("cx231xx_dump_SC_reg %s!\n", __TIME__);
+ cx231xx_info("cx231xx_dump_SC_reg!\n");
status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
value, 4);
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/video/gspca/kinect.c
index 66671a4..26fc206 100644
--- a/drivers/media/video/gspca/kinect.c
+++ b/drivers/media/video/gspca/kinect.c
@@ -34,7 +34,7 @@
MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
MODULE_LICENSE("GPL");
-#ifdef DEBUG
+#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE | D_CONF | D_STREAM | D_FRAM | D_PACK |
D_USBI | D_USBO | D_V4L2;
#endif
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 0000000..302dc3d
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+ tristate "Fujitsu M-5MOLS 8MP sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
new file mode 100644
index 0000000..0a44e02
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o
+
+obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
new file mode 100644
index 0000000..10b55c8
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,296 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef M5MOLS_H
+#define M5MOLS_H
+
+#include <media/v4l2-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd)
+
+#define to_sd(__ctrl) \
+ (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd)
+
+enum m5mols_restype {
+ M5MOLS_RESTYPE_MONITOR,
+ M5MOLS_RESTYPE_CAPTURE,
+ M5MOLS_RESTYPE_MAX,
+};
+
+/**
+ * struct m5mols_resolution - structure for the resolution
+ * @type: resolution type according to the pixel code
+ * @width: width of the resolution
+ * @height: height of the resolution
+ * @reg: resolution preset register value
+ */
+struct m5mols_resolution {
+ u8 reg;
+ enum m5mols_restype type;
+ u16 width;
+ u16 height;
+};
+
+/**
+ * struct m5mols_exif - structure for the EXIF information of M-5MOLS
+ * @exposure_time: exposure time register value
+ * @shutter_speed: speed of the shutter register value
+ * @aperture: aperture register value
+ * @exposure_bias: it calls also EV bias
+ * @iso_speed: ISO register value
+ * @flash: status register value of the flash
+ * @sdr: status register value of the Subject Distance Range
+ * @qval: not written exact meaning in document
+ */
+struct m5mols_exif {
+ u32 exposure_time;
+ u32 shutter_speed;
+ u32 aperture;
+ u32 brightness;
+ u32 exposure_bias;
+ u16 iso_speed;
+ u16 flash;
+ u16 sdr;
+ u16 qval;
+};
+
+/**
+ * struct m5mols_capture - Structure for the capture capability
+ * @exif: EXIF information
+ * @main: size in bytes of the main image
+ * @thumb: size in bytes of the thumb image, if it was accompanied
+ * @total: total size in bytes of the produced image
+ */
+struct m5mols_capture {
+ struct m5mols_exif exif;
+ u32 main;
+ u32 thumb;
+ u32 total;
+};
+
+/**
+ * struct m5mols_scenemode - structure for the scenemode capability
+ * @metering: metering light register value
+ * @ev_bias: EV bias register value
+ * @wb_mode: mode which means the WhiteBalance is Auto or Manual
+ * @wb_preset: whitebalance preset register value in the Manual mode
+ * @chroma_en: register value whether the Chroma capability is enabled or not
+ * @chroma_lvl: chroma's level register value
+ * @edge_en: register value Whether the Edge capability is enabled or not
+ * @edge_lvl: edge's level register value
+ * @af_range: Auto Focus's range
+ * @fd_mode: Face Detection mode
+ * @mcc: Multi-axis Color Conversion which means emotion color
+ * @light: status of the Light
+ * @flash: status of the Flash
+ * @tone: Tone color which means Contrast
+ * @iso: ISO register value
+ * @capt_mode: Mode of the Image Stabilization while the camera capturing
+ * @wdr: Wide Dynamic Range register value
+ *
+ * The each value according to each scenemode is recommended in the documents.
+ */
+struct m5mols_scenemode {
+ u32 metering;
+ u32 ev_bias;
+ u32 wb_mode;
+ u32 wb_preset;
+ u32 chroma_en;
+ u32 chroma_lvl;
+ u32 edge_en;
+ u32 edge_lvl;
+ u32 af_range;
+ u32 fd_mode;
+ u32 mcc;
+ u32 light;
+ u32 flash;
+ u32 tone;
+ u32 iso;
+ u32 capt_mode;
+ u32 wdr;
+};
+
+/**
+ * struct m5mols_version - firmware version information
+ * @customer: customer information
+ * @project: version of project information according to customer
+ * @fw: firmware revision
+ * @hw: hardware revision
+ * @param: version of the parameter
+ * @awb: Auto WhiteBalance algorithm version
+ * @str: information about manufacturer and packaging vendor
+ * @af: Auto Focus version
+ *
+ * The register offset starts the customer version at 0x0, and it ends
+ * the awb version at 0x09. The customer, project information occupies 1 bytes
+ * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
+ * unique string associated with firmware's version. It includes information
+ * about manufacturer and the vendor of the sensor's packaging. The least
+ * significant 2 bytes of the string indicate packaging manufacturer.
+ */
+#define VERSION_STRING_SIZE 22
+struct m5mols_version {
+ u8 customer;
+ u8 project;
+ u16 fw;
+ u16 hw;
+ u16 param;
+ u16 awb;
+ u8 str[VERSION_STRING_SIZE];
+ u8 af;
+};
+#define VERSION_SIZE sizeof(struct m5mols_version)
+
+/**
+ * struct m5mols_info - M-5MOLS driver data structure
+ * @pdata: platform data
+ * @sd: v4l-subdev instance
+ * @pad: media pad
+ * @ffmt: current fmt according to resolution type
+ * @res_type: current resolution type
+ * @code: current code
+ * @irq_waitq: waitqueue for the capture
+ * @work_irq: workqueue for the IRQ
+ * @flags: state variable for the interrupt handler
+ * @handle: control handler
+ * @autoexposure: Auto Exposure control
+ * @exposure: Exposure control
+ * @autowb: Auto White Balance control
+ * @colorfx: Color effect control
+ * @saturation: Saturation control
+ * @zoom: Zoom control
+ * @ver: information of the version
+ * @cap: the capture mode attributes
+ * @power: current sensor's power status
+ * @ctrl_sync: true means all controls of the sensor are initialized
+ * @int_capture: true means the capture interrupt is issued once
+ * @lock_ae: true means the Auto Exposure is locked
+ * @lock_awb: true means the Aut WhiteBalance is locked
+ * @resolution: register value for current resolution
+ * @interrupt: register value for current interrupt status
+ * @mode: register value for current operation mode
+ * @mode_save: register value for current operation mode for saving
+ * @set_power: optional power callback to the board code
+ */
+struct m5mols_info {
+ const struct m5mols_platform_data *pdata;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
+ int res_type;
+ enum v4l2_mbus_pixelcode code;
+ wait_queue_head_t irq_waitq;
+ struct work_struct work_irq;
+ unsigned long flags;
+
+ struct v4l2_ctrl_handler handle;
+ /* Autoexposure/exposure control cluster */
+ struct {
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *colorfx;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *zoom;
+
+ struct m5mols_version ver;
+ struct m5mols_capture cap;
+ bool power;
+ bool ctrl_sync;
+ bool lock_ae;
+ bool lock_awb;
+ u8 resolution;
+ u32 interrupt;
+ u32 mode;
+ u32 mode_save;
+ int (*set_power)(struct device *dev, int on);
+};
+
+#define ST_CAPT_IRQ 0
+
+#define is_powered(__info) (__info->power)
+#define is_ctrl_synced(__info) (__info->ctrl_sync)
+#define is_available_af(__info) (__info->ver.af)
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+#define is_manufacturer(__info, __manufacturer) \
+ (__info->ver.str[0] == __manufacturer[0] && \
+ __info->ver.str[1] == __manufacturer[1])
+/*
+ * I2C operation of the M-5MOLS
+ *
+ * The I2C read operation of the M-5MOLS requires 2 messages. The first
+ * message sends the information about the command, command category, and total
+ * message size. The second message is used to retrieve the data specifed in
+ * the first message
+ *
+ * 1st message 2nd message
+ * +-------+---+----------+-----+-------+ +------+------+------+------+
+ * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] |
+ * +-------+---+----------+-----+-------+ +------+------+------+------+
+ * - size1: message data size(5 in this case)
+ * - size2: desired buffer size of the 2nd message
+ * - d[0..3]: according to size2
+ *
+ * The I2C write operation needs just one message. The message includes
+ * category, command, total size, and desired data.
+ *
+ * 1st message
+ * +-------+---+----------+-----+------+------+------+------+
+ * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
+ * +-------+---+----------+-----+------+------+------+------+
+ * - d[0..3]: according to size1
+ */
+int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
+int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
+
+/*
+ * Mode operation of the M-5MOLS
+ *
+ * Changing the mode of the M-5MOLS is needed right executing order.
+ * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
+ * by user. There are various categories associated with each mode.
+ *
+ * +============================================================+
+ * | mode | category |
+ * +============================================================+
+ * | FLASH | FLASH(only after Stand-by or Power-on) |
+ * | SYSTEM | SYSTEM(only after sensor arm-booting) |
+ * | PARAMETER | PARAMETER |
+ * | MONITOR | MONITOR(preview), Auto Focus, Face Detection |
+ * | CAPTURE | Single CAPTURE, Preview(recording) |
+ * +============================================================+
+ *
+ * The available executing order between each modes are as follows:
+ * PARAMETER <---> MONITOR <---> CAPTURE
+ */
+int m5mols_mode(struct m5mols_info *info, u32 mode);
+
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_sync_controls(struct m5mols_info *info);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_do_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+ int (*set_power)(struct m5mols_info *, bool));
+
+#endif /* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
new file mode 100644
index 0000000..d71a390
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -0,0 +1,191 @@
+/*
+ * The Capture code for Fujitsu M-5MOLS ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static int m5mols_capture_error_handler(struct m5mols_info *info,
+ int timeout)
+{
+ int ret;
+
+ /* Disable all interrupts and clear relevant interrupt staus bits */
+ ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE,
+ info->interrupt & ~(REG_INT_CAPTURE));
+ if (ret)
+ return ret;
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+/**
+ * m5mols_read_rational - I2C read of a rational number
+ *
+ * Read numerator and denominator from registers @addr_num and @addr_den
+ * respectively and return the division result in @val.
+ */
+static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
+ u32 addr_den, u32 *val)
+{
+ u32 num, den;
+
+ int ret = m5mols_read(sd, addr_num, &num);
+ if (!ret)
+ ret = m5mols_read(sd, addr_den, &den);
+ if (ret)
+ return ret;
+ *val = den == 0 ? 0 : num / den;
+ return ret;
+}
+
+/**
+ * m5mols_capture_info - Gather captured image information
+ *
+ * For now it gathers only EXIF information and file size.
+ */
+static int m5mols_capture_info(struct m5mols_info *info)
+{
+ struct m5mols_exif *exif = &info->cap.exif;
+ struct v4l2_subdev *sd = &info->sd;
+ int ret;
+
+ ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU,
+ EXIF_INFO_EXPTIME_DE, &exif->exposure_time);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE,
+ &exif->shutter_speed);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE,
+ &exif->aperture);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE,
+ &exif->brightness);
+ if (ret)
+ return ret;
+ ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE,
+ &exif->exposure_bias);
+ if (ret)
+ return ret;
+
+ ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed);
+ if (!ret)
+ ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash);
+ if (!ret)
+ ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr);
+ if (!ret)
+ ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval);
+ if (ret)
+ return ret;
+
+ if (!ret)
+ ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main);
+ if (!ret)
+ ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
+ if (!ret)
+ info->cap.total = info->cap.main + info->cap.thumb;
+
+ return ret;
+}
+
+int m5mols_start_capture(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ u32 resolution = info->resolution;
+ int timeout;
+ int ret;
+
+ /*
+ * Preparing capture. Setting control & interrupt before entering
+ * capture mode
+ *
+ * 1) change to MONITOR mode for operating control & interrupt
+ * 2) set controls (considering v4l2_control value & lock 3A)
+ * 3) set interrupt
+ * 4) change to CAPTURE mode
+ */
+ ret = m5mols_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_sync_controls(info);
+ if (!ret)
+ ret = m5mols_lock_3a(info, true);
+ if (!ret)
+ ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+ if (!ret)
+ ret = m5mols_mode(info, REG_CAPTURE);
+ if (!ret) {
+ /* Wait for capture interrupt, after changing capture mode */
+ timeout = wait_event_interruptible_timeout(info->irq_waitq,
+ test_bit(ST_CAPT_IRQ, &info->flags),
+ msecs_to_jiffies(2000));
+ if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags))
+ ret = m5mols_capture_error_handler(info, timeout);
+ }
+ if (!ret)
+ ret = m5mols_lock_3a(info, false);
+ if (ret)
+ return ret;
+ /*
+ * Starting capture. Setting capture frame count and resolution and
+ * the format(available format: JPEG, Bayer RAW, YUV).
+ *
+ * 1) select single or multi(enable to 25), format, size
+ * 2) set interrupt
+ * 3) start capture(for main image, now)
+ * 4) get information
+ * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
+ */
+ ret = m5mols_write(sd, CAPC_SEL_FRAME, 1);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution);
+ if (!ret)
+ ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+ if (!ret)
+ ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN);
+ if (!ret) {
+ /* Wait for the capture completion interrupt */
+ timeout = wait_event_interruptible_timeout(info->irq_waitq,
+ test_bit(ST_CAPT_IRQ, &info->flags),
+ msecs_to_jiffies(2000));
+ if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) {
+ ret = m5mols_capture_info(info);
+ if (!ret)
+ v4l2_subdev_notify(sd, 0, &info->cap.total);
+ }
+ }
+
+ return m5mols_capture_error_handler(info, timeout);
+}
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 0000000..817c16f
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,299 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static struct m5mols_scenemode m5mols_default_scenemode[] = {
+ [REG_SCENE_NORMAL] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_PORTRAIT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
+ REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_LANDSCAPE] = {
+ REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_SPORTS] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_PARTY_INDOOR] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_BEACH_SNOW] = {
+ REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_SUNSET] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
+ REG_AWB_DAYLIGHT,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_DAWN_DUSK] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
+ REG_AWB_FLUORESCENT_1,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_FALL] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_NIGHT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_AGAINST_LIGHT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_FIRE] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
+ },
+ [REG_SCENE_TEXT] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
+ REG_AF_MACRO, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
+ },
+ [REG_SCENE_CANDLE] = {
+ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+ REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+ REG_AF_NORMAL, REG_FD_OFF,
+ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+ },
+};
+
+/**
+ * m5mols_do_scenemode() - Change current scenemode
+ * @mode: Desired mode of the scenemode
+ *
+ * WARNING: The execution order is important. Do not change the order.
+ */
+int m5mols_do_scenemode(struct m5mols_info *info, u32 mode)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
+ int ret;
+
+ if (mode > REG_SCENE_CANDLE)
+ return -EINVAL;
+
+ ret = m5mols_lock_3a(info, false);
+ if (!ret)
+ ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
+ if (!ret)
+ ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode);
+ if (!ret)
+ ret = m5mols_write(sd, AE_MODE, scenemode.metering);
+ if (!ret)
+ ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias);
+ if (!ret)
+ ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode);
+ if (!ret)
+ ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl);
+ if (!ret)
+ ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en);
+ if (!ret)
+ ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl);
+ if (!ret && is_available_af(info))
+ ret = m5mols_write(sd, AF_MODE, scenemode.af_range);
+ if (!ret && is_available_af(info))
+ ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode);
+ if (!ret)
+ ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone);
+ if (!ret)
+ ret = m5mols_write(sd, AE_ISO, scenemode.iso);
+ if (!ret)
+ ret = m5mols_mode(info, REG_CAPTURE);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light);
+ if (!ret)
+ ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash);
+ if (!ret)
+ ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
+ if (!ret)
+ ret = m5mols_mode(info, REG_MONITOR);
+
+ return ret;
+}
+
+static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+ int ret = 0;
+
+ if (info->lock_ae != lock)
+ ret = m5mols_write(&info->sd, AE_LOCK,
+ lock ? REG_AE_LOCK : REG_AE_UNLOCK);
+ if (!ret)
+ info->lock_ae = lock;
+
+ return ret;
+}
+
+static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+ int ret = 0;
+
+ if (info->lock_awb != lock)
+ ret = m5mols_write(&info->sd, AWB_LOCK,
+ lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+ if (!ret)
+ info->lock_awb = lock;
+
+ return ret;
+}
+
+/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */
+int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+{
+ int ret;
+
+ ret = m5mols_lock_ae(info, lock);
+ if (!ret)
+ ret = m5mols_lock_awb(info, lock);
+ /* Don't need to handle unlocking AF */
+ if (!ret && is_available_af(info) && lock)
+ ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
+
+ return ret;
+}
+
+/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ZOOM_ABSOLUTE:
+ return m5mols_write(sd, MON_ZOOM, ctrl->val);
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = m5mols_lock_ae(info,
+ ctrl->val == V4L2_EXPOSURE_AUTO ? false : true);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO)
+ ret = m5mols_write(sd, AE_MODE, REG_AE_ALL);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ int val = info->exposure->val;
+ ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
+ if (!ret)
+ ret = m5mols_write(sd, AE_MAN_GAIN_MON, val);
+ if (!ret)
+ ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val);
+ }
+ return ret;
+
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = m5mols_lock_awb(info, ctrl->val ? false : true);
+ if (!ret)
+ ret = m5mols_write(sd, AWB_MODE, ctrl->val ?
+ REG_AWB_AUTO : REG_AWB_PRESET);
+ return ret;
+
+ case V4L2_CID_SATURATION:
+ ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON);
+ return ret;
+
+ case V4L2_CID_COLORFX:
+ /*
+ * This control uses two kinds of registers: normal & color.
+ * The normal effect belongs to category 1, while the color
+ * one belongs to category 2.
+ *
+ * The normal effect uses one register: CAT1_EFFECT.
+ * The color effect uses three registers:
+ * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
+ */
+ ret = m5mols_write(sd, PARM_EFFECT,
+ ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
+ ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
+ REG_EFFECT_OFF);
+ if (!ret)
+ ret = m5mols_write(sd, MON_EFFECT,
+ ctrl->val == V4L2_COLORFX_SEPIA ?
+ REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CFIXR,
+ ctrl->val == V4L2_COLORFX_SEPIA ?
+ REG_CFIXR_SEPIA : 0);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CFIXB,
+ ctrl->val == V4L2_COLORFX_SEPIA ?
+ REG_CFIXB_SEPIA : 0);
+ return ret;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 0000000..76eac26
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1004 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MODULE_NAME "M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY 500
+
+/* The regulator consumer names for external voltage regulators */
+static struct regulator_bulk_data supplies[] = {
+ {
+ .supply = "core", /* ARM core power, 1.2V */
+ }, {
+ .supply = "dig_18", /* digital power 1, 1.8V */
+ }, {
+ .supply = "d_sensor", /* sensor power 1, 1.8V */
+ }, {
+ .supply = "dig_28", /* digital power 2, 2.8V */
+ }, {
+ .supply = "a_sensor", /* analog power */
+ }, {
+ .supply = "dig_12", /* digital power 3, 1.2V */
+ },
+};
+
+static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+ [M5MOLS_RESTYPE_MONITOR] = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ [M5MOLS_RESTYPE_CAPTURE] = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+ { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */
+ { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */
+ { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */
+ { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+ { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */
+ { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */
+ { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */
+ { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */
+ { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */
+ { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+ { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */
+ { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */
+ { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+ { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */
+ { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */
+ { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */
+ { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */
+ { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */
+ { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */
+
+ { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */
+ { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */
+ { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+ { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */
+ { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */
+ { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */
+ { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */
+ { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */
+ { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */
+ { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */
+ { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */
+ { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+ { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */
+ { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+ { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */
+ { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */
+ { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+ { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */
+};
+
+/**
+ * m5mols_swap_byte - an byte array to integer conversion function
+ * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, u8 length)
+{
+ if (length == 1)
+ return *data;
+ else if (length == 2)
+ return be16_to_cpu(*((u16 *)data));
+ else
+ return be32_to_cpu(*((u32 *)data));
+}
+
+/**
+ * m5mols_read - I2C read function
+ * @reg: combination of size, category and command for the I2C packet
+ * @val: read value
+ */
+int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
+ u8 size = I2C_SIZE(reg);
+ u8 category = I2C_CATEGORY(reg);
+ u8 cmd = I2C_COMMAND(reg);
+ struct i2c_msg msg[2];
+ u8 wbuf[5];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ if (size != 1 && size != 2 && size != 4) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 5;
+ msg[0].buf = wbuf;
+ wbuf[0] = 5;
+ wbuf[1] = M5MOLS_BYTE_READ;
+ wbuf[2] = category;
+ wbuf[3] = cmd;
+ wbuf[4] = size;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = size + 1;
+ msg[1].buf = rbuf;
+
+ /* minimum stabilization time */
+ usleep_range(200, 200);
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0) {
+ v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
+ size, category, cmd, ret);
+ return ret;
+ }
+
+ *val = m5mols_swap_byte(&rbuf[1], size);
+
+ return 0;
+}
+
+/**
+ * m5mols_write - I2C command write function
+ * @reg: combination of size, category and command for the I2C packet
+ * @val: value to write
+ */
+int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
+ u8 category = I2C_CATEGORY(reg);
+ u8 cmd = I2C_COMMAND(reg);
+ u8 size = I2C_SIZE(reg);
+ u32 *buf = (u32 *)&wbuf[4];
+ struct i2c_msg msg[1];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ if (size != 1 && size != 2 && size != 4) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ msg->addr = client->addr;
+ msg->flags = 0;
+ msg->len = (u16)size + 4;
+ msg->buf = wbuf;
+ wbuf[0] = size + 4;
+ wbuf[1] = M5MOLS_BYTE_WRITE;
+ wbuf[2] = category;
+ wbuf[3] = cmd;
+
+ *buf = m5mols_swap_byte((u8 *)&val, size);
+
+ usleep_range(200, 200);
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0) {
+ v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n",
+ size, category, cmd, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+{
+ u32 busy, i;
+ int ret;
+
+ for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
+ ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
+ if (ret < 0)
+ return ret;
+ if ((busy & mask) == mask)
+ return 0;
+ }
+ return -EBUSY;
+}
+
+/**
+ * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
+ *
+ * Before writing desired interrupt value the INT_FACTOR register should
+ * be read to clear pending interrupts.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ u32 mask = is_available_af(info) ? REG_INT_AF : 0;
+ u32 dummy;
+ int ret;
+
+ ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
+ if (!ret)
+ ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
+ return ret;
+}
+
+/**
+ * m5mols_reg_mode - Write the mode and check busy status
+ *
+ * It always accompanies a little delay changing the M-5MOLS mode, so it is
+ * needed checking current busy status to guarantee right mode.
+ */
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
+{
+ int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
+
+ return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
+}
+
+/**
+ * m5mols_mode - manage the M-5MOLS's mode
+ * @mode: the required operation mode
+ *
+ * The commands of M-5MOLS are grouped into specific modes. Each functionality
+ * can be guaranteed only when the sensor is operating in mode which which
+ * a command belongs to.
+ */
+int m5mols_mode(struct m5mols_info *info, u32 mode)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret = -EINVAL;
+ u32 reg;
+
+ if (mode < REG_PARAMETER && mode > REG_CAPTURE)
+ return ret;
+
+ ret = m5mols_read(sd, SYSTEM_SYSMODE, ®);
+ if ((!ret && reg == mode) || ret)
+ return ret;
+
+ switch (reg) {
+ case REG_PARAMETER:
+ ret = m5mols_reg_mode(sd, REG_MONITOR);
+ if (!ret && mode == REG_MONITOR)
+ break;
+ if (!ret)
+ ret = m5mols_reg_mode(sd, REG_CAPTURE);
+ break;
+
+ case REG_MONITOR:
+ if (mode == REG_PARAMETER) {
+ ret = m5mols_reg_mode(sd, REG_PARAMETER);
+ break;
+ }
+
+ ret = m5mols_reg_mode(sd, REG_CAPTURE);
+ break;
+
+ case REG_CAPTURE:
+ ret = m5mols_reg_mode(sd, REG_MONITOR);
+ if (!ret && mode == REG_MONITOR)
+ break;
+ if (!ret)
+ ret = m5mols_reg_mode(sd, REG_PARAMETER);
+ break;
+
+ default:
+ v4l2_warn(sd, "Wrong mode: %d\n", mode);
+ }
+
+ if (!ret)
+ info->mode = mode;
+
+ return ret;
+}
+
+/**
+ * m5mols_get_version - retrieve full revisions information of M-5MOLS
+ *
+ * The version information includes revisions of hardware and firmware,
+ * AutoFocus alghorithm version and the version string.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ union {
+ struct m5mols_version ver;
+ u8 bytes[VERSION_SIZE];
+ } version;
+ u32 *value;
+ u8 cmd = CAT0_VER_CUSTOMER;
+ int ret;
+
+ do {
+ value = (u32 *)&version.bytes[cmd];
+ ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
+ if (ret)
+ return ret;
+ } while (cmd++ != CAT0_VER_AWB);
+
+ do {
+ value = (u32 *)&version.bytes[cmd];
+ ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
+ if (ret)
+ return ret;
+ if (cmd >= VERSION_SIZE - 1)
+ return -EINVAL;
+ } while (version.bytes[cmd++]);
+
+ value = (u32 *)&version.bytes[cmd];
+ ret = m5mols_read(sd, AF_VERSION, value);
+ if (ret)
+ return ret;
+
+ /* store version information swapped for being readable */
+ info->ver = version.ver;
+ info->ver.fw = be16_to_cpu(info->ver.fw);
+ info->ver.hw = be16_to_cpu(info->ver.hw);
+ info->ver.param = be16_to_cpu(info->ver.param);
+ info->ver.awb = be16_to_cpu(info->ver.awb);
+
+ v4l2_info(sd, "Manufacturer\t[%s]\n",
+ is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
+ "Samsung Electro-Machanics" :
+ is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
+ "Samsung Fiber-Optics" :
+ is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
+ "Samsung Techwin" : "None");
+ v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
+ info->ver.customer, info->ver.project);
+
+ if (!is_available_af(info))
+ v4l2_info(sd, "No support Auto Focus on this firmware\n");
+
+ return ret;
+}
+
+/**
+ * __find_restype - Lookup M-5MOLS resolution type according to pixel code
+ * @code: pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+ enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+ do {
+ if (code == m5mols_default_ffmt[type].code)
+ return type;
+ } while (type++ != SIZE_DEFAULT_FFMT);
+
+ return 0;
+}
+
+/**
+ * __find_resolution - Lookup preset and type of M-5MOLS's resolution
+ * @mf: pixel format to find/negotiate the resolution preset for
+ * @type: M-5MOLS resolution type
+ * @resolution: M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution preset and adjust mf
+ * to supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf,
+ enum m5mols_restype *type,
+ u32 *resolution)
+{
+ const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
+ const struct m5mols_resolution *match = NULL;
+ enum m5mols_restype stype = __find_restype(mf->code);
+ int i = ARRAY_SIZE(m5mols_reg_res);
+ unsigned int min_err = ~0;
+
+ while (i--) {
+ int err;
+ if (stype == fsize->type) {
+ err = abs(fsize->width - mf->width)
+ + abs(fsize->height - mf->height);
+
+ if (err < min_err) {
+ min_err = err;
+ match = fsize;
+ }
+ }
+ fsize++;
+ }
+ if (match) {
+ mf->width = match->width;
+ mf->height = match->height;
+ *resolution = match->reg;
+ *type = stype;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+ struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which,
+ enum m5mols_restype type)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+ return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (fmt->pad != 0)
+ return -EINVAL;
+
+ format = __find_format(info, fh, fmt->which, info->res_type);
+ if (!format)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct v4l2_mbus_framefmt *format = &fmt->format;
+ struct v4l2_mbus_framefmt *sfmt;
+ enum m5mols_restype type;
+ u32 resolution = 0;
+ int ret;
+
+ if (fmt->pad != 0)
+ return -EINVAL;
+
+ ret = __find_resolution(sd, format, &type, &resolution);
+ if (ret < 0)
+ return ret;
+
+ sfmt = __find_format(info, fh, fmt->which, type);
+ if (!sfmt)
+ return 0;
+
+ *sfmt = m5mols_default_ffmt[type];
+ sfmt->width = format->width;
+ sfmt->height = format->height;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ info->resolution = resolution;
+ info->code = format->code;
+ info->res_type = type;
+ }
+
+ return 0;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (!code || code->index >= SIZE_DEFAULT_FFMT)
+ return -EINVAL;
+
+ code->code = m5mols_default_ffmt[code->index].code;
+
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+ .enum_mbus_code = m5mols_enum_mbus_code,
+ .get_fmt = m5mols_get_fmt,
+ .set_fmt = m5mols_set_fmt,
+};
+
+/**
+ * m5mols_sync_controls - Apply default scene mode and the current controls
+ *
+ * This is used only streaming for syncing between v4l2_ctrl framework and
+ * m5mols's controls. First, do the scenemode to the sensor, then call
+ * v4l2_ctrl_handler_setup. It can be same between some commands and
+ * the scenemode's in the default v4l2_ctrls. But, such commands of control
+ * should be prior to the scenemode's one.
+ */
+int m5mols_sync_controls(struct m5mols_info *info)
+{
+ int ret = -EINVAL;
+
+ if (!is_ctrl_synced(info)) {
+ ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_setup(&info->handle);
+ info->ctrl_sync = true;
+ }
+
+ return ret;
+}
+
+/**
+ * m5mols_start_monitor - Start the monitor mode
+ *
+ * Before applying the controls setup the resolution and frame rate
+ * in PARAMETER mode, and then switch over to MONITOR mode.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret;
+
+ ret = m5mols_mode(info, REG_PARAMETER);
+ if (!ret)
+ ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
+ if (!ret)
+ ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
+ if (!ret)
+ ret = m5mols_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_sync_controls(info);
+
+ return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+
+ if (enable) {
+ int ret = -EINVAL;
+
+ if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
+ ret = m5mols_start_monitor(info);
+ if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
+ ret = m5mols_start_capture(info);
+
+ return ret;
+ }
+
+ return m5mols_mode(info, REG_PARAMETER);
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+ .s_stream = m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ info->mode_save = info->mode;
+
+ ret = m5mols_mode(info, REG_PARAMETER);
+ if (!ret)
+ ret = m5mols_set_ctrl(ctrl);
+ if (!ret)
+ ret = m5mols_mode(info, info->mode_save);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+ .s_ctrl = m5mols_s_ctrl,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const struct m5mols_platform_data *pdata = info->pdata;
+ int ret;
+
+ if (enable) {
+ if (is_powered(info))
+ return 0;
+
+ if (info->set_power) {
+ ret = info->set_power(&client->dev, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ info->set_power(&client->dev, 0);
+ return ret;
+ }
+
+ gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+ usleep_range(1000, 1000);
+ info->power = true;
+
+ return ret;
+ }
+
+ if (!is_powered(info))
+ return 0;
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ return ret;
+
+ if (info->set_power)
+ info->set_power(&client->dev, 0);
+
+ gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+ usleep_range(1000, 1000);
+ info->power = false;
+
+ return ret;
+}
+
+/* m5mols_update_fw - optional firmware update routine */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+ int (*set_power)(struct m5mols_info *, bool))
+{
+ return 0;
+}
+
+/**
+ * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core.
+ *
+ * Booting internal ARM core makes the M-5MOLS is ready for getting commands
+ * with I2C. It's the first thing to be done after it powered up. It must wait
+ * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
+ */
+static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
+{
+ int ret;
+
+ ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
+ if (ret < 0)
+ return ret;
+
+ msleep(520);
+
+ ret = m5mols_get_version(sd);
+ if (!ret)
+ ret = m5mols_update_fw(sd, m5mols_sensor_power);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
+
+ ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
+ if (!ret)
+ ret = m5mols_enable_interrupt(sd, REG_INT_AF);
+
+ return ret;
+}
+
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ u16 max_exposure;
+ u16 step_zoom;
+ int ret;
+
+ /* Determine value's range & step of controls for various FW version */
+ ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
+ if (!ret)
+ step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(&info->handle, 6);
+ info->autowb = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 0);
+ info->saturation = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_SATURATION,
+ 1, 5, 1, 3);
+ info->zoom = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+ 1, 70, step_zoom, 1);
+ info->exposure = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+ 0, max_exposure, 1, (int)max_exposure/2);
+ info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_COLORFX,
+ 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
+ info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ 1, 0, V4L2_EXPOSURE_MANUAL);
+
+ sd->ctrl_handler = &info->handle;
+ if (info->handle.error) {
+ v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
+ v4l2_ctrl_handler_free(&info->handle);
+ return info->handle.error;
+ }
+
+ v4l2_ctrl_cluster(2, &info->autoexposure);
+
+ return 0;
+}
+
+/**
+ * m5mols_s_power - Main sensor power control function
+ *
+ * To prevent breaking the lens when the sensor is powered off the Soft-Landing
+ * algorithm is called where available. The Soft-Landing algorithm availability
+ * dependends on the firmware provider.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ if (on) {
+ ret = m5mols_sensor_power(info, true);
+ if (!ret)
+ ret = m5mols_sensor_armboot(sd);
+ if (!ret)
+ ret = m5mols_init_controls(info);
+ if (ret)
+ return ret;
+
+ info->ffmt[M5MOLS_RESTYPE_MONITOR] =
+ m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
+ info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
+ m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
+ return ret;
+ }
+
+ if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
+ ret = m5mols_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
+ if (!ret)
+ ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
+ if (!ret)
+ ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+ REG_AF_IDLE);
+ if (!ret)
+ v4l2_info(sd, "Success soft-landing lens\n");
+ }
+
+ ret = m5mols_sensor_power(info, false);
+ if (!ret) {
+ v4l2_ctrl_handler_free(&info->handle);
+ info->ctrl_sync = false;
+ }
+
+ return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+
+ v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+ .s_power = m5mols_s_power,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .log_status = m5mols_log_status,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+ .core = &m5mols_core_ops,
+ .pad = &m5mols_pad_ops,
+ .video = &m5mols_video_ops,
+};
+
+static void m5mols_irq_work(struct work_struct *work)
+{
+ struct m5mols_info *info =
+ container_of(work, struct m5mols_info, work_irq);
+ struct v4l2_subdev *sd = &info->sd;
+ u32 reg;
+ int ret;
+
+ if (!is_powered(info) ||
+ m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
+ return;
+
+ switch (info->interrupt & REG_INT_MASK) {
+ case REG_INT_AF:
+ if (!is_available_af(info))
+ break;
+ ret = m5mols_read(sd, AF_STATUS, ®);
+ v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
+ reg == REG_AF_FAIL ? "Failed" :
+ reg == REG_AF_SUCCESS ? "Success" :
+ reg == REG_AF_IDLE ? "Idle" : "Busy");
+ break;
+ case REG_INT_CAPTURE:
+ if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags))
+ wake_up_interruptible(&info->irq_waitq);
+
+ v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n");
+ break;
+ default:
+ v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg);
+ break;
+ };
+}
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+ struct v4l2_subdev *sd = data;
+ struct m5mols_info *info = to_m5mols(sd);
+
+ schedule_work(&info->work_irq);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit m5mols_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct m5mols_platform_data *pdata = client->dev.platform_data;
+ struct m5mols_info *info;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!gpio_is_valid(pdata->gpio_reset)) {
+ dev_err(&client->dev, "No valid RESET GPIO specified\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->irq) {
+ dev_err(&client->dev, "Interrupt not assigned\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pdata = pdata;
+ info->set_power = pdata->set_power;
+
+ ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+ if (ret) {
+ dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+ goto out_free;
+ }
+ gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+ ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+ goto out_gpio;
+ }
+
+ sd = &info->sd;
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
+ v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+
+ info->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ if (ret < 0)
+ goto out_reg;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+ init_waitqueue_head(&info->irq_waitq);
+ INIT_WORK(&info->work_irq, m5mols_irq_work);
+ ret = request_irq(pdata->irq, m5mols_irq_handler,
+ IRQF_TRIGGER_RISING, MODULE_NAME, sd);
+ if (ret) {
+ dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
+ goto out_me;
+ }
+ info->res_type = M5MOLS_RESTYPE_MONITOR;
+ return 0;
+out_me:
+ media_entity_cleanup(&sd->entity);
+out_reg:
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+ gpio_free(pdata->gpio_reset);
+out_free:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit m5mols_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct m5mols_info *info = to_m5mols(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ free_irq(info->pdata->irq, sd);
+
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+ gpio_free(info->pdata->gpio_reset);
+ media_entity_cleanup(&sd->entity);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id m5mols_id[] = {
+ { MODULE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, m5mols_id);
+
+static struct i2c_driver m5mols_i2c_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ },
+ .probe = m5mols_probe,
+ .remove = __devexit_p(m5mols_remove),
+ .id_table = m5mols_id,
+};
+
+static int __init m5mols_mod_init(void)
+{
+ return i2c_add_driver(&m5mols_i2c_driver);
+}
+
+static void __exit m5mols_mod_exit(void)
+{
+ i2c_del_driver(&m5mols_i2c_driver);
+}
+
+module_init(m5mols_mod_init);
+module_exit(m5mols_mod_exit);
+
+MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
new file mode 100644
index 0000000..b83e36f
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,399 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef M5MOLS_REG_H
+#define M5MOLS_REG_H
+
+#define M5MOLS_I2C_MAX_SIZE 4
+#define M5MOLS_BYTE_READ 0x01
+#define M5MOLS_BYTE_WRITE 0x02
+
+#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff)
+#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff)
+#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff)
+#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s)
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM 0x00
+#define CAT_PARAM 0x01
+#define CAT_MONITOR 0x02
+#define CAT_AE 0x03
+#define CAT_WB 0x06
+#define CAT_EXIF 0x07
+#define CAT_FD 0x09
+#define CAT_LENS 0x0a
+#define CAT_CAPT_PARM 0x0b
+#define CAT_CAPT_CTRL 0x0c
+#define CAT_FLASH 0x0f /* related to FW, revisions, booting */
+
+/*
+ * Category 0 - SYSTEM mode
+ *
+ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by
+ * packaging & manufacturer, even the customer and project code. And the
+ * function details may vary among them. The version information helps to
+ * determine what methods shall be used in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, see definition if file m5mols.h.
+ */
+#define CAT0_VER_CUSTOMER 0x00 /* customer version */
+#define CAT0_VER_AWB 0x09 /* Auto WB version */
+#define CAT0_VER_STRING 0x0a /* string including M-5MOLS */
+#define CAT0_SYSMODE 0x0b /* SYSTEM mode register */
+#define CAT0_STATUS 0x0c /* SYSTEM mode status register */
+#define CAT0_INT_FACTOR 0x10 /* interrupt pending register */
+#define CAT0_INT_ENABLE 0x11 /* interrupt enable register */
+
+#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
+#define REG_SYSINIT 0x00 /* SYSTEM mode */
+#define REG_PARAMETER 0x01 /* PARAMETER mode */
+#define REG_MONITOR 0x02 /* MONITOR mode */
+#define REG_CAPTURE 0x03 /* CAPTURE mode */
+
+#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1)
+#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1)
+#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */
+#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */
+#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */
+
+#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1)
+#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1)
+#define REG_INT_MODE (1 << 0)
+#define REG_INT_AF (1 << 1)
+#define REG_INT_ZOOM (1 << 2)
+#define REG_INT_CAPTURE (1 << 3)
+#define REG_INT_FRAMESYNC (1 << 4)
+#define REG_INT_FD (1 << 5)
+#define REG_INT_LENS_INIT (1 << 6)
+#define REG_INT_SOUND (1 << 7)
+#define REG_INT_MASK 0x0f
+
+/*
+ * category 1 - PARAMETER mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(MONITOR) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+#define CAT1_DATA_INTERFACE 0x00 /* interface between sensor and AP */
+#define CAT1_MONITOR_SIZE 0x01 /* resolution at the MONITOR mode */
+#define CAT1_MONITOR_FPS 0x02 /* frame per second at this mode */
+#define CAT1_EFFECT 0x0b /* image effects */
+
+#define PARM_MON_SIZE I2C_REG(CAT_PARAM, CAT1_MONITOR_SIZE, 1)
+
+#define PARM_MON_FPS I2C_REG(CAT_PARAM, CAT1_MONITOR_FPS, 1)
+#define REG_FPS_30 0x02
+
+#define PARM_INTERFACE I2C_REG(CAT_PARAM, CAT1_DATA_INTERFACE, 1)
+#define REG_INTERFACE_MIPI 0x02
+
+#define PARM_EFFECT I2C_REG(CAT_PARAM, CAT1_EFFECT, 1)
+#define REG_EFFECT_OFF 0x00
+#define REG_EFFECT_NEGA 0x01
+#define REG_EFFECT_EMBOSS 0x06
+#define REG_EFFECT_OUTLINE 0x07
+#define REG_EFFECT_WATERCOLOR 0x08
+
+/*
+ * Category 2 - MONITOR mode
+ *
+ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
+ * another options like zoom/color effect(different with effect in PARAMETER
+ * mode)/anti hand shaking algorithm.
+ */
+#define CAT2_ZOOM 0x01 /* set the zoom position & execute */
+#define CAT2_ZOOM_STEP 0x03 /* set the zoom step */
+#define CAT2_CFIXB 0x09 /* CB value for color effect */
+#define CAT2_CFIXR 0x0a /* CR value for color effect */
+#define CAT2_COLOR_EFFECT 0x0b /* set on/off of color effect */
+#define CAT2_CHROMA_LVL 0x0f /* set chroma level */
+#define CAT2_CHROMA_EN 0x10 /* set on/off of choroma */
+#define CAT2_EDGE_LVL 0x11 /* set sharpness level */
+#define CAT2_EDGE_EN 0x12 /* set on/off sharpness */
+#define CAT2_TONE_CTL 0x25 /* set tone color(contrast) */
+
+#define MON_ZOOM I2C_REG(CAT_MONITOR, CAT2_ZOOM, 1)
+
+#define MON_CFIXR I2C_REG(CAT_MONITOR, CAT2_CFIXR, 1)
+#define MON_CFIXB I2C_REG(CAT_MONITOR, CAT2_CFIXB, 1)
+#define REG_CFIXB_SEPIA 0xd8
+#define REG_CFIXR_SEPIA 0x18
+
+#define MON_EFFECT I2C_REG(CAT_MONITOR, CAT2_COLOR_EFFECT, 1)
+#define REG_COLOR_EFFECT_OFF 0x00
+#define REG_COLOR_EFFECT_ON 0x01
+
+#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, CAT2_CHROMA_EN, 1)
+#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, CAT2_CHROMA_LVL, 1)
+#define REG_CHROMA_OFF 0x00
+#define REG_CHROMA_ON 0x01
+
+#define MON_EDGE_EN I2C_REG(CAT_MONITOR, CAT2_EDGE_EN, 1)
+#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, CAT2_EDGE_LVL, 1)
+#define REG_EDGE_OFF 0x00
+#define REG_EDGE_ON 0x01
+
+#define MON_TONE_CTL I2C_REG(CAT_MONITOR, CAT2_TONE_CTL, 1)
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
+ */
+#define CAT3_AE_LOCK 0x00 /* locking Auto exposure */
+#define CAT3_AE_MODE 0x01 /* set AE mode, mode means range */
+#define CAT3_ISO 0x05 /* set ISO */
+#define CAT3_EV_PRESET_MONITOR 0x0a /* EV(scenemode) preset for MONITOR */
+#define CAT3_EV_PRESET_CAPTURE 0x0b /* EV(scenemode) preset for CAPTURE */
+#define CAT3_MANUAL_GAIN_MON 0x12 /* meteoring value for the MONITOR */
+#define CAT3_MAX_GAIN_MON 0x1a /* max gain value for the MONITOR */
+#define CAT3_MANUAL_GAIN_CAP 0x26 /* meteoring value for the CAPTURE */
+#define CAT3_AE_INDEX 0x38 /* AE index */
+
+#define AE_LOCK I2C_REG(CAT_AE, CAT3_AE_LOCK, 1)
+#define REG_AE_UNLOCK 0x00
+#define REG_AE_LOCK 0x01
+
+#define AE_MODE I2C_REG(CAT_AE, CAT3_AE_MODE, 1)
+#define REG_AE_OFF 0x00 /* AE off */
+#define REG_AE_ALL 0x01 /* calc AE in all block integral */
+#define REG_AE_CENTER 0x03 /* calc AE in center weighted */
+#define REG_AE_SPOT 0x06 /* calc AE in specific spot */
+
+#define AE_ISO I2C_REG(CAT_AE, CAT3_ISO, 1)
+#define REG_ISO_AUTO 0x00
+#define REG_ISO_50 0x01
+#define REG_ISO_100 0x02
+#define REG_ISO_200 0x03
+#define REG_ISO_400 0x04
+#define REG_ISO_800 0x05
+
+#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, CAT3_EV_PRESET_MONITOR, 1)
+#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, CAT3_EV_PRESET_CAPTURE, 1)
+#define REG_SCENE_NORMAL 0x00
+#define REG_SCENE_PORTRAIT 0x01
+#define REG_SCENE_LANDSCAPE 0x02
+#define REG_SCENE_SPORTS 0x03
+#define REG_SCENE_PARTY_INDOOR 0x04
+#define REG_SCENE_BEACH_SNOW 0x05
+#define REG_SCENE_SUNSET 0x06
+#define REG_SCENE_DAWN_DUSK 0x07
+#define REG_SCENE_FALL 0x08
+#define REG_SCENE_NIGHT 0x09
+#define REG_SCENE_AGAINST_LIGHT 0x0a
+#define REG_SCENE_FIRE 0x0b
+#define REG_SCENE_TEXT 0x0c
+#define REG_SCENE_CANDLE 0x0d
+
+#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_MON, 2)
+#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, CAT3_MAX_GAIN_MON, 2)
+#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_CAP, 2)
+
+#define AE_INDEX I2C_REG(CAT_AE, CAT3_AE_INDEX, 1)
+#define REG_AE_INDEX_20_NEG 0x00
+#define REG_AE_INDEX_15_NEG 0x01
+#define REG_AE_INDEX_10_NEG 0x02
+#define REG_AE_INDEX_05_NEG 0x03
+#define REG_AE_INDEX_00 0x04
+#define REG_AE_INDEX_05_POS 0x05
+#define REG_AE_INDEX_10_POS 0x06
+#define REG_AE_INDEX_15_POS 0x07
+#define REG_AE_INDEX_20_POS 0x08
+
+/*
+ * Category 6 - White Balance
+ *
+ * This category provide AWB locking/mode/preset/speed/gain bias, etc.
+ */
+#define CAT6_AWB_LOCK 0x00 /* locking Auto Whitebalance */
+#define CAT6_AWB_MODE 0x02 /* set Auto or Manual */
+#define CAT6_AWB_MANUAL 0x03 /* set Manual(preset) value */
+
+#define AWB_LOCK I2C_REG(CAT_WB, CAT6_AWB_LOCK, 1)
+#define REG_AWB_UNLOCK 0x00
+#define REG_AWB_LOCK 0x01
+
+#define AWB_MODE I2C_REG(CAT_WB, CAT6_AWB_MODE, 1)
+#define REG_AWB_AUTO 0x01 /* AWB off */
+#define REG_AWB_PRESET 0x02 /* AWB preset */
+
+#define AWB_MANUAL I2C_REG(CAT_WB, CAT6_AWB_MANUAL, 1)
+#define REG_AWB_INCANDESCENT 0x01
+#define REG_AWB_FLUORESCENT_1 0x02
+#define REG_AWB_FLUORESCENT_2 0x03
+#define REG_AWB_DAYLIGHT 0x04
+#define REG_AWB_CLOUDY 0x05
+#define REG_AWB_SHADE 0x06
+#define REG_AWB_HORIZON 0x07
+#define REG_AWB_LEDLIGHT 0x09
+
+/*
+ * Category 7 - EXIF information
+ */
+#define CAT7_INFO_EXPTIME_NU 0x00
+#define CAT7_INFO_EXPTIME_DE 0x04
+#define CAT7_INFO_TV_NU 0x08
+#define CAT7_INFO_TV_DE 0x0c
+#define CAT7_INFO_AV_NU 0x10
+#define CAT7_INFO_AV_DE 0x14
+#define CAT7_INFO_BV_NU 0x18
+#define CAT7_INFO_BV_DE 0x1c
+#define CAT7_INFO_EBV_NU 0x20
+#define CAT7_INFO_EBV_DE 0x24
+#define CAT7_INFO_ISO 0x28
+#define CAT7_INFO_FLASH 0x2a
+#define CAT7_INFO_SDR 0x2c
+#define CAT7_INFO_QVAL 0x2e
+
+#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_NU, 4)
+#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_DE, 4)
+#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, CAT7_INFO_TV_NU, 4)
+#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, CAT7_INFO_TV_DE, 4)
+#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, CAT7_INFO_AV_NU, 4)
+#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, CAT7_INFO_AV_DE, 4)
+#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, CAT7_INFO_BV_NU, 4)
+#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, CAT7_INFO_BV_DE, 4)
+#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, CAT7_INFO_EBV_NU, 4)
+#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, CAT7_INFO_EBV_DE, 4)
+#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, CAT7_INFO_ISO, 2)
+#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, CAT7_INFO_FLASH, 2)
+#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, CAT7_INFO_SDR, 2)
+#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, CAT7_INFO_QVAL, 2)
+
+/*
+ * Category 9 - Face Detection
+ */
+#define CAT9_FD_CTL 0x00
+
+#define FD_CTL I2C_REG(CAT_FD, CAT9_FD_CTL, 1)
+#define BIT_FD_EN 0
+#define BIT_FD_DRAW_FACE_FRAME 4
+#define BIT_FD_DRAW_SMILE_LVL 6
+#define REG_FD(shift) (1 << shift)
+#define REG_FD_OFF 0x0
+
+/*
+ * Category A - Lens Parameter
+ */
+#define CATA_AF_MODE 0x01
+#define CATA_AF_EXECUTE 0x02
+#define CATA_AF_STATUS 0x03
+#define CATA_AF_VERSION 0x0a
+
+#define AF_MODE I2C_REG(CAT_LENS, CATA_AF_MODE, 1)
+#define REG_AF_NORMAL 0x00 /* Normal AF, one time */
+#define REG_AF_MACRO 0x01 /* Macro AF, one time */
+#define REG_AF_POWEROFF 0x07
+
+#define AF_EXECUTE I2C_REG(CAT_LENS, CATA_AF_EXECUTE, 1)
+#define REG_AF_STOP 0x00
+#define REG_AF_EXE_AUTO 0x01
+#define REG_AF_EXE_CAF 0x02
+
+#define AF_STATUS I2C_REG(CAT_LENS, CATA_AF_STATUS, 1)
+#define REG_AF_FAIL 0x00
+#define REG_AF_SUCCESS 0x02
+#define REG_AF_IDLE 0x04
+#define REG_AF_BUSY 0x05
+
+#define AF_VERSION I2C_REG(CAT_LENS, CATA_AF_VERSION, 1)
+
+/*
+ * Category B - CAPTURE Parameter
+ */
+#define CATB_YUVOUT_MAIN 0x00
+#define CATB_MAIN_IMAGE_SIZE 0x01
+#define CATB_MCC_MODE 0x1d
+#define CATB_WDR_EN 0x2c
+#define CATB_LIGHT_CTRL 0x40
+#define CATB_FLASH_CTRL 0x41
+
+#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, CATB_YUVOUT_MAIN, 1)
+#define REG_YUV422 0x00
+#define REG_BAYER10 0x05
+#define REG_BAYER8 0x06
+#define REG_JPEG 0x10
+
+#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, CATB_MAIN_IMAGE_SIZE, 1)
+
+#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, CATB_MCC_MODE, 1)
+#define REG_MCC_OFF 0x00
+#define REG_MCC_NORMAL 0x01
+
+#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, CATB_WDR_EN, 1)
+#define REG_WDR_OFF 0x00
+#define REG_WDR_ON 0x01
+#define REG_WDR_AUTO 0x02
+
+#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, CATB_LIGHT_CTRL, 1)
+#define REG_LIGHT_OFF 0x00
+#define REG_LIGHT_ON 0x01
+#define REG_LIGHT_AUTO 0x02
+
+#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, CATB_FLASH_CTRL, 1)
+#define REG_FLASH_OFF 0x00
+#define REG_FLASH_ON 0x01
+#define REG_FLASH_AUTO 0x02
+
+/*
+ * Category C - CAPTURE Control
+ */
+#define CATC_CAP_MODE 0x00
+#define CATC_CAP_SEL_FRAME 0x06 /* It determines Single or Multi */
+#define CATC_CAP_START 0x09
+#define CATC_CAP_IMAGE_SIZE 0x0d
+#define CATC_CAP_THUMB_SIZE 0x11
+
+#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_MODE, 1)
+#define REG_CAP_NONE 0x00
+#define REG_CAP_ANTI_SHAKE 0x02
+
+#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, CATC_CAP_SEL_FRAME, 1)
+
+#define CAPC_START I2C_REG(CAT_CAPT_CTRL, CATC_CAP_START, 1)
+#define REG_CAP_START_MAIN 0x01
+#define REG_CAP_START_THUMB 0x03
+
+#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1)
+#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1)
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+#define CATF_CAM_START 0x12 /* It starts internal ARM core booting
+ * after power-up */
+
+#define FLASH_CAM_START I2C_REG(CAT_FLASH, CATF_CAM_START, 1)
+#define REG_START_ARM_BOOT 0x01
+
+#endif /* M5MOLS_REG_H */
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c
index 84d4c7c..fc611eb 100644
--- a/drivers/media/video/timblogiw.c
+++ b/drivers/media/video/timblogiw.c
@@ -24,7 +24,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
-#include <linux/mfd/core.h>
#include <linux/scatterlist.h>
#include <linux/interrupt.h>
#include <linux/list.h>
@@ -791,7 +790,7 @@
{
int err;
struct timblogiw *lw = NULL;
- struct timb_video_platform_data *pdata = mfd_get_data(pdev);
+ struct timb_video_platform_data *pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform data\n");
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile
index 968c199..2071ca8 100644
--- a/drivers/media/video/uvc/Makefile
+++ b/drivers/media/video/uvc/Makefile
@@ -1,3 +1,6 @@
uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
uvc_status.o uvc_isight.o
+ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
+uvcvideo-objs += uvc_entity.o
+endif
obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 823f4b3..b6eae48 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -248,7 +248,7 @@
* Terminal and unit management
*/
-static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
+struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
{
struct uvc_entity *entity;
@@ -795,9 +795,12 @@
struct uvc_entity *entity;
unsigned int num_inputs;
unsigned int size;
+ unsigned int i;
+ extra_size = ALIGN(extra_size, sizeof(*entity->pads));
num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
- size = sizeof(*entity) + extra_size + num_inputs;
+ size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
+ + num_inputs;
entity = kzalloc(size, GFP_KERNEL);
if (entity == NULL)
return NULL;
@@ -805,8 +808,17 @@
entity->id = id;
entity->type = type;
+ entity->num_links = 0;
+ entity->num_pads = num_pads;
+ entity->pads = ((void *)(entity + 1)) + extra_size;
+
+ for (i = 0; i < num_inputs; ++i)
+ entity->pads[i].flags = MEDIA_PAD_FL_SINK;
+ if (!UVC_ENTITY_IS_OTERM(entity))
+ entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
+
entity->bNrInPins = num_inputs;
- entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size;
+ entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);
return entity;
}
@@ -1585,6 +1597,13 @@
uvc_status_cleanup(dev);
uvc_ctrl_cleanup_device(dev);
+ if (dev->vdev.dev)
+ v4l2_device_unregister(&dev->vdev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (media_devnode_is_registered(&dev->mdev.devnode))
+ media_device_unregister(&dev->mdev);
+#endif
+
list_for_each_safe(p, n, &dev->chains) {
struct uvc_video_chain *chain;
chain = list_entry(p, struct uvc_video_chain, list);
@@ -1594,6 +1613,13 @@
list_for_each_safe(p, n, &dev->entities) {
struct uvc_entity *entity;
entity = list_entry(p, struct uvc_entity, list);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ uvc_mc_cleanup_entity(entity);
+#endif
+ if (entity->vdev) {
+ video_device_release(entity->vdev);
+ entity->vdev = NULL;
+ }
kfree(entity);
}
@@ -1616,8 +1642,6 @@
struct uvc_streaming *stream = video_get_drvdata(vdev);
struct uvc_device *dev = stream->dev;
- video_device_release(vdev);
-
/* Decrement the registered streams count and delete the device when it
* reaches zero.
*/
@@ -1682,7 +1706,7 @@
* unregistered before the reference is released, so we don't need to
* get another one.
*/
- vdev->parent = &dev->intf->dev;
+ vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops;
vdev->release = uvc_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
@@ -1731,6 +1755,8 @@
ret = uvc_register_video(dev, stream);
if (ret < 0)
return ret;
+
+ term->vdev = stream->vdev;
}
return 0;
@@ -1745,6 +1771,14 @@
ret = uvc_register_terms(dev, chain);
if (ret < 0)
return ret;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ ret = uvc_mc_register_entities(chain);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to register entites "
+ "(%d).\n", ret);
+ }
+#endif
}
return 0;
@@ -1814,6 +1848,24 @@
"linux-uvc-devel mailing list.\n");
}
+ /* Register the media and V4L2 devices. */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->mdev.dev = &intf->dev;
+ strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
+ if (udev->serial)
+ strlcpy(dev->mdev.serial, udev->serial,
+ sizeof(dev->mdev.serial));
+ strcpy(dev->mdev.bus_info, udev->devpath);
+ dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ dev->mdev.driver_version = DRIVER_VERSION_NUMBER;
+ if (media_device_register(&dev->mdev) < 0)
+ goto error;
+
+ dev->vdev.mdev = &dev->mdev;
+#endif
+ if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
+ goto error;
+
/* Initialize controls. */
if (uvc_ctrl_init_device(dev) < 0)
goto error;
@@ -1822,7 +1874,7 @@
if (uvc_scan_device(dev) < 0)
goto error;
- /* Register video devices. */
+ /* Register video device nodes. */
if (uvc_register_chains(dev) < 0)
goto error;
diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c
new file mode 100644
index 0000000..ede7852
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_entity.c
@@ -0,0 +1,118 @@
+/*
+ * uvc_entity.c -- USB Video Class driver
+ *
+ * Copyright (C) 2005-2011
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * Video subdevices registration and unregistration
+ */
+
+static int uvc_mc_register_entity(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
+{
+ const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
+ struct uvc_entity *remote;
+ unsigned int i;
+ u8 remote_pad;
+ int ret;
+
+ for (i = 0; i < entity->num_pads; ++i) {
+ struct media_entity *source;
+ struct media_entity *sink;
+
+ if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
+ if (remote == NULL)
+ return -EINVAL;
+
+ source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
+ ? &remote->vdev->entity : &remote->subdev.entity;
+ sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
+ ? &entity->vdev->entity : &entity->subdev.entity;
+
+ remote_pad = remote->num_pads - 1;
+ ret = media_entity_create_link(source, remote_pad,
+ sink, i, flags);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
+ ret = v4l2_device_register_subdev(&chain->dev->vdev,
+ &entity->subdev);
+
+ return ret;
+}
+
+static struct v4l2_subdev_ops uvc_subdev_ops = {
+};
+
+void uvc_mc_cleanup_entity(struct uvc_entity *entity)
+{
+ if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
+ media_entity_cleanup(&entity->subdev.entity);
+ else if (entity->vdev != NULL)
+ media_entity_cleanup(&entity->vdev->entity);
+}
+
+static int uvc_mc_init_entity(struct uvc_entity *entity)
+{
+ int ret;
+
+ if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
+ v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops);
+ strlcpy(entity->subdev.name, entity->name,
+ sizeof(entity->subdev.name));
+
+ ret = media_entity_init(&entity->subdev.entity,
+ entity->num_pads, entity->pads, 0);
+ } else
+ ret = media_entity_init(&entity->vdev->entity,
+ entity->num_pads, entity->pads, 0);
+
+ return ret;
+}
+
+int uvc_mc_register_entities(struct uvc_video_chain *chain)
+{
+ struct uvc_entity *entity;
+ int ret;
+
+ list_for_each_entry(entity, &chain->entities, chain) {
+ ret = uvc_mc_init_entity(entity);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to initialize entity for "
+ "entity %u\n", entity->id);
+ return ret;
+ }
+ }
+
+ list_for_each_entry(entity, &chain->entities, chain) {
+ ret = uvc_mc_register_entity(chain, entity);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to register entity for "
+ "entity %u\n", entity->id);
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 7cf224b..20107fd 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -98,8 +98,11 @@
#ifdef __KERNEL__
#include <linux/poll.h>
+#include <linux/usb.h>
#include <linux/usb/video.h>
#include <linux/uvcvideo.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
/* --------------------------------------------------------------------------
* UVC constants
@@ -301,6 +304,13 @@
__u16 type;
char name[64];
+ /* Media controller-related fields. */
+ struct video_device *vdev;
+ struct v4l2_subdev subdev;
+ unsigned int num_pads;
+ unsigned int num_links;
+ struct media_pad *pads;
+
union {
struct {
__u16 wObjectiveFocalLengthMin;
@@ -504,6 +514,10 @@
atomic_t nmappings;
/* Video control interface */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device mdev;
+#endif
+ struct v4l2_device vdev;
__u16 uvc_version;
__u32 clock_frequency;
@@ -583,6 +597,8 @@
/* Core driver */
extern struct uvc_driver uvc_driver;
+extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
+
/* Video buffers queue management. */
extern void uvc_queue_init(struct uvc_video_queue *queue,
enum v4l2_buf_type type, int drop_corrupted);
@@ -616,6 +632,10 @@
/* V4L2 interface */
extern const struct v4l2_file_operations uvc_fops;
+/* Media controller */
+extern int uvc_mc_register_entities(struct uvc_video_chain *chain);
+extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
+
/* Video */
extern int uvc_video_init(struct uvc_streaming *stream);
extern int uvc_video_suspend(struct uvc_streaming *stream);
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 011cb6c..17dfe9b 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -21,13 +21,13 @@
#define INT_STATUS_NUM 3
-static struct resource bk_resources[] __initdata = {
+static struct resource bk_resources[] __devinitdata = {
{PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,},
{PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,},
{PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,},
};
-static struct resource led_resources[] __initdata = {
+static struct resource led_resources[] __devinitdata = {
{PM8606_LED1_RED, PM8606_LED1_RED, "led0-red", IORESOURCE_IO,},
{PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,},
{PM8606_LED1_BLUE, PM8606_LED1_BLUE, "led0-blue", IORESOURCE_IO,},
@@ -36,7 +36,7 @@
{PM8606_LED2_BLUE, PM8606_LED2_BLUE, "led1-blue", IORESOURCE_IO,},
};
-static struct resource regulator_resources[] __initdata = {
+static struct resource regulator_resources[] __devinitdata = {
{PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,},
{PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,},
{PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,},
@@ -57,15 +57,15 @@
{PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,},
};
-static struct resource touch_resources[] __initdata = {
+static struct resource touch_resources[] __devinitdata = {
{PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,},
};
-static struct resource onkey_resources[] __initdata = {
+static struct resource onkey_resources[] __devinitdata = {
{PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,},
};
-static struct resource codec_resources[] __initdata = {
+static struct resource codec_resources[] __devinitdata = {
/* Headset microphone insertion or removal */
{PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,},
/* Hook-switch press or release */
@@ -76,12 +76,12 @@
{PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,},
};
-static struct resource battery_resources[] __initdata = {
+static struct resource battery_resources[] __devinitdata = {
{PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,},
{PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,},
};
-static struct resource charger_resources[] __initdata = {
+static struct resource charger_resources[] __devinitdata = {
{PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,},
{PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,},
{PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,},
@@ -90,13 +90,17 @@
{PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,},
};
-static struct mfd_cell bk_devs[] __initdata = {
+static struct resource rtc_resources[] __devinitdata = {
+ {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
+};
+
+static struct mfd_cell bk_devs[] = {
{"88pm860x-backlight", 0,},
{"88pm860x-backlight", 1,},
{"88pm860x-backlight", 2,},
};
-static struct mfd_cell led_devs[] __initdata = {
+static struct mfd_cell led_devs[] = {
{"88pm860x-led", 0,},
{"88pm860x-led", 1,},
{"88pm860x-led", 2,},
@@ -105,7 +109,7 @@
{"88pm860x-led", 5,},
};
-static struct mfd_cell regulator_devs[] __initdata = {
+static struct mfd_cell regulator_devs[] = {
{"88pm860x-regulator", 0,},
{"88pm860x-regulator", 1,},
{"88pm860x-regulator", 2,},
@@ -126,15 +130,15 @@
{"88pm860x-regulator", 17,},
};
-static struct mfd_cell touch_devs[] __initdata = {
+static struct mfd_cell touch_devs[] = {
{"88pm860x-touch", -1,},
};
-static struct mfd_cell onkey_devs[] __initdata = {
+static struct mfd_cell onkey_devs[] = {
{"88pm860x-onkey", -1,},
};
-static struct mfd_cell codec_devs[] __initdata = {
+static struct mfd_cell codec_devs[] = {
{"88pm860x-codec", -1,},
};
@@ -143,11 +147,10 @@
{"88pm860x-charger", -1,},
};
-static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)];
-static struct pm860x_led_pdata led_pdata[ARRAY_SIZE(led_devs)];
-static struct regulator_init_data regulator_pdata[ARRAY_SIZE(regulator_devs)];
-static struct pm860x_touch_pdata touch_pdata;
-static struct pm860x_power_pdata power_pdata;
+static struct mfd_cell rtc_devs[] = {
+ {"88pm860x-rtc", -1,},
+};
+
struct pm860x_irq_data {
int reg;
@@ -501,7 +504,6 @@
}
static void __devinit device_bk_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -514,13 +516,12 @@
pdata->num_backlights = ARRAY_SIZE(bk_devs);
for (i = 0; i < pdata->num_backlights; i++) {
- memcpy(&bk_pdata[i], &pdata->backlight[i],
- sizeof(struct pm860x_backlight_pdata));
- bk_devs[i].mfd_data = &bk_pdata[i];
+ bk_devs[i].platform_data = &pdata->backlight[i];
+ bk_devs[i].pdata_size = sizeof(struct pm860x_backlight_pdata);
for (j = 0; j < ARRAY_SIZE(bk_devs); j++) {
id = bk_resources[j].start;
- if (bk_pdata[i].flags != id)
+ if (pdata->backlight[i].flags != id)
continue;
bk_devs[i].num_resources = 1;
@@ -538,7 +539,6 @@
}
static void __devinit device_led_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -551,13 +551,12 @@
pdata->num_leds = ARRAY_SIZE(led_devs);
for (i = 0; i < pdata->num_leds; i++) {
- memcpy(&led_pdata[i], &pdata->led[i],
- sizeof(struct pm860x_led_pdata));
- led_devs[i].mfd_data = &led_pdata[i];
+ led_devs[i].platform_data = &pdata->led[i];
+ led_devs[i].pdata_size = sizeof(struct pm860x_led_pdata);
for (j = 0; j < ARRAY_SIZE(led_devs); j++) {
id = led_resources[j].start;
- if (led_pdata[i].flags != id)
+ if (pdata->led[i].flags != id)
continue;
led_devs[i].num_resources = 1;
@@ -575,12 +574,11 @@
}
static void __devinit device_regulator_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
struct regulator_init_data *initdata;
int ret;
- int i, j;
+ int i, seq;
if ((pdata == NULL) || (pdata->regulator == NULL))
return;
@@ -588,41 +586,21 @@
if (pdata->num_regulators > ARRAY_SIZE(regulator_devs))
pdata->num_regulators = ARRAY_SIZE(regulator_devs);
- for (i = 0, j = -1; i < pdata->num_regulators; i++) {
+ for (i = 0, seq = -1; i < pdata->num_regulators; i++) {
initdata = &pdata->regulator[i];
- if (strstr(initdata->constraints.name, "BUCK")) {
- sscanf(initdata->constraints.name, "BUCK%d", &j);
- /* BUCK1 ~ BUCK3 */
- if ((j < 1) || (j > 3)) {
- dev_err(chip->dev, "Failed to add constraint "
- "(%s)\n", initdata->constraints.name);
- goto out;
- }
- j = (j - 1) + PM8607_ID_BUCK1;
- }
- if (strstr(initdata->constraints.name, "LDO")) {
- sscanf(initdata->constraints.name, "LDO%d", &j);
- /* LDO1 ~ LDO15 */
- if ((j < 1) || (j > 15)) {
- dev_err(chip->dev, "Failed to add constraint "
- "(%s)\n", initdata->constraints.name);
- goto out;
- }
- j = (j - 1) + PM8607_ID_LDO1;
- }
- if (j == -1) {
- dev_err(chip->dev, "Failed to add constraint (%s)\n",
- initdata->constraints.name);
+ seq = *(unsigned int *)initdata->driver_data;
+ if ((seq < 0) || (seq > PM8607_ID_RG_MAX)) {
+ dev_err(chip->dev, "Wrong ID(%d) on regulator(%s)\n",
+ seq, initdata->constraints.name);
goto out;
}
- memcpy(®ulator_pdata[i], &pdata->regulator[i],
- sizeof(struct regulator_init_data));
- regulator_devs[i].mfd_data = ®ulator_pdata[i];
+ regulator_devs[i].platform_data = &pdata->regulator[i];
+ regulator_devs[i].pdata_size = sizeof(struct regulator_init_data);
regulator_devs[i].num_resources = 1;
- regulator_devs[i].resources = ®ulator_resources[j];
+ regulator_devs[i].resources = ®ulator_resources[seq];
ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[i], 1,
- ®ulator_resources[j], 0);
+ ®ulator_resources[seq], 0);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regulator subdev\n");
goto out;
@@ -632,17 +610,35 @@
return;
}
+static void __devinit device_rtc_init(struct pm860x_chip *chip,
+ struct pm860x_platform_data *pdata)
+{
+ int ret;
+
+ if ((pdata == NULL))
+ return;
+
+ rtc_devs[0].platform_data = pdata->rtc;
+ rtc_devs[0].pdata_size = sizeof(struct pm860x_rtc_pdata);
+ rtc_devs[0].num_resources = ARRAY_SIZE(rtc_resources);
+ rtc_devs[0].resources = &rtc_resources[0];
+ ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
+ ARRAY_SIZE(rtc_devs), &rtc_resources[0],
+ chip->irq_base);
+ if (ret < 0)
+ dev_err(chip->dev, "Failed to add rtc subdev\n");
+}
+
static void __devinit device_touch_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
- if ((pdata == NULL) || (pdata->touch == NULL))
+ if (pdata == NULL)
return;
- memcpy(&touch_pdata, pdata->touch, sizeof(struct pm860x_touch_pdata));
- touch_devs[0].mfd_data = &touch_pdata;
+ touch_devs[0].platform_data = pdata->touch;
+ touch_devs[0].pdata_size = sizeof(struct pm860x_touch_pdata);
touch_devs[0].num_resources = ARRAY_SIZE(touch_resources);
touch_devs[0].resources = &touch_resources[0];
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
@@ -653,16 +649,15 @@
}
static void __devinit device_power_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
- if ((pdata == NULL) || (pdata->power == NULL))
+ if (pdata == NULL)
return;
- memcpy(&power_pdata, pdata->power, sizeof(struct pm860x_power_pdata));
- power_devs[0].mfd_data = &power_pdata;
+ power_devs[0].platform_data = pdata->power;
+ power_devs[0].pdata_size = sizeof(struct pm860x_power_pdata);
power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
power_devs[0].resources = &battery_resources[0],
ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
@@ -670,7 +665,8 @@
if (ret < 0)
dev_err(chip->dev, "Failed to add battery subdev\n");
- power_devs[1].mfd_data = &power_pdata;
+ power_devs[1].platform_data = pdata->power;
+ power_devs[1].pdata_size = sizeof(struct pm860x_power_pdata);
power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
power_devs[1].resources = &charger_resources[0],
ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
@@ -680,7 +676,6 @@
}
static void __devinit device_onkey_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -695,7 +690,6 @@
}
static void __devinit device_codec_init(struct pm860x_chip *chip,
- struct i2c_client *i2c,
struct pm860x_platform_data *pdata)
{
int ret;
@@ -763,11 +757,12 @@
if (ret < 0)
goto out;
- device_regulator_init(chip, i2c, pdata);
- device_onkey_init(chip, i2c, pdata);
- device_touch_init(chip, i2c, pdata);
- device_power_init(chip, i2c, pdata);
- device_codec_init(chip, i2c, pdata);
+ device_regulator_init(chip, pdata);
+ device_rtc_init(chip, pdata);
+ device_onkey_init(chip, pdata);
+ device_touch_init(chip, pdata);
+ device_power_init(chip, pdata);
+ device_codec_init(chip, pdata);
out:
return;
}
@@ -779,8 +774,8 @@
switch (chip->id) {
case CHIP_PM8606:
- device_bk_init(chip, chip->client, pdata);
- device_led_init(chip, chip->client, pdata);
+ device_bk_init(chip, pdata);
+ device_led_init(chip, pdata);
break;
case CHIP_PM8607:
device_8607_init(chip, chip->client, pdata);
@@ -790,8 +785,8 @@
if (chip->companion) {
switch (chip->id) {
case CHIP_PM8607:
- device_bk_init(chip, chip->companion, pdata);
- device_led_init(chip, chip->companion, pdata);
+ device_bk_init(chip, pdata);
+ device_led_init(chip, pdata);
break;
case CHIP_PM8606:
device_8607_init(chip, chip->companion, pdata);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 481770a..b6c2677 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -157,6 +157,20 @@
This driver can also be built as a module. If so, the module
will be called tps6507x.
+config MFD_TPS6586X
+ bool "TPS6586x Power Management chips"
+ depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ If you say yes here you get support for the TPS6586X series of
+ Power Management chips.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
+ This driver can also be built as a module. If so, the module
+ will be called tps6586x.
+
config MENELAUS
bool "Texas Instruments TWL92330/Menelaus PM chip"
depends on I2C=y && ARCH_OMAP2
@@ -455,6 +469,20 @@
facilities, and registers devices for the various functions
so that function-specific drivers can bind to them.
+config PCF50633_ADC
+ tristate "Support for NXP PCF50633 ADC"
+ depends on MFD_PCF50633
+ help
+ Say yes here if you want to include support for ADC in the
+ NXP PCF50633 chip.
+
+config PCF50633_GPIO
+ tristate "Support for NXP PCF50633 GPIO"
+ depends on MFD_PCF50633
+ help
+ Say yes here if you want to include support GPIO for pins on
+ the PCF50633 chip.
+
config MFD_MC13783
tristate
@@ -470,20 +498,6 @@
additional drivers must be enabled in order to use the
functionality of the device.
-config PCF50633_ADC
- tristate "Support for NXP PCF50633 ADC"
- depends on MFD_PCF50633
- help
- Say yes here if you want to include support for ADC in the
- NXP PCF50633 chip.
-
-config PCF50633_GPIO
- tristate "Support for NXP PCF50633 GPIO"
- depends on MFD_PCF50633
- help
- Say yes here if you want to include support GPIO for pins on
- the PCF50633 chip.
-
config ABX500_CORE
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
default y if ARCH_U300 || ARCH_U8500
@@ -649,20 +663,6 @@
Say yes here if you want support for the ADC unit in the JZ4740 SoC.
This driver is necessary for jz4740-battery and jz4740-hwmon driver.
-config MFD_TPS6586X
- bool "TPS6586x Power Management chips"
- depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
- select MFD_CORE
- help
- If you say yes here you get support for the TPS6586X series of
- Power Management chips.
- This driver provides common support for accessing the device,
- additional drivers must be enabled in order to use the
- functionality of the device.
-
- This driver can also be built as a module. If so, the module
- will be called tps6586x.
-
config MFD_VX855
tristate "Support for VIA VX855/VX875 integrated south bridge"
depends on PCI
@@ -691,6 +691,43 @@
This MFD driver does the required setup functionalities for
OMAP USB Host drivers.
+config MFD_PM8XXX
+ tristate
+
+config MFD_PM8921_CORE
+ tristate "Qualcomm PM8921 PMIC chip"
+ depends on MSM_SSBI
+ select MFD_CORE
+ select MFD_PM8XXX
+ help
+ If you say yes to this option, support will be included for the
+ built-in PM8921 PMIC chip.
+
+ This is required if your board has a PM8921 and uses its features,
+ such as: MPPs, GPIOs, regulators, interrupts, and PWM.
+
+ Say M here if you want to include support for PM8921 chip as a module.
+ This will build a module called "pm8921-core".
+
+config MFD_PM8XXX_IRQ
+ bool "Support for Qualcomm PM8xxx IRQ features"
+ depends on MFD_PM8XXX
+ default y if MFD_PM8XXX
+ help
+ This is the IRQ driver for Qualcomm PM 8xxx PMIC chips.
+
+ This is required to use certain other PM 8xxx features, such as GPIO
+ and MPP.
+
+config MFD_TPS65910
+ bool "TPS65910 Power Management chip"
+ depends on I2C=y
+ select MFD_CORE
+ select GPIO_TPS65910
+ help
+ if you say yes here you get support for the TPS65910 series of
+ Power Management chips.
+
endif # MFD_SUPPORT
menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 24aa444..efe3cc3 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -91,3 +91,6 @@
obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
+obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
+obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
+obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index a751927..a20e1c4 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -949,8 +949,10 @@
goto exit_no_ops;
/* Set up and register the platform devices. */
- for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++)
- ab3100_devs[i].mfd_data = ab3100_plf_data;
+ for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) {
+ ab3100_devs[i].platform_data = ab3100_plf_data;
+ ab3100_devs[i].pdata_size = sizeof(struct ab3100_platform_data);
+ }
err = mfd_add_devices(&client->dev, 0, ab3100_devs,
ARRAY_SIZE(ab3100_devs), NULL, 0);
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
index ff86acf..3d7dce6 100644
--- a/drivers/mfd/ab3550-core.c
+++ b/drivers/mfd/ab3550-core.c
@@ -1320,8 +1320,10 @@
goto exit_no_ops;
/* Set up and register the platform devices. */
- for (i = 0; i < AB3550_NUM_DEVICES; i++)
- ab3550_devs[i].mfd_data = ab3550_plf_data->dev_data[i];
+ for (i = 0; i < AB3550_NUM_DEVICES; i++) {
+ ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i];
+ ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i];
+ }
err = mfd_add_devices(&client->dev, 0, ab3550_devs,
ARRAY_SIZE(ab3550_devs), NULL,
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 67d01c9..fc0c1af 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -254,8 +254,9 @@
if (new == old)
continue;
- /* Interrupt register 12 does'nt exist prior to version 0x20 */
- if (ab8500_irq_regoffset[i] == 11 && ab8500->chip_id < 0x20)
+ /* Interrupt register 12 doesn't exist prior to version 2.0 */
+ if (ab8500_irq_regoffset[i] == 11 &&
+ ab8500->chip_id < AB8500_CUT2P0)
continue;
ab8500->oldmask[i] = new;
@@ -307,8 +308,8 @@
int status;
u8 value;
- /* Interrupt register 12 does'nt exist prior to version 0x20 */
- if (regoffset == 11 && ab8500->chip_id < 0x20)
+ /* Interrupt register 12 doesn't exist prior to version 2.0 */
+ if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0)
continue;
status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
@@ -724,17 +725,15 @@
if (ret < 0)
return ret;
- /*
- * 0x0 - Early Drop
- * 0x10 - Cut 1.0
- * 0x11 - Cut 1.1
- * 0x20 - Cut 2.0
- * 0x30 - Cut 3.0
- */
- if (value == 0x0 || value == 0x10 || value == 0x11 || value == 0x20 ||
- value == 0x30) {
+ switch (value) {
+ case AB8500_CUTEARLY:
+ case AB8500_CUT1P0:
+ case AB8500_CUT1P1:
+ case AB8500_CUT2P0:
+ case AB8500_CUT3P0:
dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
- } else {
+ break;
+ default:
dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value);
return -EINVAL;
}
@@ -763,8 +762,9 @@
/* Clear and mask all interrupts */
for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
- /* Interrupt register 12 does'nt exist prior to version 0x20 */
- if (ab8500_irq_regoffset[i] == 11 && ab8500->chip_id < 0x20)
+ /* Interrupt register 12 doesn't exist prior to version 2.0 */
+ if (ab8500_irq_regoffset[i] == 11 &&
+ ab8500->chip_id < AB8500_CUT2P0)
continue;
get_register_interruptible(ab8500, AB8500_INTERRUPT,
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 6421ad1..f16afb2 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -57,6 +57,7 @@
#define SW_AVG_16 0x60
#define ADC_SW_CONV 0x04
#define EN_ICHAR 0x80
+#define BTEMP_PULL_UP 0x08
#define EN_BUF 0x40
#define DIS_ZERO 0x00
#define GPADC_BUSY 0x01
@@ -101,6 +102,7 @@
/**
* struct ab8500_gpadc - AB8500 GPADC device information
+ * @chip_id ABB chip id
* @dev: pointer to the struct device
* @node: a list of AB8500 GPADCs, hence prepared for
reentrance
@@ -112,6 +114,7 @@
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
+ u8 chip_id;
struct device *dev;
struct list_head node;
struct completion ab8500_gpadc_complete;
@@ -274,6 +277,7 @@
dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n");
goto out;
}
+
/* Select the input source and set average samples to 16 */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16));
@@ -282,9 +286,11 @@
"gpadc_conversion: set avg samples failed\n");
goto out;
}
+
/*
* Enable ADC, buffering, select rising edge and enable ADC path
- * charging current sense if it needed
+ * charging current sense if it needed, ABB 3.0 needs some special
+ * treatment too.
*/
switch (input) {
case MAIN_CHARGER_C:
@@ -294,6 +300,23 @@
EN_BUF | EN_ICHAR,
EN_BUF | EN_ICHAR);
break;
+ case BTEMP_BALL:
+ if (gpadc->chip_id >= AB8500_CUT3P0) {
+ /* Turn on btemp pull-up on ABB 3.0 */
+ ret = abx500_mask_and_set_register_interruptible(
+ gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+ EN_BUF | BTEMP_PULL_UP,
+ EN_BUF | BTEMP_PULL_UP);
+
+ /*
+ * Delay might be needed for ABB8500 cut 3.0, if not, remove
+ * when hardware will be availible
+ */
+ msleep(1);
+ break;
+ }
+ /* Intentional fallthrough */
default:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
@@ -304,6 +327,7 @@
"gpadc_conversion: select falling edge failed\n");
goto out;
}
+
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV);
if (ret < 0) {
@@ -552,6 +576,14 @@
goto fail;
}
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(gpadc->dev);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "failed to get chip ID\n");
+ goto fail_irq;
+ }
+ gpadc->chip_id = (u8) ret;
+
/* VTVout LDO used to power up ab8500-GPADC */
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 0b4d5b2..c27fd1f 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -88,19 +88,19 @@
static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset);
-static inline void asic3_write_register(struct asic3 *asic,
- unsigned int reg, u32 value)
+void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value)
{
iowrite16(value, asic->mapping +
(reg >> asic->bus_shift));
}
+EXPORT_SYMBOL_GPL(asic3_write_register);
-static inline u32 asic3_read_register(struct asic3 *asic,
- unsigned int reg)
+u32 asic3_read_register(struct asic3 *asic, unsigned int reg)
{
return ioread16(asic->mapping +
(reg >> asic->bus_shift));
}
+EXPORT_SYMBOL_GPL(asic3_read_register);
static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set)
{
@@ -676,7 +676,8 @@
.name = "ds1wm",
.enable = ds1wm_enable,
.disable = ds1wm_disable,
- .mfd_data = &ds1wm_pdata,
+ .platform_data = &ds1wm_pdata,
+ .pdata_size = sizeof(ds1wm_pdata),
.num_resources = ARRAY_SIZE(ds1wm_resources),
.resources = ds1wm_resources,
};
@@ -777,12 +778,61 @@
.name = "tmio-mmc",
.enable = asic3_mmc_enable,
.disable = asic3_mmc_disable,
- .mfd_data = &asic3_mmc_data,
+ .platform_data = &asic3_mmc_data,
+ .pdata_size = sizeof(asic3_mmc_data),
.num_resources = ARRAY_SIZE(asic3_mmc_resources),
.resources = asic3_mmc_resources,
};
+static const int clock_ledn[ASIC3_NUM_LEDS] = {
+ [0] = ASIC3_CLOCK_LED0,
+ [1] = ASIC3_CLOCK_LED1,
+ [2] = ASIC3_CLOCK_LED2,
+};
+
+static int asic3_leds_enable(struct platform_device *pdev)
+{
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+ return 0;
+}
+
+static int asic3_leds_disable(struct platform_device *pdev)
+{
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
+
+ return 0;
+}
+
+static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = {
+ [0] = {
+ .name = "leds-asic3",
+ .id = 0,
+ .enable = asic3_leds_enable,
+ .disable = asic3_leds_disable,
+ },
+ [1] = {
+ .name = "leds-asic3",
+ .id = 1,
+ .enable = asic3_leds_enable,
+ .disable = asic3_leds_disable,
+ },
+ [2] = {
+ .name = "leds-asic3",
+ .id = 2,
+ .enable = asic3_leds_enable,
+ .disable = asic3_leds_disable,
+ },
+};
+
static int __init asic3_mfd_probe(struct platform_device *pdev,
+ struct asic3_platform_data *pdata,
struct resource *mem)
{
struct asic3 *asic = platform_get_drvdata(pdev);
@@ -806,7 +856,8 @@
/* MMC */
asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) +
- mem_sdio->start, 0x400 >> asic->bus_shift);
+ mem_sdio->start,
+ ASIC3_SD_CONFIG_SIZE >> asic->bus_shift);
if (!asic->tmio_cnf) {
ret = -ENOMEM;
dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n");
@@ -820,9 +871,23 @@
if (ret < 0)
goto out;
- if (mem_sdio && (irq >= 0))
+ if (mem_sdio && (irq >= 0)) {
ret = mfd_add_devices(&pdev->dev, pdev->id,
&asic3_cell_mmc, 1, mem_sdio, irq);
+ if (ret < 0)
+ goto out;
+ }
+
+ if (pdata->leds) {
+ int i;
+
+ for (i = 0; i < ASIC3_NUM_LEDS; ++i) {
+ asic3_cell_leds[i].platform_data = &pdata->leds[i];
+ asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]);
+ }
+ ret = mfd_add_devices(&pdev->dev, 0,
+ asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0);
+ }
out:
return ret;
@@ -903,7 +968,7 @@
*/
memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init));
- asic3_mfd_probe(pdev, mem);
+ asic3_mfd_probe(pdev, pdata, mem);
dev_info(asic->dev, "ASIC3 Core driver\n");
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index 414783b..4e2af2c 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -119,12 +119,14 @@
/* Voice codec interface client */
cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL];
cell->name = "davinci-vcif";
- cell->mfd_data = davinci_vc;
+ cell->platform_data = davinci_vc;
+ cell->pdata_size = sizeof(*davinci_vc);
/* Voice codec CQ93VC client */
cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL];
cell->name = "cq93vc-codec";
- cell->mfd_data = davinci_vc;
+ cell->platform_data = davinci_vc;
+ cell->pdata_size = sizeof(*davinci_vc);
ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
DAVINCI_VC_CELLS, NULL, 0);
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index fb9770b..2808bd1 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -117,7 +117,8 @@
.name = "ds1wm",
.enable = ds1wm_enable,
.disable = ds1wm_disable,
- .mfd_data = &ds1wm_pdata,
+ .platform_data = &ds1wm_pdata,
+ .pdata_size = sizeof(ds1wm_pdata),
.num_resources = 2,
.resources = ds1wm_resources,
};
@@ -172,6 +173,8 @@
}
if (pdata && pdata->led_pdata) {
+ led_cell.platform_data = pdata->led_pdata;
+ led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo);
ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0);
if (ret < 0)
dev_warn(dev, "failed to register LED device\n");
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index fc41911..5c2a06a 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -86,7 +86,8 @@
/* Add platform data */
pdata->modno = modno;
- cell->mfd_data = pdata;
+ cell->platform_data = pdata;
+ cell->pdata_size = sizeof(*pdata);
/* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */
res->flags = IORESOURCE_MEM;
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index 58cc5fd..e1e59c9 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -627,7 +627,7 @@
goto out_dev;
}
- if (pdata && pdata->regulator[0]) {
+ if (pdata) {
ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0],
ARRAY_SIZE(regulator_devs),
®ulator_resources[0], 0);
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 668634e..7e4d44b 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -683,13 +683,14 @@
EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
- const char *format, void *pdata)
+ const char *format, void *pdata, size_t pdata_size)
{
char buf[30];
const char *name = mc13xxx_get_chipname(mc13xxx);
struct mfd_cell cell = {
- .mfd_data = pdata,
+ .platform_data = pdata,
+ .pdata_size = pdata_size,
};
/* there is no asnprintf in the kernel :-( */
@@ -705,7 +706,7 @@
static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
{
- return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL);
+ return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
}
static int mc13xxx_probe(struct spi_device *spi)
@@ -764,7 +765,7 @@
if (pdata->flags & MC13XXX_USE_REGULATOR) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
- &pdata->regulators);
+ &pdata->regulators, sizeof(pdata->regulators));
}
if (pdata->flags & MC13XXX_USE_RTC)
@@ -774,7 +775,8 @@
mc13xxx_add_subdevice(mc13xxx, "%s-ts");
if (pdata->flags & MC13XXX_USE_LED)
- mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds);
+ mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
+ pdata->leds, sizeof(*pdata->leds));
return 0;
}
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index f4c8c84..0902523 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -88,6 +88,13 @@
pdev->dev.parent = parent;
+ if (cell->pdata_size) {
+ ret = platform_device_add_data(pdev,
+ cell->platform_data, cell->pdata_size);
+ if (ret)
+ goto fail_res;
+ }
+
ret = mfd_platform_add_cell(pdev, cell);
if (ret)
goto fail_res;
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 3ab9ffa..8552195 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <plat/usb.h>
+#include <linux/pm_runtime.h>
#define USBHS_DRIVER_NAME "usbhs-omap"
#define OMAP_EHCI_DEVICE "ehci-omap"
@@ -146,9 +147,6 @@
struct usbhs_hcd_omap {
- struct clk *usbhost_ick;
- struct clk *usbhost_hs_fck;
- struct clk *usbhost_fs_fck;
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
struct clk *utmi_p1_fck;
@@ -158,8 +156,6 @@
struct clk *usbhost_p2_fck;
struct clk *usbtll_p2_fck;
struct clk *init_60m_fclk;
- struct clk *usbtll_fck;
- struct clk *usbtll_ick;
void __iomem *uhh_base;
void __iomem *tll_base;
@@ -281,6 +277,7 @@
if (!ehci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
+ ret = -ENOMEM;
goto err_end;
}
@@ -304,13 +301,14 @@
sizeof(*ohci_data), dev);
if (!ohci) {
dev_err(dev, "omap_usbhs_alloc_child failed\n");
+ ret = -ENOMEM;
goto err_ehci;
}
return 0;
err_ehci:
- platform_device_put(ehci);
+ platform_device_unregister(ehci);
err_end:
return ret;
@@ -351,46 +349,13 @@
omap->platdata.ehci_data = pdata->ehci_data;
omap->platdata.ohci_data = pdata->ohci_data;
- omap->usbhost_ick = clk_get(dev, "usbhost_ick");
- if (IS_ERR(omap->usbhost_ick)) {
- ret = PTR_ERR(omap->usbhost_ick);
- dev_err(dev, "usbhost_ick failed error:%d\n", ret);
- goto err_end;
- }
-
- omap->usbhost_hs_fck = clk_get(dev, "hs_fck");
- if (IS_ERR(omap->usbhost_hs_fck)) {
- ret = PTR_ERR(omap->usbhost_hs_fck);
- dev_err(dev, "usbhost_hs_fck failed error:%d\n", ret);
- goto err_usbhost_ick;
- }
-
- omap->usbhost_fs_fck = clk_get(dev, "fs_fck");
- if (IS_ERR(omap->usbhost_fs_fck)) {
- ret = PTR_ERR(omap->usbhost_fs_fck);
- dev_err(dev, "usbhost_fs_fck failed error:%d\n", ret);
- goto err_usbhost_hs_fck;
- }
-
- omap->usbtll_fck = clk_get(dev, "usbtll_fck");
- if (IS_ERR(omap->usbtll_fck)) {
- ret = PTR_ERR(omap->usbtll_fck);
- dev_err(dev, "usbtll_fck failed error:%d\n", ret);
- goto err_usbhost_fs_fck;
- }
-
- omap->usbtll_ick = clk_get(dev, "usbtll_ick");
- if (IS_ERR(omap->usbtll_ick)) {
- ret = PTR_ERR(omap->usbtll_ick);
- dev_err(dev, "usbtll_ick failed error:%d\n", ret);
- goto err_usbtll_fck;
- }
+ pm_runtime_enable(&pdev->dev);
omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
if (IS_ERR(omap->utmi_p1_fck)) {
ret = PTR_ERR(omap->utmi_p1_fck);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_usbtll_ick;
+ goto err_end;
}
omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
@@ -520,22 +485,8 @@
err_utmi_p1_fck:
clk_put(omap->utmi_p1_fck);
-err_usbtll_ick:
- clk_put(omap->usbtll_ick);
-
-err_usbtll_fck:
- clk_put(omap->usbtll_fck);
-
-err_usbhost_fs_fck:
- clk_put(omap->usbhost_fs_fck);
-
-err_usbhost_hs_fck:
- clk_put(omap->usbhost_hs_fck);
-
-err_usbhost_ick:
- clk_put(omap->usbhost_ick);
-
err_end:
+ pm_runtime_disable(&pdev->dev);
kfree(omap);
end_probe:
@@ -569,11 +520,7 @@
clk_put(omap->utmi_p2_fck);
clk_put(omap->xclk60mhsp1_ck);
clk_put(omap->utmi_p1_fck);
- clk_put(omap->usbtll_ick);
- clk_put(omap->usbtll_fck);
- clk_put(omap->usbhost_fs_fck);
- clk_put(omap->usbhost_hs_fck);
- clk_put(omap->usbhost_ick);
+ pm_runtime_disable(&pdev->dev);
kfree(omap);
return 0;
@@ -693,7 +640,6 @@
struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags = 0;
int ret = 0;
- unsigned long timeout;
unsigned reg;
dev_dbg(dev, "starting TI HSUSB Controller\n");
@@ -706,11 +652,7 @@
if (omap->count > 0)
goto end_count;
- clk_enable(omap->usbhost_ick);
- clk_enable(omap->usbhost_hs_fck);
- clk_enable(omap->usbhost_fs_fck);
- clk_enable(omap->usbtll_fck);
- clk_enable(omap->usbtll_ick);
+ pm_runtime_get_sync(dev);
if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) {
@@ -734,50 +676,6 @@
omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
- /* perform TLL soft reset, and wait until reset is complete */
- usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
- OMAP_USBTLL_SYSCONFIG_SOFTRESET);
-
- /* Wait for TLL reset to complete */
- timeout = jiffies + msecs_to_jiffies(1000);
- while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
- & OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
- cpu_relax();
-
- if (time_after(jiffies, timeout)) {
- dev_dbg(dev, "operation timed out\n");
- ret = -EINVAL;
- goto err_tll;
- }
- }
-
- dev_dbg(dev, "TLL RESET DONE\n");
-
- /* (1<<3) = no idle mode only for initial debugging */
- usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
- OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
- OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
- OMAP_USBTLL_SYSCONFIG_AUTOIDLE);
-
- /* Put UHH in NoIdle/NoStandby mode */
- reg = usbhs_read(omap->uhh_base, OMAP_UHH_SYSCONFIG);
- if (is_omap_usbhs_rev1(omap)) {
- reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
- | OMAP_UHH_SYSCONFIG_SIDLEMODE
- | OMAP_UHH_SYSCONFIG_CACTIVITY
- | OMAP_UHH_SYSCONFIG_MIDLEMODE);
- reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
-
-
- } else if (is_omap_usbhs_rev2(omap)) {
- reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR;
- reg |= OMAP4_UHH_SYSCONFIG_NOIDLE;
- reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR;
- reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY;
- }
-
- usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
-
reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
/* setup ULPI bypass and burst configurations */
reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
@@ -917,6 +815,8 @@
return 0;
err_tll:
+ pm_runtime_put_sync(dev);
+ spin_unlock_irqrestore(&omap->lock, flags);
if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
gpio_free(pdata->ehci_data->reset_gpio_port[0]);
@@ -924,13 +824,6 @@
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
gpio_free(pdata->ehci_data->reset_gpio_port[1]);
}
-
- clk_disable(omap->usbtll_ick);
- clk_disable(omap->usbtll_fck);
- clk_disable(omap->usbhost_fs_fck);
- clk_disable(omap->usbhost_hs_fck);
- clk_disable(omap->usbhost_ick);
- spin_unlock_irqrestore(&omap->lock, flags);
return ret;
}
@@ -994,6 +887,20 @@
dev_dbg(dev, "operation timed out\n");
}
+ if (is_omap_usbhs_rev2(omap)) {
+ if (is_ehci_tll_mode(pdata->port_mode[0]))
+ clk_enable(omap->usbtll_p1_fck);
+ if (is_ehci_tll_mode(pdata->port_mode[1]))
+ clk_enable(omap->usbtll_p2_fck);
+ clk_disable(omap->utmi_p2_fck);
+ clk_disable(omap->utmi_p1_fck);
+ }
+
+ pm_runtime_put_sync(dev);
+
+ /* The gpio_free migh sleep; so unlock the spinlock */
+ spin_unlock_irqrestore(&omap->lock, flags);
+
if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
gpio_free(pdata->ehci_data->reset_gpio_port[0]);
@@ -1001,14 +908,7 @@
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
gpio_free(pdata->ehci_data->reset_gpio_port[1]);
}
-
- clk_disable(omap->utmi_p2_fck);
- clk_disable(omap->utmi_p1_fck);
- clk_disable(omap->usbtll_ick);
- clk_disable(omap->usbtll_fck);
- clk_disable(omap->usbhost_fs_fck);
- clk_disable(omap->usbhost_hs_fck);
- clk_disable(omap->usbhost_ick);
+ return;
end_disble:
spin_unlock_irqrestore(&omap->lock, flags);
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
new file mode 100644
index 0000000..e873b157
--- /dev/null
+++ b/drivers/mfd/pm8921-core.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/msm_ssbi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define REG_HWREV 0x002 /* PMIC4 revision */
+#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
+
+struct pm8921 {
+ struct device *dev;
+ struct pm_irq_chip *irq_chip;
+};
+
+static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
+}
+
+static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
+}
+
+static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+ return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int cnt)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+ return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
+}
+
+static int pm8921_read_irq_stat(const struct device *dev, int irq)
+{
+ const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
+ const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
+
+ return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
+}
+
+static struct pm8xxx_drvdata pm8921_drvdata = {
+ .pmic_readb = pm8921_readb,
+ .pmic_writeb = pm8921_writeb,
+ .pmic_read_buf = pm8921_read_buf,
+ .pmic_write_buf = pm8921_write_buf,
+ .pmic_read_irq_stat = pm8921_read_irq_stat,
+};
+
+static int __devinit pm8921_add_subdevices(const struct pm8921_platform_data
+ *pdata,
+ struct pm8921 *pmic,
+ u32 rev)
+{
+ int ret = 0, irq_base = 0;
+ struct pm_irq_chip *irq_chip;
+
+ if (pdata->irq_pdata) {
+ pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
+ pdata->irq_pdata->irq_cdata.rev = rev;
+ irq_base = pdata->irq_pdata->irq_base;
+ irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
+
+ if (IS_ERR(irq_chip)) {
+ pr_err("Failed to init interrupts ret=%ld\n",
+ PTR_ERR(irq_chip));
+ return PTR_ERR(irq_chip);
+ }
+ pmic->irq_chip = irq_chip;
+ }
+ return ret;
+}
+
+static int __devinit pm8921_probe(struct platform_device *pdev)
+{
+ const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
+ struct pm8921 *pmic;
+ int rc;
+ u8 val;
+ u32 rev;
+
+ if (!pdata) {
+ pr_err("missing platform data\n");
+ return -EINVAL;
+ }
+
+ pmic = kzalloc(sizeof(struct pm8921), GFP_KERNEL);
+ if (!pmic) {
+ pr_err("Cannot alloc pm8921 struct\n");
+ return -ENOMEM;
+ }
+
+ /* Read PMIC chip revision */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 1: %02X\n", val);
+ rev = val;
+
+ /* Read PMIC chip revision 2 */
+ rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
+ if (rc) {
+ pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
+ REG_HWREV_2, rc);
+ goto err_read_rev;
+ }
+ pr_info("PMIC revision 2: %02X\n", val);
+ rev |= val << BITS_PER_BYTE;
+
+ pmic->dev = &pdev->dev;
+ pm8921_drvdata.pm_chip_data = pmic;
+ platform_set_drvdata(pdev, &pm8921_drvdata);
+
+ rc = pm8921_add_subdevices(pdata, pmic, rev);
+ if (rc) {
+ pr_err("Cannot add subdevices rc=%d\n", rc);
+ goto err;
+ }
+
+ /* gpio might not work if no irq device is found */
+ WARN_ON(pmic->irq_chip == NULL);
+
+ return 0;
+
+err:
+ mfd_remove_devices(pmic->dev);
+ platform_set_drvdata(pdev, NULL);
+err_read_rev:
+ kfree(pmic);
+ return rc;
+}
+
+static int __devexit pm8921_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_drvdata *drvdata;
+ struct pm8921 *pmic = NULL;
+
+ drvdata = platform_get_drvdata(pdev);
+ if (drvdata)
+ pmic = drvdata->pm_chip_data;
+ if (pmic)
+ mfd_remove_devices(pmic->dev);
+ if (pmic->irq_chip) {
+ pm8xxx_irq_exit(pmic->irq_chip);
+ pmic->irq_chip = NULL;
+ }
+ platform_set_drvdata(pdev, NULL);
+ kfree(pmic);
+
+ return 0;
+}
+
+static struct platform_driver pm8921_driver = {
+ .probe = pm8921_probe,
+ .remove = __devexit_p(pm8921_remove),
+ .driver = {
+ .name = "pm8921-core",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8921_init(void)
+{
+ return platform_driver_register(&pm8921_driver);
+}
+subsys_initcall(pm8921_init);
+
+static void __exit pm8921_exit(void)
+{
+ platform_driver_unregister(&pm8921_driver);
+}
+module_exit(pm8921_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC 8921 core driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8921-core");
diff --git a/drivers/mfd/pm8xxx-irq.c b/drivers/mfd/pm8xxx-irq.c
new file mode 100644
index 0000000..d452dd0
--- /dev/null
+++ b/drivers/mfd/pm8xxx-irq.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* PMIC8xxx IRQ */
+
+#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
+
+#define SSBI_REG_ADDR_IRQ_ROOT (SSBI_REG_ADDR_IRQ_BASE + 0)
+#define SSBI_REG_ADDR_IRQ_M_STATUS1 (SSBI_REG_ADDR_IRQ_BASE + 1)
+#define SSBI_REG_ADDR_IRQ_M_STATUS2 (SSBI_REG_ADDR_IRQ_BASE + 2)
+#define SSBI_REG_ADDR_IRQ_M_STATUS3 (SSBI_REG_ADDR_IRQ_BASE + 3)
+#define SSBI_REG_ADDR_IRQ_M_STATUS4 (SSBI_REG_ADDR_IRQ_BASE + 4)
+#define SSBI_REG_ADDR_IRQ_BLK_SEL (SSBI_REG_ADDR_IRQ_BASE + 5)
+#define SSBI_REG_ADDR_IRQ_IT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 6)
+#define SSBI_REG_ADDR_IRQ_CONFIG (SSBI_REG_ADDR_IRQ_BASE + 7)
+#define SSBI_REG_ADDR_IRQ_RT_STATUS (SSBI_REG_ADDR_IRQ_BASE + 8)
+
+#define PM_IRQF_LVL_SEL 0x01 /* level select */
+#define PM_IRQF_MASK_FE 0x02 /* mask falling edge */
+#define PM_IRQF_MASK_RE 0x04 /* mask rising edge */
+#define PM_IRQF_CLR 0x08 /* clear interrupt */
+#define PM_IRQF_BITS_MASK 0x70
+#define PM_IRQF_BITS_SHIFT 4
+#define PM_IRQF_WRITE 0x80
+
+#define PM_IRQF_MASK_ALL (PM_IRQF_MASK_FE | \
+ PM_IRQF_MASK_RE)
+
+struct pm_irq_chip {
+ struct device *dev;
+ spinlock_t pm_irq_lock;
+ unsigned int devirq;
+ unsigned int irq_base;
+ unsigned int num_irqs;
+ unsigned int num_blocks;
+ unsigned int num_masters;
+ u8 config[0];
+};
+
+static int pm8xxx_read_root_irq(const struct pm_irq_chip *chip, u8 *rp)
+{
+ return pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_ROOT, rp);
+}
+
+static int pm8xxx_read_master_irq(const struct pm_irq_chip *chip, u8 m, u8 *bp)
+{
+ return pm8xxx_readb(chip->dev,
+ SSBI_REG_ADDR_IRQ_M_STATUS1 + m, bp);
+}
+
+static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, u8 bp, u8 *ip)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_IT_STATUS, ip);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_config_irq(struct pm_irq_chip *chip, u8 bp, u8 cp)
+{
+ int rc;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, bp);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", bp, rc);
+ goto bail;
+ }
+
+ cp |= PM_IRQF_WRITE;
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_CONFIG, cp);
+ if (rc)
+ pr_err("Failed Configuring IRQ rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+ return rc;
+}
+
+static int pm8xxx_irq_block_handler(struct pm_irq_chip *chip, int block)
+{
+ int pmirq, irq, i, ret = 0;
+ u8 bits;
+
+ ret = pm8xxx_read_block_irq(chip, block, &bits);
+ if (ret) {
+ pr_err("Failed reading %d block ret=%d", block, ret);
+ return ret;
+ }
+ if (!bits) {
+ pr_err("block bit set in master but no irqs: %d", block);
+ return 0;
+ }
+
+ /* Check IRQ bits */
+ for (i = 0; i < 8; i++) {
+ if (bits & (1 << i)) {
+ pmirq = block * 8 + i;
+ irq = pmirq + chip->irq_base;
+ generic_handle_irq(irq);
+ }
+ }
+ return 0;
+}
+
+static int pm8xxx_irq_master_handler(struct pm_irq_chip *chip, int master)
+{
+ u8 blockbits;
+ int block_number, i, ret = 0;
+
+ ret = pm8xxx_read_master_irq(chip, master, &blockbits);
+ if (ret) {
+ pr_err("Failed to read master %d ret=%d\n", master, ret);
+ return ret;
+ }
+ if (!blockbits) {
+ pr_err("master bit set in root but no blocks: %d", master);
+ return 0;
+ }
+
+ for (i = 0; i < 8; i++)
+ if (blockbits & (1 << i)) {
+ block_number = master * 8 + i; /* block # */
+ ret |= pm8xxx_irq_block_handler(chip, block_number);
+ }
+ return ret;
+}
+
+static void pm8xxx_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct pm_irq_chip *chip = irq_desc_get_handler_data(desc);
+ struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+ u8 root;
+ int i, ret, masters = 0;
+
+ ret = pm8xxx_read_root_irq(chip, &root);
+ if (ret) {
+ pr_err("Can't read root status ret=%d\n", ret);
+ return;
+ }
+
+ /* on pm8xxx series masters start from bit 1 of the root */
+ masters = root >> 1;
+
+ /* Read allowed masters for blocks. */
+ for (i = 0; i < chip->num_masters; i++)
+ if (masters & (1 << i))
+ pm8xxx_irq_master_handler(chip, i);
+
+ irq_chip->irq_ack(&desc->irq_data);
+}
+
+static void pm8xxx_irq_mask_ack(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq] | PM_IRQF_MASK_ALL | PM_IRQF_CLR;
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static void pm8xxx_irq_unmask(struct irq_data *d)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ config = chip->config[pmirq];
+ pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = d->irq - chip->irq_base;
+ int master, irq_bit;
+ u8 block, config;
+
+ block = pmirq / 8;
+ master = block / 8;
+ irq_bit = pmirq % 8;
+
+ chip->config[pmirq] = (irq_bit << PM_IRQF_BITS_SHIFT)
+ | PM_IRQF_MASK_ALL;
+ if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+ if (flow_type & IRQF_TRIGGER_RISING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ if (flow_type & IRQF_TRIGGER_FALLING)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ } else {
+ chip->config[pmirq] |= PM_IRQF_LVL_SEL;
+
+ if (flow_type & IRQF_TRIGGER_HIGH)
+ chip->config[pmirq] &= ~PM_IRQF_MASK_RE;
+ else
+ chip->config[pmirq] &= ~PM_IRQF_MASK_FE;
+ }
+
+ config = chip->config[pmirq] | PM_IRQF_CLR;
+ return pm8xxx_config_irq(chip, block, config);
+}
+
+static int pm8xxx_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ return 0;
+}
+
+static struct irq_chip pm8xxx_irq_chip = {
+ .name = "pm8xxx",
+ .irq_mask_ack = pm8xxx_irq_mask_ack,
+ .irq_unmask = pm8xxx_irq_unmask,
+ .irq_set_type = pm8xxx_irq_set_type,
+ .irq_set_wake = pm8xxx_irq_set_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
+};
+
+/**
+ * pm8xxx_get_irq_stat - get the status of the irq line
+ * @chip: pointer to identify a pmic irq controller
+ * @irq: the irq number
+ *
+ * The pm8xxx gpio and mpp rely on the interrupt block to read
+ * the values on their pins. This function is to facilitate reading
+ * the status of a gpio or an mpp line. The caller has to convert the
+ * gpio number to irq number.
+ *
+ * RETURNS:
+ * an int indicating the value read on that line
+ */
+int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
+{
+ int pmirq, rc;
+ u8 block, bits, bit;
+ unsigned long flags;
+
+ if (chip == NULL || irq < chip->irq_base ||
+ irq >= chip->irq_base + chip->num_irqs)
+ return -EINVAL;
+
+ pmirq = irq - chip->irq_base;
+
+ block = pmirq / 8;
+ bit = pmirq % 8;
+
+ spin_lock_irqsave(&chip->pm_irq_lock, flags);
+
+ rc = pm8xxx_writeb(chip->dev, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ if (rc) {
+ pr_err("Failed Selecting block irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = pm8xxx_readb(chip->dev, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ if (rc) {
+ pr_err("Failed Configuring irq=%d pmirq=%d blk=%d rc=%d\n",
+ irq, pmirq, block, rc);
+ goto bail_out;
+ }
+
+ rc = (bits & (1 << bit)) ? 1 : 0;
+
+bail_out:
+ spin_unlock_irqrestore(&chip->pm_irq_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_get_irq_stat);
+
+struct pm_irq_chip * __devinit pm8xxx_irq_init(struct device *dev,
+ const struct pm8xxx_irq_platform_data *pdata)
+{
+ struct pm_irq_chip *chip;
+ int devirq, rc;
+ unsigned int pmirq;
+
+ if (!pdata) {
+ pr_err("No platform data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ devirq = pdata->devirq;
+ if (devirq < 0) {
+ pr_err("missing devirq\n");
+ rc = devirq;
+ return ERR_PTR(-EINVAL);
+ }
+
+ chip = kzalloc(sizeof(struct pm_irq_chip)
+ + sizeof(u8) * pdata->irq_cdata.nirqs, GFP_KERNEL);
+ if (!chip) {
+ pr_err("Cannot alloc pm_irq_chip struct\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ chip->dev = dev;
+ chip->devirq = devirq;
+ chip->irq_base = pdata->irq_base;
+ chip->num_irqs = pdata->irq_cdata.nirqs;
+ chip->num_blocks = DIV_ROUND_UP(chip->num_irqs, 8);
+ chip->num_masters = DIV_ROUND_UP(chip->num_blocks, 8);
+ spin_lock_init(&chip->pm_irq_lock);
+
+ for (pmirq = 0; pmirq < chip->num_irqs; pmirq++) {
+ irq_set_chip_and_handler(chip->irq_base + pmirq,
+ &pm8xxx_irq_chip,
+ handle_level_irq);
+ irq_set_chip_data(chip->irq_base + pmirq, chip);
+#ifdef CONFIG_ARM
+ set_irq_flags(chip->irq_base + pmirq, IRQF_VALID);
+#else
+ irq_set_noprobe(chip->irq_base + pmirq);
+#endif
+ }
+
+ irq_set_irq_type(devirq, pdata->irq_trigger_flag);
+ irq_set_handler_data(devirq, chip);
+ irq_set_chained_handler(devirq, pm8xxx_irq_handler);
+ set_irq_wake(devirq, 1);
+
+ return chip;
+}
+
+int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
+{
+ irq_set_chained_handler(chip->devirq, NULL);
+ kfree(chip);
+ return 0;
+}
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index 10dbe63..809bd4a 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -61,12 +61,14 @@
.name = "rdc321x-wdt",
.resources = rdc321x_wdt_resource,
.num_resources = ARRAY_SIZE(rdc321x_wdt_resource),
- .mfd_data = &rdc321x_wdt_pdata,
+ .platform_data = &rdc321x_wdt_pdata,
+ .pdata_size = sizeof(rdc321x_wdt_pdata),
}, {
.name = "rdc321x-gpio",
.resources = rdc321x_gpio_resources,
.num_resources = ARRAY_SIZE(rdc321x_gpio_resources),
- .mfd_data = &rdc321x_gpio_pdata,
+ .platform_data = &rdc321x_gpio_pdata,
+ .pdata_size = sizeof(rdc321x_gpio_pdata),
},
};
diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
index 42830e6..91ad21e 100644
--- a/drivers/mfd/t7l66xb.c
+++ b/drivers/mfd/t7l66xb.c
@@ -170,7 +170,8 @@
.name = "tmio-mmc",
.enable = t7l66xb_mmc_enable,
.disable = t7l66xb_mmc_disable,
- .mfd_data = &t7166xb_mmc_data,
+ .platform_data = &t7166xb_mmc_data,
+ .pdata_size = sizeof(t7166xb_mmc_data),
.num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
.resources = t7l66xb_mmc_resources,
},
@@ -382,7 +383,8 @@
t7l66xb_attach_irq(dev);
- t7l66xb_cells[T7L66XB_CELL_NAND].mfd_data = pdata->nand_data;
+ t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = pdata->nand_data;
+ t7l66xb_cells[T7L66XB_CELL_NAND].pdata_size = sizeof(*pdata->nand_data);
ret = mfd_add_devices(&dev->dev, dev->id,
t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells),
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index b006f7c..ad715bf 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -131,7 +131,8 @@
.name = "tmio-mmc",
.enable = tc6387xb_mmc_enable,
.disable = tc6387xb_mmc_disable,
- .mfd_data = &tc6387xb_mmc_data,
+ .platform_data = &tc6387xb_mmc_data,
+ .pdata_size = sizeof(tc6387xb_mmc_data),
.num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
.resources = tc6387xb_mmc_resources,
},
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index fc53ce2..9612264 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -393,7 +393,8 @@
.name = "tmio-mmc",
.enable = tc6393xb_mmc_enable,
.resume = tc6393xb_mmc_resume,
- .mfd_data = &tc6393xb_mmc_data,
+ .platform_data = &tc6393xb_mmc_data,
+ .pdata_size = sizeof(tc6393xb_mmc_data),
.num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
.resources = tc6393xb_mmc_resources,
},
@@ -692,8 +693,11 @@
goto err_setup;
}
- tc6393xb_cells[TC6393XB_CELL_NAND].mfd_data = tcpd->nand_data;
- tc6393xb_cells[TC6393XB_CELL_FB].mfd_data = tcpd->fb_data;
+ tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = tcpd->nand_data;
+ tc6393xb_cells[TC6393XB_CELL_NAND].pdata_size =
+ sizeof(*tcpd->nand_data);
+ tc6393xb_cells[TC6393XB_CELL_FB].platform_data = tcpd->fb_data;
+ tc6393xb_cells[TC6393XB_CELL_FB].pdata_size = sizeof(*tcpd->fb_data);
ret = mfd_add_devices(&dev->dev, dev->id,
tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 94c6c8a..69272e4 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -384,7 +384,8 @@
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
.resources = timberdale_dma_resources,
- .mfd_data = &timb_dma_platform_data,
+ .platform_data = &timb_dma_platform_data,
+ .pdata_size = sizeof(timb_dma_platform_data),
},
{
.name = "timb-uart",
@@ -395,37 +396,43 @@
.name = "xiic-i2c",
.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
.resources = timberdale_xiic_resources,
- .mfd_data = &timberdale_xiic_platform_data,
+ .platform_data = &timberdale_xiic_platform_data,
+ .pdata_size = sizeof(timberdale_xiic_platform_data),
},
{
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
- .mfd_data = &timberdale_gpio_platform_data,
+ .platform_data = &timberdale_gpio_platform_data,
+ .pdata_size = sizeof(timberdale_gpio_platform_data),
},
{
.name = "timb-video",
.num_resources = ARRAY_SIZE(timberdale_video_resources),
.resources = timberdale_video_resources,
- .mfd_data = &timberdale_video_platform_data,
+ .platform_data = &timberdale_video_platform_data,
+ .pdata_size = sizeof(timberdale_video_platform_data),
},
{
.name = "timb-radio",
.num_resources = ARRAY_SIZE(timberdale_radio_resources),
.resources = timberdale_radio_resources,
- .mfd_data = &timberdale_radio_platform_data,
+ .platform_data = &timberdale_radio_platform_data,
+ .pdata_size = sizeof(timberdale_radio_platform_data),
},
{
.name = "xilinx_spi",
.num_resources = ARRAY_SIZE(timberdale_spi_resources),
.resources = timberdale_spi_resources,
- .mfd_data = &timberdale_xspi_platform_data,
+ .platform_data = &timberdale_xspi_platform_data,
+ .pdata_size = sizeof(timberdale_xspi_platform_data),
},
{
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
- .mfd_data = &timberdale_ks8842_platform_data,
+ .platform_data = &timberdale_ks8842_platform_data,
+ .pdata_size = sizeof(timberdale_ks8842_platform_data),
},
};
@@ -434,7 +441,8 @@
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
.resources = timberdale_dma_resources,
- .mfd_data = &timb_dma_platform_data,
+ .platform_data = &timb_dma_platform_data,
+ .pdata_size = sizeof(timb_dma_platform_data),
},
{
.name = "timb-uart",
@@ -450,13 +458,15 @@
.name = "xiic-i2c",
.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
.resources = timberdale_xiic_resources,
- .mfd_data = &timberdale_xiic_platform_data,
+ .platform_data = &timberdale_xiic_platform_data,
+ .pdata_size = sizeof(timberdale_xiic_platform_data),
},
{
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
- .mfd_data = &timberdale_gpio_platform_data,
+ .platform_data = &timberdale_gpio_platform_data,
+ .pdata_size = sizeof(timberdale_gpio_platform_data),
},
{
.name = "timb-mlogicore",
@@ -467,25 +477,29 @@
.name = "timb-video",
.num_resources = ARRAY_SIZE(timberdale_video_resources),
.resources = timberdale_video_resources,
- .mfd_data = &timberdale_video_platform_data,
+ .platform_data = &timberdale_video_platform_data,
+ .pdata_size = sizeof(timberdale_video_platform_data),
},
{
.name = "timb-radio",
.num_resources = ARRAY_SIZE(timberdale_radio_resources),
.resources = timberdale_radio_resources,
- .mfd_data = &timberdale_radio_platform_data,
+ .platform_data = &timberdale_radio_platform_data,
+ .pdata_size = sizeof(timberdale_radio_platform_data),
},
{
.name = "xilinx_spi",
.num_resources = ARRAY_SIZE(timberdale_spi_resources),
.resources = timberdale_spi_resources,
- .mfd_data = &timberdale_xspi_platform_data,
+ .platform_data = &timberdale_xspi_platform_data,
+ .pdata_size = sizeof(timberdale_xspi_platform_data),
},
{
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
- .mfd_data = &timberdale_ks8842_platform_data,
+ .platform_data = &timberdale_ks8842_platform_data,
+ .pdata_size = sizeof(timberdale_ks8842_platform_data),
},
};
@@ -494,7 +508,8 @@
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
.resources = timberdale_dma_resources,
- .mfd_data = &timb_dma_platform_data,
+ .platform_data = &timb_dma_platform_data,
+ .pdata_size = sizeof(timb_dma_platform_data),
},
{
.name = "timb-uart",
@@ -505,31 +520,36 @@
.name = "xiic-i2c",
.num_resources = ARRAY_SIZE(timberdale_xiic_resources),
.resources = timberdale_xiic_resources,
- .mfd_data = &timberdale_xiic_platform_data,
+ .platform_data = &timberdale_xiic_platform_data,
+ .pdata_size = sizeof(timberdale_xiic_platform_data),
},
{
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
- .mfd_data = &timberdale_gpio_platform_data,
+ .platform_data = &timberdale_gpio_platform_data,
+ .pdata_size = sizeof(timberdale_gpio_platform_data),
},
{
.name = "timb-video",
.num_resources = ARRAY_SIZE(timberdale_video_resources),
.resources = timberdale_video_resources,
- .mfd_data = &timberdale_video_platform_data,
+ .platform_data = &timberdale_video_platform_data,
+ .pdata_size = sizeof(timberdale_video_platform_data),
},
{
.name = "timb-radio",
.num_resources = ARRAY_SIZE(timberdale_radio_resources),
.resources = timberdale_radio_resources,
- .mfd_data = &timberdale_radio_platform_data,
+ .platform_data = &timberdale_radio_platform_data,
+ .pdata_size = sizeof(timberdale_radio_platform_data),
},
{
.name = "xilinx_spi",
.num_resources = ARRAY_SIZE(timberdale_spi_resources),
.resources = timberdale_spi_resources,
- .mfd_data = &timberdale_xspi_platform_data,
+ .platform_data = &timberdale_xspi_platform_data,
+ .pdata_size = sizeof(timberdale_xspi_platform_data),
},
};
@@ -538,7 +558,8 @@
.name = "timb-dma",
.num_resources = ARRAY_SIZE(timberdale_dma_resources),
.resources = timberdale_dma_resources,
- .mfd_data = &timb_dma_platform_data,
+ .platform_data = &timb_dma_platform_data,
+ .pdata_size = sizeof(timb_dma_platform_data),
},
{
.name = "timb-uart",
@@ -549,37 +570,43 @@
.name = "ocores-i2c",
.num_resources = ARRAY_SIZE(timberdale_ocores_resources),
.resources = timberdale_ocores_resources,
- .mfd_data = &timberdale_ocores_platform_data,
+ .platform_data = &timberdale_ocores_platform_data,
+ .pdata_size = sizeof(timberdale_ocores_platform_data),
},
{
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
- .mfd_data = &timberdale_gpio_platform_data,
+ .platform_data = &timberdale_gpio_platform_data,
+ .pdata_size = sizeof(timberdale_gpio_platform_data),
},
{
.name = "timb-video",
.num_resources = ARRAY_SIZE(timberdale_video_resources),
.resources = timberdale_video_resources,
- .mfd_data = &timberdale_video_platform_data,
+ .platform_data = &timberdale_video_platform_data,
+ .pdata_size = sizeof(timberdale_video_platform_data),
},
{
.name = "timb-radio",
.num_resources = ARRAY_SIZE(timberdale_radio_resources),
.resources = timberdale_radio_resources,
- .mfd_data = &timberdale_radio_platform_data,
+ .platform_data = &timberdale_radio_platform_data,
+ .pdata_size = sizeof(timberdale_radio_platform_data),
},
{
.name = "xilinx_spi",
.num_resources = ARRAY_SIZE(timberdale_spi_resources),
.resources = timberdale_spi_resources,
- .mfd_data = &timberdale_xspi_platform_data,
+ .platform_data = &timberdale_xspi_platform_data,
+ .pdata_size = sizeof(timberdale_xspi_platform_data),
},
{
.name = "ks8842",
.num_resources = ARRAY_SIZE(timberdale_eth_resources),
.resources = timberdale_eth_resources,
- .mfd_data = &timberdale_ks8842_platform_data,
+ .platform_data = &timberdale_ks8842_platform_data,
+ .pdata_size = sizeof(timberdale_ks8842_platform_data),
},
};
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index 46d8205..a293b97 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -183,7 +183,8 @@
/* Set up and register the platform devices. */
for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) {
/* One state holder for all drivers, this is simple */
- tps6105x_cells[i].mfd_data = tps6105x;
+ tps6105x_cells[i].platform_data = tps6105x;
+ tps6105x_cells[i].pdata_size = sizeof(*tps6105x);
}
ret = mfd_add_devices(&client->dev, 0, tps6105x_cells,
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index b600808..bba26d9 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -270,8 +270,8 @@
{
struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
- __tps6586x_write(tps6586x->client, TPS6586X_GPIOSET2,
- value << offset);
+ tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2,
+ value << offset, 1 << offset);
}
static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c
new file mode 100644
index 0000000..2bfad5c
--- /dev/null
+++ b/drivers/mfd/tps65910-irq.c
@@ -0,0 +1,218 @@
+/*
+ * tps65910-irq.c -- TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65910.h>
+
+static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
+ int irq)
+{
+ return (irq - tps65910->irq_base);
+}
+
+/*
+ * This is a threaded IRQ handler so can access I2C/SPI. Since all
+ * interrupts are clear on read the IRQ line will be reasserted and
+ * the physical IRQ will be handled again if another interrupt is
+ * asserted while we run - in the normal course of events this is a
+ * rare occurrence so we save I2C/SPI reads. We're also assuming that
+ * it's rare to get lots of interrupts firing simultaneously so try to
+ * minimise I/O.
+ */
+static irqreturn_t tps65910_irq(int irq, void *irq_data)
+{
+ struct tps65910 *tps65910 = irq_data;
+ u32 irq_sts;
+ u32 irq_mask;
+ u8 reg;
+ int i;
+
+ tps65910->read(tps65910, TPS65910_INT_STS, 1, ®);
+ irq_sts = reg;
+ tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®);
+ irq_sts |= reg << 8;
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65911:
+ tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®);
+ irq_sts |= reg << 16;
+ }
+
+ tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®);
+ irq_mask = reg;
+ tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®);
+ irq_mask |= reg << 8;
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65911:
+ tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®);
+ irq_mask |= reg << 16;
+ }
+
+ irq_sts &= ~irq_mask;
+
+ if (!irq_sts)
+ return IRQ_NONE;
+
+ for (i = 0; i < tps65910->irq_num; i++) {
+
+ if (!(irq_sts & (1 << i)))
+ continue;
+
+ handle_nested_irq(tps65910->irq_base + i);
+ }
+
+ /* Write the STS register back to clear IRQs we handled */
+ reg = irq_sts & 0xFF;
+ irq_sts >>= 8;
+ tps65910->write(tps65910, TPS65910_INT_STS, 1, ®);
+ reg = irq_sts & 0xFF;
+ tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®);
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65911:
+ reg = irq_sts >> 8;
+ tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void tps65910_irq_lock(struct irq_data *data)
+{
+ struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&tps65910->irq_lock);
+}
+
+static void tps65910_irq_sync_unlock(struct irq_data *data)
+{
+ struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+ u32 reg_mask;
+ u8 reg;
+
+ tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®);
+ reg_mask = reg;
+ tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®);
+ reg_mask |= reg << 8;
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65911:
+ tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®);
+ reg_mask |= reg << 16;
+ }
+
+ if (tps65910->irq_mask != reg_mask) {
+ reg = tps65910->irq_mask & 0xFF;
+ tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®);
+ reg = tps65910->irq_mask >> 8 & 0xFF;
+ tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®);
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65911:
+ reg = tps65910->irq_mask >> 16;
+ tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®);
+ }
+ }
+ mutex_unlock(&tps65910->irq_lock);
+}
+
+static void tps65910_irq_enable(struct irq_data *data)
+{
+ struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+
+ tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq));
+}
+
+static void tps65910_irq_disable(struct irq_data *data)
+{
+ struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
+
+ tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));
+}
+
+static struct irq_chip tps65910_irq_chip = {
+ .name = "tps65910",
+ .irq_bus_lock = tps65910_irq_lock,
+ .irq_bus_sync_unlock = tps65910_irq_sync_unlock,
+ .irq_disable = tps65910_irq_disable,
+ .irq_enable = tps65910_irq_enable,
+};
+
+int tps65910_irq_init(struct tps65910 *tps65910, int irq,
+ struct tps65910_platform_data *pdata)
+{
+ int ret, cur_irq;
+ int flags = IRQF_ONESHOT;
+
+ if (!irq) {
+ dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n");
+ return -EINVAL;
+ }
+
+ if (!pdata || !pdata->irq_base) {
+ dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n");
+ return -EINVAL;
+ }
+
+ tps65910->irq_mask = 0xFFFFFF;
+
+ mutex_init(&tps65910->irq_lock);
+ tps65910->chip_irq = irq;
+ tps65910->irq_base = pdata->irq_base;
+
+ switch (tps65910_chip_id(tps65910)) {
+ case TPS65910:
+ tps65910->irq_num = TPS65910_NUM_IRQ;
+ case TPS65911:
+ tps65910->irq_num = TPS65911_NUM_IRQ;
+ }
+
+ /* Register with genirq */
+ for (cur_irq = tps65910->irq_base;
+ cur_irq < tps65910->irq_num + tps65910->irq_base;
+ cur_irq++) {
+ irq_set_chip_data(cur_irq, tps65910);
+ irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+
+ /* ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so. */
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
+ "tps65910", tps65910);
+
+ irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+
+ if (ret != 0)
+ dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret);
+
+ return ret;
+}
+
+int tps65910_irq_exit(struct tps65910 *tps65910)
+{
+ free_irq(tps65910->chip_irq, tps65910);
+ return 0;
+}
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
new file mode 100644
index 0000000..2229e66
--- /dev/null
+++ b/drivers/mfd/tps65910.c
@@ -0,0 +1,229 @@
+/*
+ * tps65910.c -- TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65910.h>
+
+static struct mfd_cell tps65910s[] = {
+ {
+ .name = "tps65910-pmic",
+ },
+ {
+ .name = "tps65910-rtc",
+ },
+ {
+ .name = "tps65910-power",
+ },
+};
+
+
+static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,
+ int bytes, void *dest)
+{
+ struct i2c_client *i2c = tps65910->i2c_client;
+ struct i2c_msg xfer[2];
+ int ret;
+
+ /* Write register */
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 1;
+ xfer[0].buf = ®
+
+ /* Read data */
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = bytes;
+ xfer[1].buf = dest;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+
+ return ret;
+}
+
+static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg,
+ int bytes, void *src)
+{
+ struct i2c_client *i2c = tps65910->i2c_client;
+ /* we add 1 byte for device register */
+ u8 msg[TPS65910_MAX_REGISTER + 1];
+ int ret;
+
+ if (bytes > TPS65910_MAX_REGISTER)
+ return -EINVAL;
+
+ msg[0] = reg;
+ memcpy(&msg[1], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 1);
+ if (ret < 0)
+ return ret;
+ if (ret != bytes + 1)
+ return -EIO;
+ return 0;
+}
+
+int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&tps65910->io_mutex);
+ err = tps65910_i2c_read(tps65910, reg, 1, &data);
+ if (err) {
+ dev_err(tps65910->dev, "read from reg %x failed\n", reg);
+ goto out;
+ }
+
+ data |= mask;
+ err = tps65910_i2c_write(tps65910, reg, 1, &data);
+ if (err)
+ dev_err(tps65910->dev, "write to reg %x failed\n", reg);
+
+out:
+ mutex_unlock(&tps65910->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65910_set_bits);
+
+int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&tps65910->io_mutex);
+ err = tps65910_i2c_read(tps65910, reg, 1, &data);
+ if (err) {
+ dev_err(tps65910->dev, "read from reg %x failed\n", reg);
+ goto out;
+ }
+
+ data &= mask;
+ err = tps65910_i2c_write(tps65910, reg, 1, &data);
+ if (err)
+ dev_err(tps65910->dev, "write to reg %x failed\n", reg);
+
+out:
+ mutex_unlock(&tps65910->io_mutex);
+ return err;
+}
+EXPORT_SYMBOL_GPL(tps65910_clear_bits);
+
+static int tps65910_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tps65910 *tps65910;
+ struct tps65910_board *pmic_plat_data;
+ struct tps65910_platform_data *init_data;
+ int ret = 0;
+
+ pmic_plat_data = dev_get_platdata(&i2c->dev);
+ if (!pmic_plat_data)
+ return -EINVAL;
+
+ init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
+ if (init_data == NULL)
+ return -ENOMEM;
+
+ init_data->irq = pmic_plat_data->irq;
+ init_data->irq_base = pmic_plat_data->irq;
+
+ tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
+ if (tps65910 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, tps65910);
+ tps65910->dev = &i2c->dev;
+ tps65910->i2c_client = i2c;
+ tps65910->id = id->driver_data;
+ tps65910->read = tps65910_i2c_read;
+ tps65910->write = tps65910_i2c_write;
+ mutex_init(&tps65910->io_mutex);
+
+ ret = mfd_add_devices(tps65910->dev, -1,
+ tps65910s, ARRAY_SIZE(tps65910s),
+ NULL, 0);
+ if (ret < 0)
+ goto err;
+
+ tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
+
+ ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ mfd_remove_devices(tps65910->dev);
+ kfree(tps65910);
+ return ret;
+}
+
+static int tps65910_i2c_remove(struct i2c_client *i2c)
+{
+ struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(tps65910->dev);
+ kfree(tps65910);
+
+ return 0;
+}
+
+static const struct i2c_device_id tps65910_i2c_id[] = {
+ { "tps65910", TPS65910 },
+ { "tps65911", TPS65911 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tps65910_i2c_id);
+
+
+static struct i2c_driver tps65910_i2c_driver = {
+ .driver = {
+ .name = "tps65910",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65910_i2c_probe,
+ .remove = tps65910_i2c_remove,
+ .id_table = tps65910_i2c_id,
+};
+
+static int __init tps65910_i2c_init(void)
+{
+ return i2c_add_driver(&tps65910_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps65910_i2c_init);
+
+static void __exit tps65910_i2c_exit(void)
+{
+ i2c_del_driver(&tps65910_i2c_driver);
+}
+module_exit(tps65910_i2c_exit);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6591x chip family multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
new file mode 100644
index 0000000..3d2dc56
--- /dev/null
+++ b/drivers/mfd/tps65911-comparator.c
@@ -0,0 +1,188 @@
+/*
+ * tps65910.c -- TI TPS6591x
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65910.h>
+
+#define COMP 0
+#define COMP1 1
+#define COMP2 2
+
+/* Comparator 1 voltage selection table in milivolts */
+static const u16 COMP_VSEL_TABLE[] = {
+ 0, 2500, 2500, 2500, 2500, 2550, 2600, 2650,
+ 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050,
+ 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450,
+ 3500,
+};
+
+struct comparator {
+ const char *name;
+ int reg;
+ int uV_max;
+ const u16 *vsel_table;
+};
+
+static struct comparator tps_comparators[] = {
+ {
+ .name = "COMP1",
+ .reg = TPS65911_VMBCH,
+ .uV_max = 3500,
+ .vsel_table = COMP_VSEL_TABLE,
+ },
+ {
+ .name = "COMP2",
+ .reg = TPS65911_VMBCH2,
+ .uV_max = 3500,
+ .vsel_table = COMP_VSEL_TABLE,
+ },
+};
+
+static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage)
+{
+ struct comparator tps_comp = tps_comparators[id];
+ int curr_voltage = 0;
+ int ret;
+ u8 index = 0, val;
+
+ if (id == COMP)
+ return 0;
+
+ while (curr_voltage < tps_comp.uV_max) {
+ curr_voltage = tps_comp.vsel_table[index];
+ if (curr_voltage >= voltage)
+ break;
+ else if (curr_voltage < voltage)
+ index ++;
+ }
+
+ if (curr_voltage > tps_comp.uV_max)
+ return -EINVAL;
+
+ val = index << 1;
+ ret = tps65910->write(tps65910, tps_comp.reg, 1, &val);
+
+ return ret;
+}
+
+static int comp_threshold_get(struct tps65910 *tps65910, int id)
+{
+ struct comparator tps_comp = tps_comparators[id];
+ int ret;
+ u8 val;
+
+ if (id == COMP)
+ return 0;
+
+ ret = tps65910->read(tps65910, tps_comp.reg, 1, &val);
+ if (ret < 0)
+ return ret;
+
+ val >>= 1;
+ return tps_comp.vsel_table[val];
+}
+
+static ssize_t comp_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tps65910 *tps65910 = dev_get_drvdata(dev->parent);
+ struct attribute comp_attr = attr->attr;
+ int id, uVolt;
+
+ if (!strcmp(comp_attr.name, "comp1_threshold"))
+ id = COMP1;
+ else if (!strcmp(comp_attr.name, "comp2_threshold"))
+ id = COMP2;
+ else
+ return -EINVAL;
+
+ uVolt = comp_threshold_get(tps65910, id);
+
+ return sprintf(buf, "%d\n", uVolt);
+}
+
+static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL);
+static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL);
+
+static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
+{
+ struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
+ struct tps65910_platform_data *pdata = dev_get_platdata(tps65910->dev);
+ int ret;
+
+ ret = comp_threshold_set(tps65910, COMP1, pdata->vmbch_threshold);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot set COMP1 threshold\n");
+ return ret;
+ }
+
+ ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot set COMP2 theshold\n");
+ return ret;
+ }
+
+ /* Create sysfs entry */
+ ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n");
+
+ ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n");
+
+ return ret;
+}
+
+static __devexit int tps65911_comparator_remove(struct platform_device *pdev)
+{
+ struct tps65910 *tps65910;
+
+ tps65910 = dev_get_drvdata(pdev->dev.parent);
+
+ return 0;
+}
+
+static struct platform_driver tps65911_comparator_driver = {
+ .driver = {
+ .name = "tps65911-comparator",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65911_comparator_probe,
+ .remove = __devexit_p(tps65911_comparator_remove),
+};
+
+static int __init tps65911_comparator_init(void)
+{
+ return platform_driver_register(&tps65911_comparator_driver);
+}
+subsys_initcall(tps65911_comparator_init);
+
+static void __exit tps65911_comparator_exit(void)
+{
+ platform_driver_unregister(&tps65911_comparator_driver);
+}
+module_exit(tps65911_comparator_exit);
+
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS65911 comparator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65911-comparator");
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 960b5be..b8f2a4e 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -198,6 +198,7 @@
#define TWL6030_BASEADD_GASGAUGE 0x00C0
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0
+#define TWL6025_BASEADD_CHARGER 0x00DA
/* subchip/slave 2 0x4A - DFT */
#define TWL6030_BASEADD_DIEID 0x00C0
@@ -229,6 +230,9 @@
/* is driver active, bound to a chip? */
static bool inuse;
+/* TWL IDCODE Register value */
+static u32 twl_idcode;
+
static unsigned int twl_id;
unsigned int twl_rev(void)
{
@@ -328,6 +332,7 @@
{ SUB_CHIP_ID0, TWL6030_BASEADD_RTC },
{ SUB_CHIP_ID0, TWL6030_BASEADD_MEM },
+ { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER },
};
/*----------------------------------------------------------------------*/
@@ -487,6 +492,58 @@
/*----------------------------------------------------------------------*/
+/**
+ * twl_read_idcode_register - API to read the IDCODE register.
+ *
+ * Unlocks the IDCODE register and read the 32 bit value.
+ */
+static int twl_read_idcode_register(void)
+{
+ int err;
+
+ err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, TWL_EEPROM_R_UNLOCK,
+ REG_UNLOCK_TEST_REG);
+ if (err) {
+ pr_err("TWL4030 Unable to unlock IDCODE registers -%d\n", err);
+ goto fail;
+ }
+
+ err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode),
+ REG_IDCODE_7_0, 4);
+ if (err) {
+ pr_err("TWL4030: unable to read IDCODE -%d\n", err);
+ goto fail;
+ }
+
+ err = twl_i2c_write_u8(TWL4030_MODULE_INTBR, 0x0, REG_UNLOCK_TEST_REG);
+ if (err)
+ pr_err("TWL4030 Unable to relock IDCODE registers -%d\n", err);
+fail:
+ return err;
+}
+
+/**
+ * twl_get_type - API to get TWL Si type.
+ *
+ * Api to get the TWL Si type from IDCODE value.
+ */
+int twl_get_type(void)
+{
+ return TWL_SIL_TYPE(twl_idcode);
+}
+EXPORT_SYMBOL_GPL(twl_get_type);
+
+/**
+ * twl_get_version - API to get TWL Si version.
+ *
+ * Api to get the TWL Si version from IDCODE value.
+ */
+int twl_get_version(void)
+{
+ return TWL_SIL_REV(twl_idcode);
+}
+EXPORT_SYMBOL_GPL(twl_get_version);
+
static struct device *
add_numbered_child(unsigned chip, const char *name, int num,
void *pdata, unsigned pdata_len,
@@ -549,7 +606,7 @@
static struct device *
add_regulator_linked(int num, struct regulator_init_data *pdata,
struct regulator_consumer_supply *consumers,
- unsigned num_consumers)
+ unsigned num_consumers, unsigned long features)
{
unsigned sub_chip_id;
/* regulator framework demands init_data ... */
@@ -561,6 +618,8 @@
pdata->num_consumer_supplies = num_consumers;
}
+ pdata->driver_data = (void *)features;
+
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
return add_numbered_child(sub_chip_id, "twl_reg", num,
@@ -568,9 +627,10 @@
}
static struct device *
-add_regulator(int num, struct regulator_init_data *pdata)
+add_regulator(int num, struct regulator_init_data *pdata,
+ unsigned long features)
{
- return add_regulator_linked(num, pdata, NULL, 0);
+ return add_regulator_linked(num, pdata, NULL, 0, features);
}
/*
@@ -650,17 +710,20 @@
};
child = add_regulator_linked(TWL4030_REG_VUSB1V5,
- &usb_fixed, &usb1v5, 1);
+ &usb_fixed, &usb1v5, 1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator_linked(TWL4030_REG_VUSB1V8,
- &usb_fixed, &usb1v8, 1);
+ &usb_fixed, &usb1v8, 1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator_linked(TWL4030_REG_VUSB3V1,
- &usb_fixed, &usb3v1, 1);
+ &usb_fixed, &usb3v1, 1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
@@ -685,9 +748,8 @@
}
if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
- static struct regulator_consumer_supply usb3v3 = {
- .supply = "vusb",
- };
+ static struct regulator_consumer_supply usb3v3;
+ int regulator;
if (twl_has_regulator()) {
/* this is a template that gets copied */
@@ -700,12 +762,22 @@
| REGULATOR_CHANGE_STATUS,
};
- child = add_regulator_linked(TWL6030_REG_VUSB,
- &usb_fixed, &usb3v3, 1);
+ if (features & TWL6025_SUBCLASS) {
+ usb3v3.supply = "ldousb";
+ regulator = TWL6025_REG_LDOUSB;
+ } else {
+ usb3v3.supply = "vusb";
+ regulator = TWL6030_REG_VUSB;
+ }
+ child = add_regulator_linked(regulator, &usb_fixed,
+ &usb3v3, 1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
}
+ pdata->usb->features = features;
+
child = add_child(0, "twl6030_usb",
pdata->usb, sizeof(*pdata->usb),
true,
@@ -718,7 +790,16 @@
/* we need to connect regulators to this transceiver */
if (twl_has_regulator() && child)
usb3v3.dev = child;
+ } else if (twl_has_regulator() && twl_class_is_6030()) {
+ if (features & TWL6025_SUBCLASS)
+ child = add_regulator(TWL6025_REG_LDOUSB,
+ pdata->ldousb, features);
+ else
+ child = add_regulator(TWL6030_REG_VUSB,
+ pdata->vusb, features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
}
if (twl_has_watchdog() && twl_class_is_4030()) {
@@ -755,46 +836,55 @@
/* twl4030 regulators */
if (twl_has_regulator() && twl_class_is_4030()) {
- child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
+ child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VIO, pdata->vio);
+ child = add_regulator(TWL4030_REG_VIO, pdata->vio,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1);
+ child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2);
+ child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1);
+ child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VDAC, pdata->vdac);
+ child = add_regulator(TWL4030_REG_VDAC, pdata->vdac,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
child = add_regulator((features & TWL4030_VAUX2)
? TWL4030_REG_VAUX2_4030
: TWL4030_REG_VAUX2,
- pdata->vaux2);
+ pdata->vaux2, features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1);
+ child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2);
+ child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig);
+ child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -802,72 +892,152 @@
/* maybe add LDOs that are omitted on cost-reduced parts */
if (twl_has_regulator() && !(features & TPS_SUBSET)
&& twl_class_is_4030()) {
- child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2);
+ child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2);
+ child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VSIM, pdata->vsim);
+ child = add_regulator(TWL4030_REG_VSIM, pdata->vsim,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1);
+ child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3);
+ child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4);
+ child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
}
/* twl6030 regulators */
+ if (twl_has_regulator() && twl_class_is_6030() &&
+ !(features & TWL6025_SUBCLASS)) {
+ child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VPP, pdata->vpp,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VDAC, pdata->vdac,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+ /* 6030 and 6025 share this regulator */
if (twl_has_regulator() && twl_class_is_6030()) {
- child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc);
+ child = add_regulator(TWL6030_REG_VANA, pdata->vana,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
+ /* twl6025 regulators */
+ if (twl_has_regulator() && twl_class_is_6030() &&
+ (features & TWL6025_SUBCLASS)) {
+ child = add_regulator(TWL6025_REG_LDO5, pdata->ldo5,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VPP, pdata->vpp);
+ child = add_regulator(TWL6025_REG_LDO1, pdata->ldo1,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim);
+ child = add_regulator(TWL6025_REG_LDO7, pdata->ldo7,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VANA, pdata->vana);
+ child = add_regulator(TWL6025_REG_LDO6, pdata->ldo6,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio);
+ child = add_regulator(TWL6025_REG_LDOLN, pdata->ldoln,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VDAC, pdata->vdac);
+ child = add_regulator(TWL6025_REG_LDO2, pdata->ldo2,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1);
+ child = add_regulator(TWL6025_REG_LDO4, pdata->ldo4,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2);
+ child = add_regulator(TWL6025_REG_LDO3, pdata->ldo3,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3);
+ child = add_regulator(TWL6025_REG_SMPS3, pdata->smps3,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
- child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg);
+ child = add_regulator(TWL6025_REG_SMPS4, pdata->smps4,
+ features);
if (IS_ERR(child))
return PTR_ERR(child);
+
+ child = add_regulator(TWL6025_REG_VIO, pdata->vio6025,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
}
if (twl_has_bci() && pdata->bci &&
@@ -1014,6 +1184,7 @@
unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
u8 temp;
+ int ret = 0;
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
@@ -1060,6 +1231,12 @@
/* setup clock framework */
clocks_init(&client->dev, pdata->clock);
+ /* read TWL IDCODE Register */
+ if (twl_id == TWL4030_CLASS_ID) {
+ ret = twl_read_idcode_register();
+ WARN(ret < 0, "Error: reading twl_idcode register value\n");
+ }
+
/* load power event scripts */
if (twl_has_power() && pdata->power)
twl4030_power_init(pdata->power);
@@ -1108,6 +1285,7 @@
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
+ { "twl6025", TWL6030_CLASS | TWL6025_SUBCLASS }, /* "Phoenix lite" */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl_ids);
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
index c02fded..2bf4136 100644
--- a/drivers/mfd/twl4030-codec.c
+++ b/drivers/mfd/twl4030-codec.c
@@ -1,7 +1,7 @@
/*
* MFD driver for twl4030 codec submodule
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
@@ -208,13 +208,15 @@
if (pdata->audio) {
cell = &codec->cells[childs];
cell->name = "twl4030-codec";
- cell->mfd_data = pdata->audio;
+ cell->platform_data = pdata->audio;
+ cell->pdata_size = sizeof(*pdata->audio);
childs++;
}
if (pdata->vibra) {
cell = &codec->cells[childs];
cell->name = "twl4030-vibra";
- cell->mfd_data = pdata->vibra;
+ cell->platform_data = pdata->vibra;
+ cell->pdata_size = sizeof(*pdata->vibra);
childs++;
}
@@ -270,6 +272,6 @@
}
module_exit(twl4030_codec_exit);
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index 2c0d4d1..a764676 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -120,7 +120,7 @@
[RES_HFCLKOUT] = 0x8b,
[RES_32KCLKOUT] = 0x8e,
[RES_RESET] = 0x91,
- [RES_Main_Ref] = 0x94,
+ [RES_MAIN_REF] = 0x94,
};
static int __init twl4030_write_script_byte(u8 address, u8 byte)
@@ -448,7 +448,7 @@
goto out;
}
if (tscript->flags & TWL4030_SLEEP_SCRIPT) {
- if (order)
+ if (!order)
pr_warning("TWL4030: Bad order of scripts (sleep "\
"script before wakeup) Leads to boot"\
"failure on some boards\n");
@@ -485,9 +485,9 @@
return err;
}
if (flags & TWL4030_WAKEUP12_SCRIPT) {
- if (err)
err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT,
R_SEQ_ADD_S2A12);
+ if (err)
return err;
}
if (flags & TWL4030_WAKEUP3_SCRIPT) {
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index dfbae34..eb3b5f8 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -76,8 +76,8 @@
USBOTG_INTR_OFFSET, /* Bit 18 ID */
USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */
CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
- CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */
- CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */
+ CHARGERFAULT_INTR_OFFSET, /* Bit 21 EXT_CHRG */
+ CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */
RSV_INTR_OFFSET, /* Bit 23 Reserved */
};
/*----------------------------------------------------------------------*/
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
index 04914f2..d97a869 100644
--- a/drivers/mfd/wl1273-core.c
+++ b/drivers/mfd/wl1273-core.c
@@ -153,7 +153,6 @@
*/
static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
{
- u16 val;
int r;
if (volume > WL1273_MAX_VOLUME)
@@ -217,7 +216,8 @@
cell = &core->cells[children];
cell->name = "wl1273_fm_radio";
- cell->mfd_data = &core;
+ cell->platform_data = &core;
+ cell->pdata_size = sizeof(core);
children++;
core->read = wl1273_fm_read_reg;
@@ -231,7 +231,8 @@
dev_dbg(&client->dev, "%s: Have codec.\n", __func__);
cell->name = "wl1273-codec";
- cell->mfd_data = &core;
+ cell->platform_data = &core;
+ cell->pdata_size = sizeof(core);
children++;
}
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 3fe9a58..265f75f 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -1442,7 +1442,7 @@
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
int rev;
enum wm831x_parent parent;
- int ret;
+ int ret, i;
mutex_init(&wm831x->io_lock);
mutex_init(&wm831x->key_lock);
@@ -1581,6 +1581,17 @@
}
}
+ if (pdata) {
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
+ if (!pdata->gpio_defaults[i])
+ continue;
+
+ wm831x_reg_write(wm831x,
+ WM831X_GPIO1_CONTROL + i,
+ pdata->gpio_defaults[i] & 0xffff);
+ }
+ }
+
ret = wm831x_irq_init(wm831x, irq);
if (ret != 0)
goto err;
diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c
index 23e66af..42b928e 100644
--- a/drivers/mfd/wm831x-irq.c
+++ b/drivers/mfd/wm831x-irq.c
@@ -515,12 +515,6 @@
0xffff);
}
- if (!irq) {
- dev_warn(wm831x->dev,
- "No interrupt specified - functionality limited\n");
- return 0;
- }
-
if (!pdata || !pdata->irq_base) {
dev_err(wm831x->dev,
"No interrupt base specified, no interrupts\n");
@@ -567,15 +561,22 @@
#endif
}
- ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "wm831x", wm831x);
- if (ret != 0) {
- dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
- irq, ret);
- return ret;
+ if (irq) {
+ ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "wm831x", wm831x);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to request IRQ %d: %d\n",
+ irq, ret);
+ return ret;
+ }
+ } else {
+ dev_warn(wm831x->dev,
+ "No interrupt specified - functionality limited\n");
}
+
+
/* Enable top level interrupts, we mask at secondary level */
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 3a6e78c..597f82e 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -245,7 +245,8 @@
{
struct mfd_cell cell = {
.name = "wm8400-codec",
- .mfd_data = wm8400,
+ .platform_data = wm8400,
+ .pdata_size = sizeof(*wm8400),
};
return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0);
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 74f16f1..b0c5631 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -285,33 +285,26 @@
static int check_and_rewind_pc(char *put_str, char *arg)
{
unsigned long addr = lookup_addr(arg);
+ unsigned long ip;
int offset = 0;
kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs,
NUMREGBYTES);
gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
- v2printk("Stopped at IP: %lx\n", instruction_pointer(&kgdbts_regs));
-#ifdef CONFIG_X86
- /* On x86 a breakpoint stop requires it to be decremented */
- if (addr + 1 == kgdbts_regs.ip)
- offset = -1;
-#elif defined(CONFIG_SUPERH)
- /* On SUPERH a breakpoint stop requires it to be decremented */
- if (addr + 2 == kgdbts_regs.pc)
- offset = -2;
+ ip = instruction_pointer(&kgdbts_regs);
+ v2printk("Stopped at IP: %lx\n", ip);
+#ifdef GDB_ADJUSTS_BREAK_OFFSET
+ /* On some arches, a breakpoint stop requires it to be decremented */
+ if (addr + BREAK_INSTR_SIZE == ip)
+ offset = -BREAK_INSTR_SIZE;
#endif
- if (strcmp(arg, "silent") &&
- instruction_pointer(&kgdbts_regs) + offset != addr) {
+ if (strcmp(arg, "silent") && ip + offset != addr) {
eprintk("kgdbts: BP mismatch %lx expected %lx\n",
- instruction_pointer(&kgdbts_regs) + offset, addr);
+ ip + offset, addr);
return 1;
}
-#ifdef CONFIG_X86
- /* On x86 adjust the instruction pointer if needed */
- kgdbts_regs.ip += offset;
-#elif defined(CONFIG_SUPERH)
- kgdbts_regs.pc += offset;
-#endif
+ /* Readjust the instruction pointer if needed */
+ instruction_pointer_set(&kgdbts_regs, ip + offset);
return 0;
}
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 14479f9..8d185de 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -69,7 +69,7 @@
if (pdev->num_resources != 2)
goto out;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata || !pdata->hclk)
goto out;
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index 14c5787..c004e47 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -372,7 +372,7 @@
static int tmio_probe(struct platform_device *dev)
{
- struct tmio_nand_data *data = mfd_get_data(dev);
+ struct tmio_nand_data *data = dev->dev.platform_data;
struct resource *fcr = platform_get_resource(dev,
IORESOURCE_MEM, 0);
struct resource *ccr = platform_get_resource(dev,
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index 587fba4..f1942ca 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/netdevice.h>
#include <linux/can.h>
@@ -1644,7 +1643,7 @@
struct device *dev;
int ret;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata)
return -ENXIO;
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index a3c0dc9..9537aaa 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -69,7 +69,7 @@
static const char bc_drvname[] = "baycom_epp";
static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n"
-"baycom_epp: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
+"baycom_epp: version 0.7\n";
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c
index 5f5af9a..279d229 100644
--- a/drivers/net/hamradio/baycom_par.c
+++ b/drivers/net/hamradio/baycom_par.c
@@ -102,7 +102,7 @@
static const char bc_drvname[] = "baycom_par";
static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
-"baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n";
+"baycom_par: version 0.9\n";
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index 3e25f10..99cdce3 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -92,7 +92,7 @@
static const char bc_drvname[] = "baycom_ser_fdx";
static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
-"baycom_ser_fdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n";
+"baycom_ser_fdx: version 0.10\n";
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c
index 1686f6d..d92fe6c 100644
--- a/drivers/net/hamradio/baycom_ser_hdx.c
+++ b/drivers/net/hamradio/baycom_ser_hdx.c
@@ -80,7 +80,7 @@
static const char bc_drvname[] = "baycom_ser_hdx";
static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
-"baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n";
+"baycom_ser_hdx: version 0.10\n";
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index 5b37579..a4a3516 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -749,7 +749,7 @@
static int __init hdlcdrv_init_driver(void)
{
printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n");
- printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n");
+ printk(KERN_INFO "hdlcdrv: version 0.8\n");
return 0;
}
diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c
index f0d8346..4d40626 100644
--- a/drivers/net/ks8842.c
+++ b/drivers/net/ks8842.c
@@ -26,7 +26,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
@@ -1146,7 +1145,7 @@
struct resource *iomem;
struct net_device *netdev;
struct ks8842_adapter *adapter;
- struct ks8842_platform_data *pdata = mfd_get_data(pdev);
+ struct ks8842_platform_data *pdata = pdev->dev.platform_data;
u16 id;
unsigned i;
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 737b59f..9617d3d 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -3242,8 +3242,7 @@
rcsdate++;
tmp = strrchr(rcsdate, ' ');
*tmp = '\0';
- printk(KERN_INFO "Cyclades-PC300 driver %s %s (built %s %s)\n",
- rcsvers, rcsdate, __DATE__, __TIME__);
+ printk(KERN_INFO "Cyclades-PC300 driver %s %s\n", rcsvers, rcsdate);
} /* show_version */
static const struct net_device_ops cpc_netdev_ops = {
diff --git a/drivers/parport/parport_ip32.c b/drivers/parport/parport_ip32.c
index d3d7809..0dc34f1 100644
--- a/drivers/parport/parport_ip32.c
+++ b/drivers/parport/parport_ip32.c
@@ -2203,7 +2203,6 @@
static int __init parport_ip32_init(void)
{
pr_info(PPIP32 "SGI IP32 built-in parallel port driver v0.6\n");
- pr_debug1(PPIP32 "Compiled on %s, %s\n", __DATE__, __TIME__);
this_port = parport_ip32_probe_port();
return IS_ERR(this_port) ? PTR_ERR(this_port) : 0;
}
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 52a462f..e57b50b 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -68,6 +68,13 @@
help
Say Y here to enable support for batteries with ds2760 chip.
+config BATTERY_DS2780
+ tristate "DS2780 battery driver"
+ select W1
+ select W1_SLAVE_DS2780
+ help
+ Say Y here to enable support for batteries with ds2780 chip.
+
config BATTERY_DS2782
tristate "DS2782/DS2786 standalone gas-gauge"
depends on I2C
@@ -203,6 +210,15 @@
Say Y to enable support for USB Charger Detection with
ISP1707/ISP1704 USB transceivers.
+config CHARGER_MAX8903
+ tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power"
+ depends on GENERIC_HARDIRQS
+ help
+ Say Y to enable support for the MAX8903 DC-DC charger and sysfs.
+ The driver supports controlling charger-enable and current-limit
+ pins based on the status of charger connections with interrupt
+ handlers.
+
config CHARGER_TWL4030
tristate "OMAP TWL4030 BCI charger driver"
depends on TWL4030_CORE
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 8385bfa..009a90f 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -15,6 +15,7 @@
obj-$(CONFIG_TEST_POWER) += test_power.o
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
+obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
@@ -32,5 +33,6 @@
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
+obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index 59e68db..bb16f5b 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -4,6 +4,7 @@
* Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
* Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
* Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
*
* Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
*
@@ -76,7 +77,7 @@
int time_to_empty_avg;
int time_to_full;
int charge_full;
- int charge_counter;
+ int cycle_count;
int capacity;
int flags;
@@ -115,7 +116,7 @@
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_ENERGY_NOW,
};
@@ -267,7 +268,7 @@
cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);
cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);
cache.charge_full = bq27x00_battery_read_lmd(di);
- cache.charge_counter = bq27x00_battery_read_cyct(di);
+ cache.cycle_count = bq27x00_battery_read_cyct(di);
if (!is_bq27500)
cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false);
@@ -496,8 +497,8 @@
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
ret = bq27x00_simple_value(di->charge_design_full, val);
break;
- case POWER_SUPPLY_PROP_CHARGE_COUNTER:
- ret = bq27x00_simple_value(di->cache.charge_counter, val);
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ ret = bq27x00_simple_value(di->cache.cycle_count, val);
break;
case POWER_SUPPLY_PROP_ENERGY_NOW:
ret = bq27x00_battery_energy(di, val);
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c
index e534290..f2c9cc3 100644
--- a/drivers/power/ds2760_battery.c
+++ b/drivers/power/ds2760_battery.c
@@ -86,7 +86,11 @@
920, /* NEC */
1440, /* Samsung */
1440, /* BYD */
+#ifdef CONFIG_MACH_H4700
+ 1800, /* HP iPAQ hx4700 3.7V 1800mAh (359113-001) */
+#else
1440, /* Lishen */
+#endif
1440, /* NEC */
2880, /* Samsung */
2880, /* BYD */
@@ -186,7 +190,7 @@
scale[0] = di->full_active_uAh;
for (i = 1; i < 5; i++)
- scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i];
+ scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 1 + i];
di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10);
di->full_active_uAh *= 1000; /* convert to µAh */
diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c
new file mode 100644
index 0000000..1fefe82
--- /dev/null
+++ b/drivers/power/ds2780_battery.c
@@ -0,0 +1,853 @@
+/*
+ * 1-wire client/driver for the Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on ds2760_battery and ds2782_battery drivers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/param.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+
+#include "../w1/w1.h"
+#include "../w1/slaves/w1_ds2780.h"
+
+/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
+#define DS2780_CURRENT_UNITS 1563
+/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */
+#define DS2780_CHARGE_UNITS 6250
+/* Number of bytes in user EEPROM space */
+#define DS2780_USER_EEPROM_SIZE (DS2780_EEPROM_BLOCK0_END - \
+ DS2780_EEPROM_BLOCK0_START + 1)
+/* Number of bytes in parameter EEPROM space */
+#define DS2780_PARAM_EEPROM_SIZE (DS2780_EEPROM_BLOCK1_END - \
+ DS2780_EEPROM_BLOCK1_START + 1)
+
+struct ds2780_device_info {
+ struct device *dev;
+ struct power_supply bat;
+ struct device *w1_dev;
+};
+
+enum current_types {
+ CURRENT_NOW,
+ CURRENT_AVG,
+};
+
+static const char model[] = "DS2780";
+static const char manufacturer[] = "Maxim/Dallas";
+
+static inline struct ds2780_device_info *to_ds2780_device_info(
+ struct power_supply *psy)
+{
+ return container_of(psy, struct ds2780_device_info, bat);
+}
+
+static inline struct power_supply *to_power_supply(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+static inline int ds2780_read8(struct device *dev, u8 *val, int addr)
+{
+ return w1_ds2780_io(dev, val, addr, sizeof(u8), 0);
+}
+
+static int ds2780_read16(struct device *dev, s16 *val, int addr)
+{
+ int ret;
+ u8 raw[2];
+
+ ret = w1_ds2780_io(dev, raw, addr, sizeof(u8) * 2, 0);
+ if (ret < 0)
+ return ret;
+
+ *val = (raw[0] << 8) | raw[1];
+
+ return 0;
+}
+
+static inline int ds2780_read_block(struct device *dev, u8 *val, int addr,
+ size_t count)
+{
+ return w1_ds2780_io(dev, val, addr, count, 0);
+}
+
+static inline int ds2780_write(struct device *dev, u8 *val, int addr,
+ size_t count)
+{
+ return w1_ds2780_io(dev, val, addr, count, 1);
+}
+
+static inline int ds2780_store_eeprom(struct device *dev, int addr)
+{
+ return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_COPY_DATA);
+}
+
+static inline int ds2780_recall_eeprom(struct device *dev, int addr)
+{
+ return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_RECALL_DATA);
+}
+
+static int ds2780_save_eeprom(struct ds2780_device_info *dev_info, int reg)
+{
+ int ret;
+
+ ret = ds2780_store_eeprom(dev_info->w1_dev, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = ds2780_recall_eeprom(dev_info->w1_dev, reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Set sense resistor value in mhos */
+static int ds2780_set_sense_register(struct ds2780_device_info *dev_info,
+ u8 conductance)
+{
+ int ret;
+
+ ret = ds2780_write(dev_info->w1_dev, &conductance,
+ DS2780_RSNSP_REG, sizeof(u8));
+ if (ret < 0)
+ return ret;
+
+ return ds2780_save_eeprom(dev_info, DS2780_RSNSP_REG);
+}
+
+/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2780_get_rsgain_register(struct ds2780_device_info *dev_info,
+ u16 *rsgain)
+{
+ return ds2780_read16(dev_info->w1_dev, rsgain, DS2780_RSGAIN_MSB_REG);
+}
+
+/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */
+static int ds2780_set_rsgain_register(struct ds2780_device_info *dev_info,
+ u16 rsgain)
+{
+ int ret;
+ u8 raw[] = {rsgain >> 8, rsgain & 0xFF};
+
+ ret = ds2780_write(dev_info->w1_dev, raw,
+ DS2780_RSGAIN_MSB_REG, sizeof(u8) * 2);
+ if (ret < 0)
+ return ret;
+
+ return ds2780_save_eeprom(dev_info, DS2780_RSGAIN_MSB_REG);
+}
+
+static int ds2780_get_voltage(struct ds2780_device_info *dev_info,
+ int *voltage_uV)
+{
+ int ret;
+ s16 voltage_raw;
+
+ /*
+ * The voltage value is located in 10 bits across the voltage MSB
+ * and LSB registers in two's compliment form
+ * Sign bit of the voltage value is in bit 7 of the voltage MSB register
+ * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the
+ * voltage MSB register
+ * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the
+ * voltage LSB register
+ */
+ ret = ds2780_read16(dev_info->w1_dev, &voltage_raw,
+ DS2780_VOLT_MSB_REG);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * DS2780 reports voltage in units of 4.88mV, but the battery class
+ * reports in units of uV, so convert by multiplying by 4880.
+ */
+ *voltage_uV = (voltage_raw / 32) * 4880;
+ return 0;
+}
+
+static int ds2780_get_temperature(struct ds2780_device_info *dev_info,
+ int *temperature)
+{
+ int ret;
+ s16 temperature_raw;
+
+ /*
+ * The temperature value is located in 10 bits across the temperature
+ * MSB and LSB registers in two's compliment form
+ * Sign bit of the temperature value is in bit 7 of the temperature
+ * MSB register
+ * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the
+ * temperature MSB register
+ * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the
+ * temperature LSB register
+ */
+ ret = ds2780_read16(dev_info->w1_dev, &temperature_raw,
+ DS2780_TEMP_MSB_REG);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Temperature is measured in units of 0.125 degrees celcius, the
+ * power_supply class measures temperature in tenths of degrees
+ * celsius. The temperature value is stored as a 10 bit number, plus
+ * sign in the upper bits of a 16 bit register.
+ */
+ *temperature = ((temperature_raw / 32) * 125) / 100;
+ return 0;
+}
+
+static int ds2780_get_current(struct ds2780_device_info *dev_info,
+ enum current_types type, int *current_uA)
+{
+ int ret, sense_res;
+ s16 current_raw;
+ u8 sense_res_raw, reg_msb;
+
+ /*
+ * The units of measurement for current are dependent on the value of
+ * the sense resistor.
+ */
+ ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG);
+ if (ret < 0)
+ return ret;
+
+ if (sense_res_raw == 0) {
+ dev_err(dev_info->dev, "sense resistor value is 0\n");
+ return -ENXIO;
+ }
+ sense_res = 1000 / sense_res_raw;
+
+ if (type == CURRENT_NOW)
+ reg_msb = DS2780_CURRENT_MSB_REG;
+ else if (type == CURRENT_AVG)
+ reg_msb = DS2780_IAVG_MSB_REG;
+ else
+ return -EINVAL;
+
+ /*
+ * The current value is located in 16 bits across the current MSB
+ * and LSB registers in two's compliment form
+ * Sign bit of the current value is in bit 7 of the current MSB register
+ * Bits 14 - 8 of the current value are in bits 6 - 0 of the current
+ * MSB register
+ * Bits 7 - 0 of the current value are in bits 7 - 0 of the current
+ * LSB register
+ */
+ ret = ds2780_read16(dev_info->w1_dev, ¤t_raw, reg_msb);
+ if (ret < 0)
+ return ret;
+
+ *current_uA = current_raw * (DS2780_CURRENT_UNITS / sense_res);
+ return 0;
+}
+
+static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info,
+ int *accumulated_current)
+{
+ int ret, sense_res;
+ s16 current_raw;
+ u8 sense_res_raw;
+
+ /*
+ * The units of measurement for accumulated current are dependent on
+ * the value of the sense resistor.
+ */
+ ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG);
+ if (ret < 0)
+ return ret;
+
+ if (sense_res_raw == 0) {
+ dev_err(dev_info->dev, "sense resistor value is 0\n");
+ return -ENXIO;
+ }
+ sense_res = 1000 / sense_res_raw;
+
+ /*
+ * The ACR value is located in 16 bits across the ACR MSB and
+ * LSB registers
+ * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR
+ * MSB register
+ * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR
+ * LSB register
+ */
+ ret = ds2780_read16(dev_info->w1_dev, ¤t_raw, DS2780_ACR_MSB_REG);
+ if (ret < 0)
+ return ret;
+
+ *accumulated_current = current_raw * (DS2780_CHARGE_UNITS / sense_res);
+ return 0;
+}
+
+static int ds2780_get_capacity(struct ds2780_device_info *dev_info,
+ int *capacity)
+{
+ int ret;
+ u8 raw;
+
+ ret = ds2780_read8(dev_info->w1_dev, &raw, DS2780_RARC_REG);
+ if (ret < 0)
+ return ret;
+
+ *capacity = raw;
+ return raw;
+}
+
+static int ds2780_get_status(struct ds2780_device_info *dev_info, int *status)
+{
+ int ret, current_uA, capacity;
+
+ ret = ds2780_get_current(dev_info, CURRENT_NOW, ¤t_uA);
+ if (ret < 0)
+ return ret;
+
+ ret = ds2780_get_capacity(dev_info, &capacity);
+ if (ret < 0)
+ return ret;
+
+ if (capacity == 100)
+ *status = POWER_SUPPLY_STATUS_FULL;
+ else if (current_uA == 0)
+ *status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (current_uA < 0)
+ *status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ *status = POWER_SUPPLY_STATUS_CHARGING;
+
+ return 0;
+}
+
+static int ds2780_get_charge_now(struct ds2780_device_info *dev_info,
+ int *charge_now)
+{
+ int ret;
+ u16 charge_raw;
+
+ /*
+ * The RAAC value is located in 16 bits across the RAAC MSB and
+ * LSB registers
+ * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC
+ * MSB register
+ * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC
+ * LSB register
+ */
+ ret = ds2780_read16(dev_info->w1_dev, &charge_raw, DS2780_RAAC_MSB_REG);
+ if (ret < 0)
+ return ret;
+
+ *charge_now = charge_raw * 1600;
+ return 0;
+}
+
+static int ds2780_get_control_register(struct ds2780_device_info *dev_info,
+ u8 *control_reg)
+{
+ return ds2780_read8(dev_info->w1_dev, control_reg, DS2780_CONTROL_REG);
+}
+
+static int ds2780_set_control_register(struct ds2780_device_info *dev_info,
+ u8 control_reg)
+{
+ int ret;
+
+ ret = ds2780_write(dev_info->w1_dev, &control_reg,
+ DS2780_CONTROL_REG, sizeof(u8));
+ if (ret < 0)
+ return ret;
+
+ return ds2780_save_eeprom(dev_info, DS2780_CONTROL_REG);
+}
+
+static int ds2780_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = ds2780_get_voltage(dev_info, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ ret = ds2780_get_temperature(dev_info, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = model;
+ break;
+
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = manufacturer;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = ds2780_get_current(dev_info, CURRENT_NOW, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ ret = ds2780_get_current(dev_info, CURRENT_AVG, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = ds2780_get_status(dev_info, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ ret = ds2780_get_capacity(dev_info, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ ret = ds2780_get_accumulated_current(dev_info, &val->intval);
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ ret = ds2780_get_charge_now(dev_info, &val->intval);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property ds2780_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static ssize_t ds2780_get_pmod_enabled(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 control_reg;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ /* Get power mode */
+ ret = ds2780_get_control_register(dev_info, &control_reg);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n",
+ !!(control_reg & DS2780_CONTROL_REG_PMOD));
+}
+
+static ssize_t ds2780_set_pmod_enabled(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int ret;
+ u8 control_reg, new_setting;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ /* Set power mode */
+ ret = ds2780_get_control_register(dev_info, &control_reg);
+ if (ret < 0)
+ return ret;
+
+ ret = kstrtou8(buf, 0, &new_setting);
+ if (ret < 0)
+ return ret;
+
+ if ((new_setting != 0) && (new_setting != 1)) {
+ dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n");
+ return -EINVAL;
+ }
+
+ if (new_setting)
+ control_reg |= DS2780_CONTROL_REG_PMOD;
+ else
+ control_reg &= ~DS2780_CONTROL_REG_PMOD;
+
+ ret = ds2780_set_control_register(dev_info, control_reg);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t ds2780_get_sense_resistor_value(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 sense_resistor;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ ret = ds2780_read8(dev_info->w1_dev, &sense_resistor, DS2780_RSNSP_REG);
+ if (ret < 0)
+ return ret;
+
+ ret = sprintf(buf, "%d\n", sense_resistor);
+ return ret;
+}
+
+static ssize_t ds2780_set_sense_resistor_value(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int ret;
+ u8 new_setting;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ ret = kstrtou8(buf, 0, &new_setting);
+ if (ret < 0)
+ return ret;
+
+ ret = ds2780_set_sense_register(dev_info, new_setting);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t ds2780_get_rsgain_setting(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 rsgain;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ ret = ds2780_get_rsgain_register(dev_info, &rsgain);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", rsgain);
+}
+
+static ssize_t ds2780_set_rsgain_setting(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int ret;
+ u16 new_setting;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ ret = kstrtou16(buf, 0, &new_setting);
+ if (ret < 0)
+ return ret;
+
+ /* Gain can only be from 0 to 1.999 in steps of .001 */
+ if (new_setting > 1999) {
+ dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n");
+ return -EINVAL;
+ }
+
+ ret = ds2780_set_rsgain_register(dev_info, new_setting);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t ds2780_get_pio_pin(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 sfr;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ ret = ds2780_read8(dev_info->w1_dev, &sfr, DS2780_SFR_REG);
+ if (ret < 0)
+ return ret;
+
+ ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC);
+ return ret;
+}
+
+static ssize_t ds2780_set_pio_pin(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int ret;
+ u8 new_setting;
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ ret = kstrtou8(buf, 0, &new_setting);
+ if (ret < 0)
+ return ret;
+
+ if ((new_setting != 0) && (new_setting != 1)) {
+ dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n");
+ return -EINVAL;
+ }
+
+ ret = ds2780_write(dev_info->w1_dev, &new_setting,
+ DS2780_SFR_REG, sizeof(u8));
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t ds2780_read_param_eeprom_bin(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ count = min_t(loff_t, count,
+ DS2780_EEPROM_BLOCK1_END -
+ DS2780_EEPROM_BLOCK1_START + 1 - off);
+
+ return ds2780_read_block(dev_info->w1_dev, buf,
+ DS2780_EEPROM_BLOCK1_START + off, count);
+}
+
+static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+ int ret;
+
+ count = min_t(loff_t, count,
+ DS2780_EEPROM_BLOCK1_END -
+ DS2780_EEPROM_BLOCK1_START + 1 - off);
+
+ ret = ds2780_write(dev_info->w1_dev, buf,
+ DS2780_EEPROM_BLOCK1_START + off, count);
+ if (ret < 0)
+ return ret;
+
+ ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK1_START);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static struct bin_attribute ds2780_param_eeprom_bin_attr = {
+ .attr = {
+ .name = "param_eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = DS2780_EEPROM_BLOCK1_END - DS2780_EEPROM_BLOCK1_START + 1,
+ .read = ds2780_read_param_eeprom_bin,
+ .write = ds2780_write_param_eeprom_bin,
+};
+
+static ssize_t ds2780_read_user_eeprom_bin(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+
+ count = min_t(loff_t, count,
+ DS2780_EEPROM_BLOCK0_END -
+ DS2780_EEPROM_BLOCK0_START + 1 - off);
+
+ return ds2780_read_block(dev_info->w1_dev, buf,
+ DS2780_EEPROM_BLOCK0_START + off, count);
+
+}
+
+static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct power_supply *psy = to_power_supply(dev);
+ struct ds2780_device_info *dev_info = to_ds2780_device_info(psy);
+ int ret;
+
+ count = min_t(loff_t, count,
+ DS2780_EEPROM_BLOCK0_END -
+ DS2780_EEPROM_BLOCK0_START + 1 - off);
+
+ ret = ds2780_write(dev_info->w1_dev, buf,
+ DS2780_EEPROM_BLOCK0_START + off, count);
+ if (ret < 0)
+ return ret;
+
+ ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK0_START);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static struct bin_attribute ds2780_user_eeprom_bin_attr = {
+ .attr = {
+ .name = "user_eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = DS2780_EEPROM_BLOCK0_END - DS2780_EEPROM_BLOCK0_START + 1,
+ .read = ds2780_read_user_eeprom_bin,
+ .write = ds2780_write_user_eeprom_bin,
+};
+
+static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled,
+ ds2780_set_pmod_enabled);
+static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR,
+ ds2780_get_sense_resistor_value, ds2780_set_sense_resistor_value);
+static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting,
+ ds2780_set_rsgain_setting);
+static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin,
+ ds2780_set_pio_pin);
+
+
+static struct attribute *ds2780_attributes[] = {
+ &dev_attr_pmod_enabled.attr,
+ &dev_attr_sense_resistor_value.attr,
+ &dev_attr_rsgain_setting.attr,
+ &dev_attr_pio_pin.attr,
+ NULL
+};
+
+static const struct attribute_group ds2780_attr_group = {
+ .attrs = ds2780_attributes,
+};
+
+static int __devinit ds2780_battery_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ds2780_device_info *dev_info;
+
+ dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
+ if (!dev_info) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, dev_info);
+
+ dev_info->dev = &pdev->dev;
+ dev_info->w1_dev = pdev->dev.parent;
+ dev_info->bat.name = dev_name(&pdev->dev);
+ dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ dev_info->bat.properties = ds2780_battery_props;
+ dev_info->bat.num_properties = ARRAY_SIZE(ds2780_battery_props);
+ dev_info->bat.get_property = ds2780_battery_get_property;
+
+ ret = power_supply_register(&pdev->dev, &dev_info->bat);
+ if (ret) {
+ dev_err(dev_info->dev, "failed to register battery\n");
+ goto fail_free_info;
+ }
+
+ ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
+ if (ret) {
+ dev_err(dev_info->dev, "failed to create sysfs group\n");
+ goto fail_unregister;
+ }
+
+ ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj,
+ &ds2780_param_eeprom_bin_attr);
+ if (ret) {
+ dev_err(dev_info->dev,
+ "failed to create param eeprom bin file");
+ goto fail_remove_group;
+ }
+
+ ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj,
+ &ds2780_user_eeprom_bin_attr);
+ if (ret) {
+ dev_err(dev_info->dev,
+ "failed to create user eeprom bin file");
+ goto fail_remove_bin_file;
+ }
+
+ return 0;
+
+fail_remove_bin_file:
+ sysfs_remove_bin_file(&dev_info->bat.dev->kobj,
+ &ds2780_param_eeprom_bin_attr);
+fail_remove_group:
+ sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
+fail_unregister:
+ power_supply_unregister(&dev_info->bat);
+fail_free_info:
+ kfree(dev_info);
+fail:
+ return ret;
+}
+
+static int __devexit ds2780_battery_remove(struct platform_device *pdev)
+{
+ struct ds2780_device_info *dev_info = platform_get_drvdata(pdev);
+
+ /* remove attributes */
+ sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group);
+
+ power_supply_unregister(&dev_info->bat);
+
+ kfree(dev_info);
+ return 0;
+}
+
+MODULE_ALIAS("platform:ds2780-battery");
+
+static struct platform_driver ds2780_battery_driver = {
+ .driver = {
+ .name = "ds2780-battery",
+ },
+ .probe = ds2780_battery_probe,
+ .remove = ds2780_battery_remove,
+};
+
+static int __init ds2780_battery_init(void)
+{
+ return platform_driver_register(&ds2780_battery_driver);
+}
+
+static void __exit ds2780_battery_exit(void)
+{
+ platform_driver_unregister(&ds2780_battery_driver);
+}
+
+module_init(ds2780_battery_init);
+module_exit(ds2780_battery_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver");
diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c
index 25b88ac..718f2c5 100644
--- a/drivers/power/gpio-charger.c
+++ b/drivers/power/gpio-charger.c
@@ -161,12 +161,27 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int gpio_charger_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
+
+ power_supply_changed(&gpio_charger->charger);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, NULL, gpio_charger_resume);
+
static struct platform_driver gpio_charger_driver = {
.probe = gpio_charger_probe,
.remove = __devexit_p(gpio_charger_remove),
.driver = {
.name = "gpio-charger",
.owner = THIS_MODULE,
+ .pm = &gpio_charger_pm_ops,
},
};
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c
index 2ad9b14..f6d72b4 100644
--- a/drivers/power/isp1704_charger.c
+++ b/drivers/power/isp1704_charger.c
@@ -33,6 +33,7 @@
#include <linux/usb/ulpi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/power/isp1704_charger.h>
/* Vendor specific Power Control register */
#define ISP1704_PWR_CTRL 0x3d
@@ -71,6 +72,18 @@
};
/*
+ * Disable/enable the power from the isp1704 if a function for it
+ * has been provided with platform data.
+ */
+static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
+{
+ struct isp1704_charger_data *board = isp->dev->platform_data;
+
+ if (board->set_power)
+ board->set_power(on);
+}
+
+/*
* Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB
* chargers).
*
@@ -222,6 +235,9 @@
mutex_lock(&lock);
+ if (event != USB_EVENT_NONE)
+ isp1704_charger_set_power(isp, 1);
+
switch (event) {
case USB_EVENT_VBUS:
isp->online = true;
@@ -269,6 +285,8 @@
*/
if (isp->otg->gadget)
usb_gadget_disconnect(isp->otg->gadget);
+
+ isp1704_charger_set_power(isp, 0);
break;
case USB_EVENT_ENUMERATED:
if (isp->present)
@@ -394,6 +412,8 @@
isp->dev = &pdev->dev;
platform_set_drvdata(pdev, isp);
+ isp1704_charger_set_power(isp, 1);
+
ret = isp1704_test_ulpi(isp);
if (ret < 0)
goto fail1;
@@ -434,6 +454,7 @@
/* Detect charger if VBUS is valid (the cable was already plugged). */
ret = otg_io_read(isp->otg, ULPI_USB_INT_STS);
+ isp1704_charger_set_power(isp, 0);
if ((ret & ULPI_INT_VBUS_VALID) && !isp->otg->default_a) {
isp->event = USB_EVENT_VBUS;
schedule_work(&isp->work);
@@ -459,6 +480,7 @@
otg_unregister_notifier(isp->otg, &isp->nb);
power_supply_unregister(&isp->psy);
otg_put_transceiver(isp->otg);
+ isp1704_charger_set_power(isp, 0);
kfree(isp);
return 0;
diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c
new file mode 100644
index 0000000..33ff0e3
--- /dev/null
+++ b/drivers/power/max8903_charger.c
@@ -0,0 +1,391 @@
+/*
+ * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/power/max8903_charger.h>
+
+struct max8903_data {
+ struct max8903_pdata *pdata;
+ struct device *dev;
+ struct power_supply psy;
+ bool fault;
+ bool usb_in;
+ bool ta_in;
+};
+
+static enum power_supply_property max8903_charger_props[] = {
+ POWER_SUPPLY_PROP_STATUS, /* Charger status output */
+ POWER_SUPPLY_PROP_ONLINE, /* External power source */
+ POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
+};
+
+static int max8903_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max8903_data *data = container_of(psy,
+ struct max8903_data, psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ if (data->pdata->chg) {
+ if (gpio_get_value(data->pdata->chg) == 0)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (data->usb_in || data->ta_in)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = 0;
+ if (data->usb_in || data->ta_in)
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ if (data->fault)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static irqreturn_t max8903_dcin(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool ta_in;
+ enum power_supply_type old_type;
+
+ ta_in = gpio_get_value(pdata->dok) ? false : true;
+
+ if (ta_in == data->ta_in)
+ return IRQ_HANDLED;
+
+ data->ta_in = ta_in;
+
+ /* Set Current-Limit-Mode 1:DC 0:USB */
+ if (pdata->dcm)
+ gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
+
+ /* Charger Enable / Disable (cen is negated) */
+ if (pdata->cen)
+ gpio_set_value(pdata->cen, ta_in ? 0 :
+ (data->usb_in ? 0 : 1));
+
+ dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
+ "Connected" : "Disconnected");
+
+ old_type = data->psy.type;
+
+ if (data->ta_in)
+ data->psy.type = POWER_SUPPLY_TYPE_MAINS;
+ else if (data->usb_in)
+ data->psy.type = POWER_SUPPLY_TYPE_USB;
+ else
+ data->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ if (old_type != data->psy.type)
+ power_supply_changed(&data->psy);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_usbin(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool usb_in;
+ enum power_supply_type old_type;
+
+ usb_in = gpio_get_value(pdata->uok) ? false : true;
+
+ if (usb_in == data->usb_in)
+ return IRQ_HANDLED;
+
+ data->usb_in = usb_in;
+
+ /* Do not touch Current-Limit-Mode */
+
+ /* Charger Enable / Disable (cen is negated) */
+ if (pdata->cen)
+ gpio_set_value(pdata->cen, usb_in ? 0 :
+ (data->ta_in ? 0 : 1));
+
+ dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
+ "Connected" : "Disconnected");
+
+ old_type = data->psy.type;
+
+ if (data->ta_in)
+ data->psy.type = POWER_SUPPLY_TYPE_MAINS;
+ else if (data->usb_in)
+ data->psy.type = POWER_SUPPLY_TYPE_USB;
+ else
+ data->psy.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ if (old_type != data->psy.type)
+ power_supply_changed(&data->psy);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max8903_fault(int irq, void *_data)
+{
+ struct max8903_data *data = _data;
+ struct max8903_pdata *pdata = data->pdata;
+ bool fault;
+
+ fault = gpio_get_value(pdata->flt) ? false : true;
+
+ if (fault == data->fault)
+ return IRQ_HANDLED;
+
+ data->fault = fault;
+
+ if (fault)
+ dev_err(data->dev, "Charger suffers a fault and stops.\n");
+ else
+ dev_err(data->dev, "Charger recovered from a fault.\n");
+
+ return IRQ_HANDLED;
+}
+
+static __devinit int max8903_probe(struct platform_device *pdev)
+{
+ struct max8903_data *data;
+ struct device *dev = &pdev->dev;
+ struct max8903_pdata *pdata = pdev->dev.platform_data;
+ int ret = 0;
+ int gpio;
+ int ta_in = 0;
+ int usb_in = 0;
+
+ data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL);
+ if (data == NULL) {
+ dev_err(dev, "Cannot allocate memory.\n");
+ return -ENOMEM;
+ }
+ data->pdata = pdata;
+ data->dev = dev;
+ platform_set_drvdata(pdev, data);
+
+ if (pdata->dc_valid == false && pdata->usb_valid == false) {
+ dev_err(dev, "No valid power sources.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (pdata->dc_valid) {
+ if (pdata->dok && gpio_is_valid(pdata->dok) &&
+ pdata->dcm && gpio_is_valid(pdata->dcm)) {
+ gpio = pdata->dok; /* PULL_UPed Interrupt */
+ ta_in = gpio_get_value(gpio) ? 0 : 1;
+
+ gpio = pdata->dcm; /* Output */
+ gpio_set_value(gpio, ta_in);
+ } else {
+ dev_err(dev, "When DC is wired, DOK and DCM should"
+ " be wired as well.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ } else {
+ if (pdata->dcm) {
+ if (gpio_is_valid(pdata->dcm))
+ gpio_set_value(pdata->dcm, 0);
+ else {
+ dev_err(dev, "Invalid pin: dcm.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ }
+
+ if (pdata->usb_valid) {
+ if (pdata->uok && gpio_is_valid(pdata->uok)) {
+ gpio = pdata->uok;
+ usb_in = gpio_get_value(gpio) ? 0 : 1;
+ } else {
+ dev_err(dev, "When USB is wired, UOK should be wired."
+ "as well.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (pdata->cen) {
+ if (gpio_is_valid(pdata->cen)) {
+ gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
+ } else {
+ dev_err(dev, "Invalid pin: cen.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (pdata->chg) {
+ if (!gpio_is_valid(pdata->chg)) {
+ dev_err(dev, "Invalid pin: chg.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (pdata->flt) {
+ if (!gpio_is_valid(pdata->flt)) {
+ dev_err(dev, "Invalid pin: flt.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (pdata->usus) {
+ if (!gpio_is_valid(pdata->usus)) {
+ dev_err(dev, "Invalid pin: usus.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ data->fault = false;
+ data->ta_in = ta_in;
+ data->usb_in = usb_in;
+
+ data->psy.name = "max8903_charger";
+ data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS :
+ ((usb_in) ? POWER_SUPPLY_TYPE_USB :
+ POWER_SUPPLY_TYPE_BATTERY);
+ data->psy.get_property = max8903_get_property;
+ data->psy.properties = max8903_charger_props;
+ data->psy.num_properties = ARRAY_SIZE(max8903_charger_props);
+
+ ret = power_supply_register(dev, &data->psy);
+ if (ret) {
+ dev_err(dev, "failed: power supply register.\n");
+ goto err;
+ }
+
+ if (pdata->dc_valid) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->dok),
+ NULL, max8903_dcin,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 DC IN", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for DC (%d)\n",
+ gpio_to_irq(pdata->dok), ret);
+ goto err_psy;
+ }
+ }
+
+ if (pdata->usb_valid) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->uok),
+ NULL, max8903_usbin,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 USB IN", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for USB (%d)\n",
+ gpio_to_irq(pdata->uok), ret);
+ goto err_dc_irq;
+ }
+ }
+
+ if (pdata->flt) {
+ ret = request_threaded_irq(gpio_to_irq(pdata->flt),
+ NULL, max8903_fault,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "MAX8903 Fault", data);
+ if (ret) {
+ dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
+ gpio_to_irq(pdata->flt), ret);
+ goto err_usb_irq;
+ }
+ }
+
+ return 0;
+
+err_usb_irq:
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+err_dc_irq:
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+err_psy:
+ power_supply_unregister(&data->psy);
+err:
+ kfree(data);
+ return ret;
+}
+
+static __devexit int max8903_remove(struct platform_device *pdev)
+{
+ struct max8903_data *data = platform_get_drvdata(pdev);
+
+ if (data) {
+ struct max8903_pdata *pdata = data->pdata;
+
+ if (pdata->flt)
+ free_irq(gpio_to_irq(pdata->flt), data);
+ if (pdata->usb_valid)
+ free_irq(gpio_to_irq(pdata->uok), data);
+ if (pdata->dc_valid)
+ free_irq(gpio_to_irq(pdata->dok), data);
+ power_supply_unregister(&data->psy);
+ kfree(data);
+ }
+
+ return 0;
+}
+
+static struct platform_driver max8903_driver = {
+ .probe = max8903_probe,
+ .remove = __devexit_p(max8903_remove),
+ .driver = {
+ .name = "max8903-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max8903_init(void)
+{
+ return platform_driver_register(&max8903_driver);
+}
+module_init(max8903_init);
+
+static void __exit max8903_exit(void)
+{
+ platform_driver_unregister(&max8903_driver);
+}
+module_exit(max8903_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MAX8903 Charger Driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_ALIAS("max8903-charger");
diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c
index 8e5aec2..a70e16d 100644
--- a/drivers/power/max8925_power.c
+++ b/drivers/power/max8925_power.c
@@ -425,16 +425,11 @@
static __devinit int max8925_power_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
- struct max8925_platform_data *max8925_pdata;
struct max8925_power_pdata *pdata = NULL;
struct max8925_power_info *info;
int ret;
- if (pdev->dev.parent->platform_data) {
- max8925_pdata = pdev->dev.parent->platform_data;
- pdata = max8925_pdata->power;
- }
-
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"power supply\n");
@@ -447,6 +442,7 @@
info->chip = chip;
info->gpm = chip->i2c;
info->adc = chip->adc;
+ platform_set_drvdata(pdev, info);
info->ac.name = "max8925-ac";
info->ac.type = POWER_SUPPLY_TYPE_MAINS;
@@ -482,8 +478,6 @@
info->topoff_threshold = pdata->topoff_threshold;
info->fast_charge = pdata->fast_charge;
info->set_charger = pdata->set_charger;
- dev_set_drvdata(&pdev->dev, info);
- platform_set_drvdata(pdev, info);
max8925_init_charger(chip, info);
return 0;
diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c
index 0cd9f67..b527c93 100644
--- a/drivers/power/test_power.c
+++ b/drivers/power/test_power.c
@@ -3,6 +3,12 @@
*
* Copyright 2010 Anton Vorontsov <cbouatmailru@gmail.com>
*
+ * Dynamic module parameter code from the Virtual Battery Driver
+ * Copyright (C) 2008 Pylone, Inc.
+ * By: Masashi YOKOTA <yokota@pylone.jp>
+ * Originally found here:
+ * http://downloads.pylone.jp/src/virtual_battery/virtual_battery-0.0.1.tar.bz2
+ *
* 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.
@@ -15,8 +21,12 @@
#include <linux/delay.h>
#include <linux/vermagic.h>
-static int test_power_ac_online = 1;
-static int test_power_battery_status = POWER_SUPPLY_STATUS_CHARGING;
+static int ac_online = 1;
+static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+static int battery_health = POWER_SUPPLY_HEALTH_GOOD;
+static int battery_present = 1; /* true */
+static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION;
+static int battery_capacity = 50;
static int test_power_get_ac_property(struct power_supply *psy,
enum power_supply_property psp,
@@ -24,7 +34,7 @@
{
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
- val->intval = test_power_ac_online;
+ val->intval = ac_online;
break;
default:
return -EINVAL;
@@ -47,22 +57,30 @@
val->strval = UTS_RELEASE;
break;
case POWER_SUPPLY_PROP_STATUS:
- val->intval = test_power_battery_status;
+ val->intval = battery_status;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
break;
case POWER_SUPPLY_PROP_HEALTH:
- val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ val->intval = battery_health;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = battery_present;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ val->intval = battery_technology;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
break;
case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = 50;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = battery_capacity;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = 100;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
@@ -84,9 +102,11 @@
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_FULL,
- POWER_SUPPLY_PROP_CHARGE_EMPTY,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
@@ -118,6 +138,7 @@
},
};
+
static int __init test_power_init(void)
{
int i;
@@ -145,8 +166,8 @@
int i;
/* Let's see how we handle changes... */
- test_power_ac_online = 0;
- test_power_battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ ac_online = 0;
+ battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
power_supply_changed(&test_power_supplies[i]);
pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n",
@@ -158,6 +179,241 @@
}
module_exit(test_power_exit);
+
+
+#define MAX_KEYLENGTH 256
+struct battery_property_map {
+ int value;
+ char const *key;
+};
+
+static struct battery_property_map map_ac_online[] = {
+ { 0, "on" },
+ { 1, "off" },
+ { -1, NULL },
+};
+
+static struct battery_property_map map_status[] = {
+ { POWER_SUPPLY_STATUS_CHARGING, "charging" },
+ { POWER_SUPPLY_STATUS_DISCHARGING, "discharging" },
+ { POWER_SUPPLY_STATUS_NOT_CHARGING, "not-charging" },
+ { POWER_SUPPLY_STATUS_FULL, "full" },
+ { -1, NULL },
+};
+
+static struct battery_property_map map_health[] = {
+ { POWER_SUPPLY_HEALTH_GOOD, "good" },
+ { POWER_SUPPLY_HEALTH_OVERHEAT, "overheat" },
+ { POWER_SUPPLY_HEALTH_DEAD, "dead" },
+ { POWER_SUPPLY_HEALTH_OVERVOLTAGE, "overvoltage" },
+ { POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, "failure" },
+ { -1, NULL },
+};
+
+static struct battery_property_map map_present[] = {
+ { 0, "false" },
+ { 1, "true" },
+ { -1, NULL },
+};
+
+static struct battery_property_map map_technology[] = {
+ { POWER_SUPPLY_TECHNOLOGY_NiMH, "NiMH" },
+ { POWER_SUPPLY_TECHNOLOGY_LION, "LION" },
+ { POWER_SUPPLY_TECHNOLOGY_LIPO, "LIPO" },
+ { POWER_SUPPLY_TECHNOLOGY_LiFe, "LiFe" },
+ { POWER_SUPPLY_TECHNOLOGY_NiCd, "NiCd" },
+ { POWER_SUPPLY_TECHNOLOGY_LiMn, "LiMn" },
+ { -1, NULL },
+};
+
+
+static int map_get_value(struct battery_property_map *map, const char *key,
+ int def_val)
+{
+ char buf[MAX_KEYLENGTH];
+ int cr;
+
+ strncpy(buf, key, MAX_KEYLENGTH);
+ buf[MAX_KEYLENGTH-1] = '\0';
+
+ cr = strnlen(buf, MAX_KEYLENGTH) - 1;
+ if (buf[cr] == '\n')
+ buf[cr] = '\0';
+
+ while (map->key) {
+ if (strncasecmp(map->key, buf, MAX_KEYLENGTH) == 0)
+ return map->value;
+ map++;
+ }
+
+ return def_val;
+}
+
+
+static const char *map_get_key(struct battery_property_map *map, int value,
+ const char *def_key)
+{
+ while (map->key) {
+ if (map->value == value)
+ return map->key;
+ map++;
+ }
+
+ return def_key;
+}
+
+static int param_set_ac_online(const char *key, const struct kernel_param *kp)
+{
+ ac_online = map_get_value(map_ac_online, key, ac_online);
+ power_supply_changed(&test_power_supplies[0]);
+ return 0;
+}
+
+static int param_get_ac_online(char *buffer, const struct kernel_param *kp)
+{
+ strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown"));
+ return strlen(buffer);
+}
+
+static int param_set_battery_status(const char *key,
+ const struct kernel_param *kp)
+{
+ battery_status = map_get_value(map_status, key, battery_status);
+ power_supply_changed(&test_power_supplies[1]);
+ return 0;
+}
+
+static int param_get_battery_status(char *buffer, const struct kernel_param *kp)
+{
+ strcpy(buffer, map_get_key(map_status, battery_status, "unknown"));
+ return strlen(buffer);
+}
+
+static int param_set_battery_health(const char *key,
+ const struct kernel_param *kp)
+{
+ battery_health = map_get_value(map_health, key, battery_health);
+ power_supply_changed(&test_power_supplies[1]);
+ return 0;
+}
+
+static int param_get_battery_health(char *buffer, const struct kernel_param *kp)
+{
+ strcpy(buffer, map_get_key(map_health, battery_health, "unknown"));
+ return strlen(buffer);
+}
+
+static int param_set_battery_present(const char *key,
+ const struct kernel_param *kp)
+{
+ battery_present = map_get_value(map_present, key, battery_present);
+ power_supply_changed(&test_power_supplies[0]);
+ return 0;
+}
+
+static int param_get_battery_present(char *buffer,
+ const struct kernel_param *kp)
+{
+ strcpy(buffer, map_get_key(map_present, battery_present, "unknown"));
+ return strlen(buffer);
+}
+
+static int param_set_battery_technology(const char *key,
+ const struct kernel_param *kp)
+{
+ battery_technology = map_get_value(map_technology, key,
+ battery_technology);
+ power_supply_changed(&test_power_supplies[1]);
+ return 0;
+}
+
+static int param_get_battery_technology(char *buffer,
+ const struct kernel_param *kp)
+{
+ strcpy(buffer,
+ map_get_key(map_technology, battery_technology, "unknown"));
+ return strlen(buffer);
+}
+
+static int param_set_battery_capacity(const char *key,
+ const struct kernel_param *kp)
+{
+ int tmp;
+
+ if (1 != sscanf(key, "%d", &tmp))
+ return -EINVAL;
+
+ battery_capacity = tmp;
+ power_supply_changed(&test_power_supplies[1]);
+ return 0;
+}
+
+#define param_get_battery_capacity param_get_int
+
+
+
+static struct kernel_param_ops param_ops_ac_online = {
+ .set = param_set_ac_online,
+ .get = param_get_ac_online,
+};
+
+static struct kernel_param_ops param_ops_battery_status = {
+ .set = param_set_battery_status,
+ .get = param_get_battery_status,
+};
+
+static struct kernel_param_ops param_ops_battery_present = {
+ .set = param_set_battery_present,
+ .get = param_get_battery_present,
+};
+
+static struct kernel_param_ops param_ops_battery_technology = {
+ .set = param_set_battery_technology,
+ .get = param_get_battery_technology,
+};
+
+static struct kernel_param_ops param_ops_battery_health = {
+ .set = param_set_battery_health,
+ .get = param_get_battery_health,
+};
+
+static struct kernel_param_ops param_ops_battery_capacity = {
+ .set = param_set_battery_capacity,
+ .get = param_get_battery_capacity,
+};
+
+
+#define param_check_ac_online(name, p) __param_check(name, p, void);
+#define param_check_battery_status(name, p) __param_check(name, p, void);
+#define param_check_battery_present(name, p) __param_check(name, p, void);
+#define param_check_battery_technology(name, p) __param_check(name, p, void);
+#define param_check_battery_health(name, p) __param_check(name, p, void);
+#define param_check_battery_capacity(name, p) __param_check(name, p, void);
+
+
+module_param(ac_online, ac_online, 0644);
+MODULE_PARM_DESC(ac_online, "AC charging state <on|off>");
+
+module_param(battery_status, battery_status, 0644);
+MODULE_PARM_DESC(battery_status,
+ "battery status <charging|discharging|not-charging|full>");
+
+module_param(battery_present, battery_present, 0644);
+MODULE_PARM_DESC(battery_present,
+ "battery presence state <good|overheat|dead|overvoltage|failure>");
+
+module_param(battery_technology, battery_technology, 0644);
+MODULE_PARM_DESC(battery_technology,
+ "battery technology <NiMH|LION|LIPO|LiFe|NiCd|LiMn>");
+
+module_param(battery_health, battery_health, 0644);
+MODULE_PARM_DESC(battery_health,
+ "battery health state <good|overheat|dead|overvoltage|failure>");
+
+module_param(battery_capacity, battery_capacity, 0644);
+MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)");
+
+
MODULE_DESCRIPTION("Power supply driver for testing");
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c
index e5ced3a..d119c38 100644
--- a/drivers/power/z2_battery.c
+++ b/drivers/power/z2_battery.c
@@ -271,24 +271,33 @@
}
#ifdef CONFIG_PM
-static int z2_batt_suspend(struct i2c_client *client, pm_message_t state)
+static int z2_batt_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct z2_charger *charger = i2c_get_clientdata(client);
flush_work_sync(&charger->bat_work);
return 0;
}
-static int z2_batt_resume(struct i2c_client *client)
+static int z2_batt_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct z2_charger *charger = i2c_get_clientdata(client);
schedule_work(&charger->bat_work);
return 0;
}
+
+static const struct dev_pm_ops z2_battery_pm_ops = {
+ .suspend = z2_batt_suspend,
+ .resume = z2_batt_resume,
+};
+
+#define Z2_BATTERY_PM_OPS (&z2_battery_pm_ops)
+
#else
-#define z2_batt_suspend NULL
-#define z2_batt_resume NULL
+#define Z2_BATTERY_PM_OPS (NULL)
#endif
static const struct i2c_device_id z2_batt_id[] = {
@@ -301,11 +310,10 @@
.driver = {
.name = "z2-battery",
.owner = THIS_MODULE,
+ .pm = Z2_BATTERY_PM_OPS
},
.probe = z2_batt_probe,
.remove = z2_batt_remove,
- .suspend = z2_batt_suspend,
- .resume = z2_batt_resume,
.id_table = z2_batt_id,
};
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index 8592512..d63fddb 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -15,7 +15,6 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
-#include <linux/mfd/core.h>
#include <linux/mfd/88pm860x.h>
struct pm8607_regulator_info {
@@ -399,36 +398,33 @@
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm8607_regulator_info *info = NULL;
- struct regulator_init_data *pdata;
- struct mfd_cell *cell;
+ struct regulator_init_data *pdata = pdev->dev.platform_data;
+ struct resource *res;
int i;
- cell = pdev->dev.platform_data;
- if (cell == NULL)
- return -ENODEV;
- pdata = cell->mfd_data;
- if (pdata == NULL)
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No I/O resource!\n");
return -EINVAL;
-
+ }
for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) {
info = &pm8607_regulator_info[i];
- if (!strcmp(info->desc.name, pdata->constraints.name))
+ if (info->desc.id == res->start)
break;
}
- if (i > ARRAY_SIZE(pm8607_regulator_info)) {
- dev_err(&pdev->dev, "Failed to find regulator %s\n",
- pdata->constraints.name);
+ if ((i < 0) || (i > PM8607_ID_RG_MAX)) {
+ dev_err(&pdev->dev, "Failed to find regulator %llu\n",
+ (unsigned long long)res->start);
return -EINVAL;
}
-
info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
info->chip = chip;
/* check DVC ramp slope double */
- if (!strcmp(info->desc.name, "BUCK3"))
- if (info->chip->buck3_double)
- info->slope_double = 1;
+ if ((i == PM8607_ID_BUCK3) && info->chip->buck3_double)
+ info->slope_double = 1;
+ /* replace driver_data with info */
info->regulator = regulator_register(&info->desc, &pdev->dev,
pdata, info);
if (IS_ERR(info->regulator)) {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f0b13a0..d7ed20f 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -297,5 +297,11 @@
serial interface currently supported on the sequencer serial
port controller.
+config REGULATOR_TPS65910
+ tristate "TI TPS65910 Power Regulator"
+ depends on MFD_TPS65910
+ help
+ This driver supports TPS65910 voltage regulator chips.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 165ff53..3932d2e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -42,5 +42,6 @@
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
+obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index b1d7794..585e494 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -17,7 +17,6 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/mfd/abx500.h>
-#include <linux/mfd/core.h>
/* LDO registers and some handy masking definitions for AB3100 */
#define AB3100_LDO_A 0x40
@@ -582,7 +581,7 @@
static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
{
- struct ab3100_platform_data *plfdata = mfd_get_data(pdev);
+ struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
int err = 0;
u8 data;
int i;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 0fae51c..d3e3879 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -158,6 +158,13 @@
struct regulator *regulator;
list_for_each_entry(regulator, &rdev->consumer_list, list) {
+ /*
+ * Assume consumers that didn't say anything are OK
+ * with anything in the constraint range.
+ */
+ if (!regulator->min_uV && !regulator->max_uV)
+ continue;
+
if (*max_uV > regulator->max_uV)
*max_uV = regulator->max_uV;
if (*min_uV < regulator->min_uV)
@@ -197,9 +204,9 @@
}
/* operating mode constraint check */
-static int regulator_check_mode(struct regulator_dev *rdev, int mode)
+static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode)
{
- switch (mode) {
+ switch (*mode) {
case REGULATOR_MODE_FAST:
case REGULATOR_MODE_NORMAL:
case REGULATOR_MODE_IDLE:
@@ -217,11 +224,17 @@
rdev_err(rdev, "operation not allowed\n");
return -EPERM;
}
- if (!(rdev->constraints->valid_modes_mask & mode)) {
- rdev_err(rdev, "invalid mode %x\n", mode);
- return -EINVAL;
+
+ /* The modes are bitmasks, the most power hungry modes having
+ * the lowest values. If the requested mode isn't supported
+ * try higher modes. */
+ while (*mode) {
+ if (rdev->constraints->valid_modes_mask & *mode)
+ return 0;
+ *mode /= 2;
}
- return 0;
+
+ return -EINVAL;
}
/* dynamic regulator mode switching constraint check */
@@ -612,7 +625,7 @@
output_uV, current_uA);
/* check the new mode is allowed */
- err = regulator_check_mode(rdev, mode);
+ err = regulator_mode_constrain(rdev, &mode);
if (err == 0)
rdev->desc->ops->set_mode(rdev, mode);
}
@@ -718,6 +731,10 @@
count += sprintf(buf + count, "at %d mV ", ret / 1000);
}
+ if (constraints->uV_offset)
+ count += sprintf(buf, "%dmV offset ",
+ constraints->uV_offset / 1000);
+
if (constraints->min_uA && constraints->max_uA) {
if (constraints->min_uA == constraints->max_uA)
count += sprintf(buf + count, "%d mA ",
@@ -1498,13 +1515,14 @@
*/
int regulator_force_disable(struct regulator *regulator)
{
+ struct regulator_dev *rdev = regulator->rdev;
struct regulator_dev *supply_rdev = NULL;
int ret;
- mutex_lock(®ulator->rdev->mutex);
+ mutex_lock(&rdev->mutex);
regulator->uA_load = 0;
- ret = _regulator_force_disable(regulator->rdev, &supply_rdev);
- mutex_unlock(®ulator->rdev->mutex);
+ ret = _regulator_force_disable(rdev, &supply_rdev);
+ mutex_unlock(&rdev->mutex);
if (supply_rdev)
regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev)));
@@ -1634,6 +1652,9 @@
trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);
+ min_uV += rdev->constraints->uV_offset;
+ max_uV += rdev->constraints->uV_offset;
+
if (rdev->desc->ops->set_voltage) {
ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV,
&selector);
@@ -1858,18 +1879,22 @@
static int _regulator_get_voltage(struct regulator_dev *rdev)
{
- int sel;
+ int sel, ret;
if (rdev->desc->ops->get_voltage_sel) {
sel = rdev->desc->ops->get_voltage_sel(rdev);
if (sel < 0)
return sel;
- return rdev->desc->ops->list_voltage(rdev, sel);
- }
- if (rdev->desc->ops->get_voltage)
- return rdev->desc->ops->get_voltage(rdev);
- else
+ ret = rdev->desc->ops->list_voltage(rdev, sel);
+ } else if (rdev->desc->ops->get_voltage) {
+ ret = rdev->desc->ops->get_voltage(rdev);
+ } else {
return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+ return ret - rdev->constraints->uV_offset;
}
/**
@@ -2005,7 +2030,7 @@
}
/* constraints check */
- ret = regulator_check_mode(rdev, mode);
+ ret = regulator_mode_constrain(rdev, &mode);
if (ret < 0)
goto out;
@@ -2081,16 +2106,26 @@
mutex_lock(&rdev->mutex);
+ /*
+ * first check to see if we can set modes at all, otherwise just
+ * tell the consumer everything is OK.
+ */
regulator->uA_load = uA_load;
ret = regulator_check_drms(rdev);
- if (ret < 0)
+ if (ret < 0) {
+ ret = 0;
goto out;
- ret = -EINVAL;
+ }
- /* sanity check */
if (!rdev->desc->ops->get_optimum_mode)
goto out;
+ /*
+ * we can actually do this so any errors are indicators of
+ * potential real failure.
+ */
+ ret = -EINVAL;
+
/* get output voltage */
output_uV = _regulator_get_voltage(rdev);
if (output_uV <= 0) {
@@ -2116,7 +2151,7 @@
mode = rdev->desc->ops->get_optimum_mode(rdev,
input_uV, output_uV,
total_uA_load);
- ret = regulator_check_mode(rdev, mode);
+ ret = regulator_mode_constrain(rdev, &mode);
if (ret < 0) {
rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n",
total_uA_load, input_uV, output_uV);
@@ -2589,14 +2624,6 @@
if (ret < 0)
goto scrub;
- /* set supply regulator if it exists */
- if (init_data->supply_regulator && init_data->supply_regulator_dev) {
- dev_err(dev,
- "Supply regulator specified by both name and dev\n");
- ret = -EINVAL;
- goto scrub;
- }
-
if (init_data->supply_regulator) {
struct regulator_dev *r;
int found = 0;
@@ -2621,14 +2648,6 @@
goto scrub;
}
- if (init_data->supply_regulator_dev) {
- dev_warn(dev, "Uses supply_regulator_dev instead of regulator_supply\n");
- ret = set_supply(rdev,
- dev_get_drvdata(init_data->supply_regulator_dev));
- if (ret < 0)
- goto scrub;
- }
-
/* add consumers devices */
for (i = 0; i < init_data->num_consumer_supplies; i++) {
ret = set_consumer_device_supply(rdev,
diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c
index 1089a96..e5f7b8fe 100644
--- a/drivers/regulator/db8500-prcmu.c
+++ b/drivers/regulator/db8500-prcmu.c
@@ -13,7 +13,6 @@
#include <linux/err.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/mfd/db8500-prcmu.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
@@ -471,7 +470,8 @@
static int __devinit db8500_regulator_probe(struct platform_device *pdev)
{
- struct regulator_init_data *db8500_init_data = mfd_get_data(pdev);
+ struct regulator_init_data *db8500_init_data =
+ dev_get_platdata(&pdev->dev);
int i, err;
/* register all regulators */
diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c
index 8ae1475..e4dbd66 100644
--- a/drivers/regulator/max8925-regulator.c
+++ b/drivers/regulator/max8925-regulator.c
@@ -23,6 +23,10 @@
#define SD1_DVM_SHIFT 5 /* SDCTL1 bit5 */
#define SD1_DVM_EN 6 /* SDV1 bit 6 */
+/* bit definitions in SD & LDO control registers */
+#define OUT_ENABLE 0x1f /* Power U/D sequence as I2C */
+#define OUT_DISABLE 0x1e /* Power U/D sequence as I2C */
+
struct max8925_regulator_info {
struct regulator_desc desc;
struct regulator_dev *regulator;
@@ -93,8 +97,8 @@
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return max8925_set_bits(info->i2c, info->enable_reg,
- 1 << info->enable_bit,
- 1 << info->enable_bit);
+ OUT_ENABLE << info->enable_bit,
+ OUT_ENABLE << info->enable_bit);
}
static int max8925_disable(struct regulator_dev *rdev)
@@ -102,7 +106,8 @@
struct max8925_regulator_info *info = rdev_get_drvdata(rdev);
return max8925_set_bits(info->i2c, info->enable_reg,
- 1 << info->enable_bit, 0);
+ OUT_ENABLE << info->enable_bit,
+ OUT_DISABLE << info->enable_bit);
}
static int max8925_is_enabled(struct regulator_dev *rdev)
diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c
index 77e0cfb..10d5a1d 100644
--- a/drivers/regulator/max8997.c
+++ b/drivers/regulator/max8997.c
@@ -267,7 +267,6 @@
default:
/* Not controllable or not exists */
return -EINVAL;
- break;
}
return 0;
@@ -1033,11 +1032,11 @@
/* For the safety, set max voltage before setting up */
for (i = 0; i < 8; i++) {
- max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS(i + 1),
+ max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
max_buck1, 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS(i + 1),
+ max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
max_buck2, 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS(i + 1),
+ max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
max_buck5, 0x3f);
}
@@ -1114,13 +1113,13 @@
/* Initialize all the DVS related BUCK registers */
for (i = 0; i < 8; i++) {
- max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS(i + 1),
+ max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
max8997->buck1_vol[i],
0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS(i + 1),
+ max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
max8997->buck2_vol[i],
0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS(i + 1),
+ max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
max8997->buck5_vol[i],
0x3f);
}
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
index f57e9c4..41a1495 100644
--- a/drivers/regulator/max8998.c
+++ b/drivers/regulator/max8998.c
@@ -732,13 +732,15 @@
if (!pdata->buck1_set1) {
printk(KERN_ERR "MAX8998 SET1 GPIO defined as 0 !\n");
WARN_ON(!pdata->buck1_set1);
- return -EIO;
+ ret = -EIO;
+ goto err_free_mem;
}
/* Check if SET2 is not equal to 0 */
if (!pdata->buck1_set2) {
printk(KERN_ERR "MAX8998 SET2 GPIO defined as 0 !\n");
WARN_ON(!pdata->buck1_set2);
- return -EIO;
+ ret = -EIO;
+ goto err_free_mem;
}
gpio_request(pdata->buck1_set1, "MAX8998 BUCK1_SET1");
@@ -758,7 +760,7 @@
max8998->buck1_vol[0] = i;
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i);
if (ret)
- return ret;
+ goto err_free_mem;
/* Set predefined value for BUCK1 register 2 */
i = 0;
@@ -770,7 +772,7 @@
max8998->buck1_vol[1] = i;
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE2, i);
if (ret)
- return ret;
+ goto err_free_mem;
/* Set predefined value for BUCK1 register 3 */
i = 0;
@@ -782,7 +784,7 @@
max8998->buck1_vol[2] = i;
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE3, i);
if (ret)
- return ret;
+ goto err_free_mem;
/* Set predefined value for BUCK1 register 4 */
i = 0;
@@ -794,7 +796,7 @@
max8998->buck1_vol[3] = i;
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE4, i);
if (ret)
- return ret;
+ goto err_free_mem;
}
@@ -803,7 +805,8 @@
if (!pdata->buck2_set3) {
printk(KERN_ERR "MAX8998 SET3 GPIO defined as 0 !\n");
WARN_ON(!pdata->buck2_set3);
- return -EIO;
+ ret = -EIO;
+ goto err_free_mem;
}
gpio_request(pdata->buck2_set3, "MAX8998 BUCK2_SET3");
gpio_direction_output(pdata->buck2_set3,
@@ -818,7 +821,7 @@
max8998->buck2_vol[0] = i;
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i);
if (ret)
- return ret;
+ goto err_free_mem;
/* BUCK2 register 2 */
i = 0;
@@ -830,7 +833,7 @@
max8998->buck2_vol[1] = i;
ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i);
if (ret)
- return ret;
+ goto err_free_mem;
}
for (i = 0; i < pdata->num_regulators; i++) {
@@ -860,6 +863,7 @@
if (rdev[i])
regulator_unregister(rdev[i]);
+err_free_mem:
kfree(max8998->rdev);
kfree(max8998);
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index b8a00c7..730f43a 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -15,7 +15,6 @@
#include <linux/regulator/driver.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
@@ -337,7 +336,8 @@
{
struct mc13xxx_regulator_priv *priv;
struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
- struct mc13783_regulator_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13783_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
struct mc13783_regulator_init_data *init_data;
int i, ret;
@@ -381,7 +381,8 @@
static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
- struct mc13783_regulator_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13783_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
int i;
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index 6f15168..3285d41 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -15,7 +15,6 @@
#include <linux/regulator/driver.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
-#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
@@ -432,7 +431,8 @@
int min_uV, int max_uV, unsigned *selector)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
- int hi, value, val, mask, id = rdev_get_id(rdev);
+ int hi, value, mask, id = rdev_get_id(rdev);
+ u32 valread;
int ret;
dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
@@ -448,15 +448,16 @@
mc13xxx_lock(priv->mc13xxx);
ret = mc13xxx_reg_read(priv->mc13xxx,
- mc13892_regulators[id].vsel_reg, &val);
+ mc13892_regulators[id].vsel_reg, &valread);
if (ret)
goto err;
- hi = val & MC13892_SWITCHERS0_SWxHI;
- if (value > 1375)
+ if (value > 1375000)
hi = 1;
- if (value < 1100)
+ else if (value < 1100000)
hi = 0;
+ else
+ hi = valread & MC13892_SWITCHERS0_SWxHI;
if (hi) {
value = (value - 1100000) / 25000;
@@ -465,8 +466,10 @@
value = (value - 600000) / 25000;
mask = mc13892_regulators[id].vsel_mask | MC13892_SWITCHERS0_SWxHI;
- ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].vsel_reg,
- mask, value << mc13892_regulators[id].vsel_shift);
+ valread = (valread & ~mask) |
+ (value << mc13892_regulators[id].vsel_shift);
+ ret = mc13xxx_reg_write(priv->mc13xxx, mc13892_regulators[id].vsel_reg,
+ valread);
err:
mc13xxx_unlock(priv->mc13xxx);
@@ -521,7 +524,8 @@
{
struct mc13xxx_regulator_priv *priv;
struct mc13xxx *mc13892 = dev_get_drvdata(pdev->dev.parent);
- struct mc13xxx_regulator_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13xxx_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
struct mc13xxx_regulator_init_data *init_data;
int i, ret;
u32 val;
@@ -595,7 +599,8 @@
static int __devexit mc13892_regulator_remove(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
- struct mc13xxx_regulator_platform_data *pdata = mfd_get_data(pdev);
+ struct mc13xxx_regulator_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
int i;
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c
index 2bb5de1..bc27ab1 100644
--- a/drivers/regulator/mc13xxx-regulator-core.c
+++ b/drivers/regulator/mc13xxx-regulator-core.c
@@ -174,7 +174,7 @@
dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
- BUG_ON(val > mc13xxx_regulators[id].desc.n_voltages);
+ BUG_ON(val >= mc13xxx_regulators[id].desc.n_voltages);
return mc13xxx_regulators[id].voltages[val];
}
diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c
index 1661499..1011873 100644
--- a/drivers/regulator/tps6105x-regulator.c
+++ b/drivers/regulator/tps6105x-regulator.c
@@ -137,7 +137,7 @@
*/
static int __devinit tps6105x_regulator_probe(struct platform_device *pdev)
{
- struct tps6105x *tps6105x = mfd_get_data(pdev);
+ struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev);
struct tps6105x_platform_data *pdata = tps6105x->pdata;
int ret;
@@ -158,13 +158,14 @@
"failed to register regulator\n");
return ret;
}
+ platform_set_drvdata(pdev, tps6105x);
return 0;
}
static int __devexit tps6105x_regulator_remove(struct platform_device *pdev)
{
- struct tps6105x *tps6105x = platform_get_drvdata(pdev);
+ struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev);
regulator_unregister(tps6105x->regulator);
return 0;
}
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index 60a7ca5..fbddc15 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -466,7 +466,6 @@
static int __devinit tps_65023_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- static int desc_id;
const struct tps_info *info = (void *)id->driver_data;
struct regulator_init_data *init_data;
struct regulator_dev *rdev;
@@ -499,7 +498,7 @@
tps->info[i] = info;
tps->desc[i].name = info->name;
- tps->desc[i].id = desc_id++;
+ tps->desc[i].id = i;
tps->desc[i].n_voltages = num_voltages[i];
tps->desc[i].ops = (i > TPS65023_DCDC_3 ?
&tps65023_ldo_ops : &tps65023_dcdc_ops);
diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c
index 0647552..bfffabc 100644
--- a/drivers/regulator/tps6507x-regulator.c
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -553,7 +553,6 @@
int tps6507x_pmic_probe(struct platform_device *pdev)
{
struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
- static int desc_id;
struct tps_info *info = &tps6507x_pmic_regs[0];
struct regulator_init_data *init_data;
struct regulator_dev *rdev;
@@ -598,7 +597,7 @@
}
tps->desc[i].name = info->name;
- tps->desc[i].id = desc_id++;
+ tps->desc[i].id = i;
tps->desc[i].n_voltages = num_voltages[i];
tps->desc[i].ops = (i > TPS6507X_DCDC_3 ?
&tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops);
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
new file mode 100644
index 0000000..55dd4e6
--- /dev/null
+++ b/drivers/regulator/tps65910-regulator.c
@@ -0,0 +1,993 @@
+/*
+ * tps65910.c -- TI tps65910
+ *
+ * Copyright 2010 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/tps65910.h>
+
+#define TPS65910_REG_VRTC 0
+#define TPS65910_REG_VIO 1
+#define TPS65910_REG_VDD1 2
+#define TPS65910_REG_VDD2 3
+#define TPS65910_REG_VDD3 4
+#define TPS65910_REG_VDIG1 5
+#define TPS65910_REG_VDIG2 6
+#define TPS65910_REG_VPLL 7
+#define TPS65910_REG_VDAC 8
+#define TPS65910_REG_VAUX1 9
+#define TPS65910_REG_VAUX2 10
+#define TPS65910_REG_VAUX33 11
+#define TPS65910_REG_VMMC 12
+
+#define TPS65911_REG_VDDCTRL 4
+#define TPS65911_REG_LDO1 5
+#define TPS65911_REG_LDO2 6
+#define TPS65911_REG_LDO3 7
+#define TPS65911_REG_LDO4 8
+#define TPS65911_REG_LDO5 9
+#define TPS65911_REG_LDO6 10
+#define TPS65911_REG_LDO7 11
+#define TPS65911_REG_LDO8 12
+
+#define TPS65910_NUM_REGULATOR 13
+#define TPS65910_SUPPLY_STATE_ENABLED 0x1
+
+/* supported VIO voltages in milivolts */
+static const u16 VIO_VSEL_table[] = {
+ 1500, 1800, 2500, 3300,
+};
+
+/* VSEL tables for TPS65910 specific LDOs and dcdc's */
+
+/* supported VDD3 voltages in milivolts */
+static const u16 VDD3_VSEL_table[] = {
+ 5000,
+};
+
+/* supported VDIG1 voltages in milivolts */
+static const u16 VDIG1_VSEL_table[] = {
+ 1200, 1500, 1800, 2700,
+};
+
+/* supported VDIG2 voltages in milivolts */
+static const u16 VDIG2_VSEL_table[] = {
+ 1000, 1100, 1200, 1800,
+};
+
+/* supported VPLL voltages in milivolts */
+static const u16 VPLL_VSEL_table[] = {
+ 1000, 1100, 1800, 2500,
+};
+
+/* supported VDAC voltages in milivolts */
+static const u16 VDAC_VSEL_table[] = {
+ 1800, 2600, 2800, 2850,
+};
+
+/* supported VAUX1 voltages in milivolts */
+static const u16 VAUX1_VSEL_table[] = {
+ 1800, 2500, 2800, 2850,
+};
+
+/* supported VAUX2 voltages in milivolts */
+static const u16 VAUX2_VSEL_table[] = {
+ 1800, 2800, 2900, 3300,
+};
+
+/* supported VAUX33 voltages in milivolts */
+static const u16 VAUX33_VSEL_table[] = {
+ 1800, 2000, 2800, 3300,
+};
+
+/* supported VMMC voltages in milivolts */
+static const u16 VMMC_VSEL_table[] = {
+ 1800, 2800, 3000, 3300,
+};
+
+struct tps_info {
+ const char *name;
+ unsigned min_uV;
+ unsigned max_uV;
+ u8 table_len;
+ const u16 *table;
+};
+
+static struct tps_info tps65910_regs[] = {
+ {
+ .name = "VRTC",
+ },
+ {
+ .name = "VIO",
+ .min_uV = 1500000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(VIO_VSEL_table),
+ .table = VIO_VSEL_table,
+ },
+ {
+ .name = "VDD1",
+ .min_uV = 600000,
+ .max_uV = 4500000,
+ },
+ {
+ .name = "VDD2",
+ .min_uV = 600000,
+ .max_uV = 4500000,
+ },
+ {
+ .name = "VDD3",
+ .min_uV = 5000000,
+ .max_uV = 5000000,
+ .table_len = ARRAY_SIZE(VDD3_VSEL_table),
+ .table = VDD3_VSEL_table,
+ },
+ {
+ .name = "VDIG1",
+ .min_uV = 1200000,
+ .max_uV = 2700000,
+ .table_len = ARRAY_SIZE(VDIG1_VSEL_table),
+ .table = VDIG1_VSEL_table,
+ },
+ {
+ .name = "VDIG2",
+ .min_uV = 1000000,
+ .max_uV = 1800000,
+ .table_len = ARRAY_SIZE(VDIG2_VSEL_table),
+ .table = VDIG2_VSEL_table,
+ },
+ {
+ .name = "VPLL",
+ .min_uV = 1000000,
+ .max_uV = 2500000,
+ .table_len = ARRAY_SIZE(VPLL_VSEL_table),
+ .table = VPLL_VSEL_table,
+ },
+ {
+ .name = "VDAC",
+ .min_uV = 1800000,
+ .max_uV = 2850000,
+ .table_len = ARRAY_SIZE(VDAC_VSEL_table),
+ .table = VDAC_VSEL_table,
+ },
+ {
+ .name = "VAUX1",
+ .min_uV = 1800000,
+ .max_uV = 2850000,
+ .table_len = ARRAY_SIZE(VAUX1_VSEL_table),
+ .table = VAUX1_VSEL_table,
+ },
+ {
+ .name = "VAUX2",
+ .min_uV = 1800000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(VAUX2_VSEL_table),
+ .table = VAUX2_VSEL_table,
+ },
+ {
+ .name = "VAUX33",
+ .min_uV = 1800000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(VAUX33_VSEL_table),
+ .table = VAUX33_VSEL_table,
+ },
+ {
+ .name = "VMMC",
+ .min_uV = 1800000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(VMMC_VSEL_table),
+ .table = VMMC_VSEL_table,
+ },
+};
+
+static struct tps_info tps65911_regs[] = {
+ {
+ .name = "VIO",
+ .min_uV = 1500000,
+ .max_uV = 3300000,
+ .table_len = ARRAY_SIZE(VIO_VSEL_table),
+ .table = VIO_VSEL_table,
+ },
+ {
+ .name = "VDD1",
+ .min_uV = 600000,
+ .max_uV = 4500000,
+ },
+ {
+ .name = "VDD2",
+ .min_uV = 600000,
+ .max_uV = 4500000,
+ },
+ {
+ .name = "VDDCTRL",
+ .min_uV = 600000,
+ .max_uV = 1400000,
+ },
+ {
+ .name = "LDO1",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO2",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO3",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO4",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO5",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO6",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO7",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+ {
+ .name = "LDO8",
+ .min_uV = 1000000,
+ .max_uV = 3300000,
+ },
+};
+
+struct tps65910_reg {
+ struct regulator_desc desc[TPS65910_NUM_REGULATOR];
+ struct tps65910 *mfd;
+ struct regulator_dev *rdev[TPS65910_NUM_REGULATOR];
+ struct tps_info *info[TPS65910_NUM_REGULATOR];
+ struct mutex mutex;
+ int mode;
+ int (*get_ctrl_reg)(int);
+};
+
+static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg)
+{
+ u8 val;
+ int err;
+
+ err = pmic->mfd->read(pmic->mfd, reg, 1, &val);
+ if (err)
+ return err;
+
+ return val;
+}
+
+static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val)
+{
+ return pmic->mfd->write(pmic->mfd, reg, 1, &val);
+}
+
+static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg,
+ u8 set_mask, u8 clear_mask)
+{
+ int err, data;
+
+ mutex_lock(&pmic->mutex);
+
+ data = tps65910_read(pmic, reg);
+ if (data < 0) {
+ dev_err(pmic->mfd->dev, "Read from reg 0x%x failed\n", reg);
+ err = data;
+ goto out;
+ }
+
+ data &= ~clear_mask;
+ data |= set_mask;
+ err = tps65910_write(pmic, reg, data);
+ if (err)
+ dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg);
+
+out:
+ mutex_unlock(&pmic->mutex);
+ return err;
+}
+
+static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg)
+{
+ int data;
+
+ mutex_lock(&pmic->mutex);
+
+ data = tps65910_read(pmic, reg);
+ if (data < 0)
+ dev_err(pmic->mfd->dev, "Read from reg 0x%x failed\n", reg);
+
+ mutex_unlock(&pmic->mutex);
+ return data;
+}
+
+static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val)
+{
+ int err;
+
+ mutex_lock(&pmic->mutex);
+
+ err = tps65910_write(pmic, reg, val);
+ if (err < 0)
+ dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg);
+
+ mutex_unlock(&pmic->mutex);
+ return err;
+}
+
+static int tps65910_get_ctrl_register(int id)
+{
+ switch (id) {
+ case TPS65910_REG_VRTC:
+ return TPS65910_VRTC;
+ case TPS65910_REG_VIO:
+ return TPS65910_VIO;
+ case TPS65910_REG_VDD1:
+ return TPS65910_VDD1;
+ case TPS65910_REG_VDD2:
+ return TPS65910_VDD2;
+ case TPS65910_REG_VDD3:
+ return TPS65910_VDD3;
+ case TPS65910_REG_VDIG1:
+ return TPS65910_VDIG1;
+ case TPS65910_REG_VDIG2:
+ return TPS65910_VDIG2;
+ case TPS65910_REG_VPLL:
+ return TPS65910_VPLL;
+ case TPS65910_REG_VDAC:
+ return TPS65910_VDAC;
+ case TPS65910_REG_VAUX1:
+ return TPS65910_VAUX1;
+ case TPS65910_REG_VAUX2:
+ return TPS65910_VAUX2;
+ case TPS65910_REG_VAUX33:
+ return TPS65910_VAUX33;
+ case TPS65910_REG_VMMC:
+ return TPS65910_VMMC;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tps65911_get_ctrl_register(int id)
+{
+ switch (id) {
+ case TPS65910_REG_VRTC:
+ return TPS65910_VRTC;
+ case TPS65910_REG_VIO:
+ return TPS65910_VIO;
+ case TPS65910_REG_VDD1:
+ return TPS65910_VDD1;
+ case TPS65910_REG_VDD2:
+ return TPS65910_VDD2;
+ case TPS65911_REG_VDDCTRL:
+ return TPS65911_VDDCTRL;
+ case TPS65911_REG_LDO1:
+ return TPS65911_LDO1;
+ case TPS65911_REG_LDO2:
+ return TPS65911_LDO2;
+ case TPS65911_REG_LDO3:
+ return TPS65911_LDO3;
+ case TPS65911_REG_LDO4:
+ return TPS65911_LDO4;
+ case TPS65911_REG_LDO5:
+ return TPS65911_LDO5;
+ case TPS65911_REG_LDO6:
+ return TPS65911_LDO6;
+ case TPS65911_REG_LDO7:
+ return TPS65911_LDO7;
+ case TPS65911_REG_LDO8:
+ return TPS65911_LDO8;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tps65910_is_enabled(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int reg, value, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ value = tps65910_reg_read(pmic, reg);
+ if (value < 0)
+ return value;
+
+ return value & TPS65910_SUPPLY_STATE_ENABLED;
+}
+
+static int tps65910_enable(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65910 *mfd = pmic->mfd;
+ int reg, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ return tps65910_set_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED);
+}
+
+static int tps65910_disable(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65910 *mfd = pmic->mfd;
+ int reg, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ return tps65910_clear_bits(mfd, reg, TPS65910_SUPPLY_STATE_ENABLED);
+}
+
+
+static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ struct tps65910 *mfd = pmic->mfd;
+ int reg, value, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ return tps65910_modify_bits(pmic, reg, LDO_ST_ON_BIT,
+ LDO_ST_MODE_BIT);
+ case REGULATOR_MODE_IDLE:
+ value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT;
+ return tps65910_set_bits(mfd, reg, value);
+ case REGULATOR_MODE_STANDBY:
+ return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT);
+ }
+
+ return -EINVAL;
+}
+
+static unsigned int tps65910_get_mode(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int reg, value, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ value = tps65910_reg_read(pmic, reg);
+ if (value < 0)
+ return value;
+
+ if (value & LDO_ST_ON_BIT)
+ return REGULATOR_MODE_STANDBY;
+ else if (value & LDO_ST_MODE_BIT)
+ return REGULATOR_MODE_IDLE;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int tps65910_get_voltage_dcdc(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int id = rdev_get_id(dev), voltage = 0;
+ int opvsel = 0, srvsel = 0, vselmax = 0, mult = 0, sr = 0;
+
+ switch (id) {
+ case TPS65910_REG_VDD1:
+ opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP);
+ mult = tps65910_reg_read(pmic, TPS65910_VDD1);
+ mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT;
+ srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR);
+ sr = opvsel & VDD1_OP_CMD_MASK;
+ opvsel &= VDD1_OP_SEL_MASK;
+ srvsel &= VDD1_SR_SEL_MASK;
+ vselmax = 75;
+ break;
+ case TPS65910_REG_VDD2:
+ opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP);
+ mult = tps65910_reg_read(pmic, TPS65910_VDD2);
+ mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT;
+ srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR);
+ sr = opvsel & VDD2_OP_CMD_MASK;
+ opvsel &= VDD2_OP_SEL_MASK;
+ srvsel &= VDD2_SR_SEL_MASK;
+ vselmax = 75;
+ break;
+ case TPS65911_REG_VDDCTRL:
+ opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP);
+ srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR);
+ sr = opvsel & VDDCTRL_OP_CMD_MASK;
+ opvsel &= VDDCTRL_OP_SEL_MASK;
+ srvsel &= VDDCTRL_SR_SEL_MASK;
+ vselmax = 64;
+ break;
+ }
+
+ /* multiplier 0 == 1 but 2,3 normal */
+ if (!mult)
+ mult=1;
+
+ if (sr) {
+ /* normalise to valid range */
+ if (srvsel < 3)
+ srvsel = 3;
+ if (srvsel > vselmax)
+ srvsel = vselmax;
+ srvsel -= 3;
+
+ voltage = (srvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100;
+ } else {
+
+ /* normalise to valid range*/
+ if (opvsel < 3)
+ opvsel = 3;
+ if (opvsel > vselmax)
+ opvsel = vselmax;
+ opvsel -= 3;
+
+ voltage = (opvsel * VDD1_2_OFFSET + VDD1_2_MIN_VOLT) * 100;
+ }
+
+ voltage *= mult;
+
+ return voltage;
+}
+
+static int tps65910_get_voltage(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int reg, value, id = rdev_get_id(dev), voltage = 0;
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ value = tps65910_reg_read(pmic, reg);
+ if (value < 0)
+ return value;
+
+ switch (id) {
+ case TPS65910_REG_VIO:
+ case TPS65910_REG_VDIG1:
+ case TPS65910_REG_VDIG2:
+ case TPS65910_REG_VPLL:
+ case TPS65910_REG_VDAC:
+ case TPS65910_REG_VAUX1:
+ case TPS65910_REG_VAUX2:
+ case TPS65910_REG_VAUX33:
+ case TPS65910_REG_VMMC:
+ value &= LDO_SEL_MASK;
+ value >>= LDO_SEL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ voltage = pmic->info[id]->table[value] * 1000;
+
+ return voltage;
+}
+
+static int tps65910_get_voltage_vdd3(struct regulator_dev *dev)
+{
+ return 5 * 1000 * 1000;
+}
+
+static int tps65911_get_voltage(struct regulator_dev *dev)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int step_mv, id = rdev_get_id(dev);
+ u8 value, reg;
+
+ reg = pmic->get_ctrl_reg(id);
+
+ value = tps65910_reg_read(pmic, reg);
+
+ switch (id) {
+ case TPS65911_REG_LDO1:
+ case TPS65911_REG_LDO2:
+ case TPS65911_REG_LDO4:
+ value &= LDO1_SEL_MASK;
+ value >>= LDO_SEL_SHIFT;
+ /* The first 5 values of the selector correspond to 1V */
+ if (value < 5)
+ value = 0;
+ else
+ value -= 4;
+
+ step_mv = 50;
+ break;
+ case TPS65911_REG_LDO3:
+ case TPS65911_REG_LDO5:
+ case TPS65911_REG_LDO6:
+ case TPS65911_REG_LDO7:
+ case TPS65911_REG_LDO8:
+ value &= LDO3_SEL_MASK;
+ value >>= LDO_SEL_SHIFT;
+ /* The first 3 values of the selector correspond to 1V */
+ if (value < 3)
+ value = 0;
+ else
+ value -= 2;
+
+ step_mv = 100;
+ break;
+ case TPS65910_REG_VIO:
+ return pmic->info[id]->table[value] * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (LDO_MIN_VOLT + value * step_mv) * 1000;
+}
+
+static int tps65910_set_voltage_dcdc(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int id = rdev_get_id(dev), vsel;
+ int dcdc_mult = 0;
+
+ switch (id) {
+ case TPS65910_REG_VDD1:
+ dcdc_mult = (selector / VDD1_2_NUM_VOLTS) + 1;
+ if (dcdc_mult == 1)
+ dcdc_mult--;
+ vsel = (selector % VDD1_2_NUM_VOLTS) + 3;
+
+ tps65910_modify_bits(pmic, TPS65910_VDD1,
+ (dcdc_mult << VDD1_VGAIN_SEL_SHIFT),
+ VDD1_VGAIN_SEL_MASK);
+ tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel);
+ break;
+ case TPS65910_REG_VDD2:
+ dcdc_mult = (selector / VDD1_2_NUM_VOLTS) + 1;
+ if (dcdc_mult == 1)
+ dcdc_mult--;
+ vsel = (selector % VDD1_2_NUM_VOLTS) + 3;
+
+ tps65910_modify_bits(pmic, TPS65910_VDD2,
+ (dcdc_mult << VDD2_VGAIN_SEL_SHIFT),
+ VDD1_VGAIN_SEL_MASK);
+ tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel);
+ break;
+ case TPS65911_REG_VDDCTRL:
+ vsel = selector;
+ tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel);
+ }
+
+ return 0;
+}
+
+static int tps65910_set_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int reg, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ switch (id) {
+ case TPS65910_REG_VIO:
+ case TPS65910_REG_VDIG1:
+ case TPS65910_REG_VDIG2:
+ case TPS65910_REG_VPLL:
+ case TPS65910_REG_VDAC:
+ case TPS65910_REG_VAUX1:
+ case TPS65910_REG_VAUX2:
+ case TPS65910_REG_VAUX33:
+ case TPS65910_REG_VMMC:
+ return tps65910_modify_bits(pmic, reg,
+ (selector << LDO_SEL_SHIFT), LDO_SEL_MASK);
+ }
+
+ return -EINVAL;
+}
+
+static int tps65911_set_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int reg, id = rdev_get_id(dev);
+
+ reg = pmic->get_ctrl_reg(id);
+ if (reg < 0)
+ return reg;
+
+ switch (id) {
+ case TPS65911_REG_LDO1:
+ case TPS65911_REG_LDO2:
+ case TPS65911_REG_LDO4:
+ return tps65910_modify_bits(pmic, reg,
+ (selector << LDO_SEL_SHIFT), LDO1_SEL_MASK);
+ case TPS65911_REG_LDO3:
+ case TPS65911_REG_LDO5:
+ case TPS65911_REG_LDO6:
+ case TPS65911_REG_LDO7:
+ case TPS65911_REG_LDO8:
+ case TPS65910_REG_VIO:
+ return tps65910_modify_bits(pmic, reg,
+ (selector << LDO_SEL_SHIFT), LDO3_SEL_MASK);
+ }
+
+ return -EINVAL;
+}
+
+
+static int tps65910_list_voltage_dcdc(struct regulator_dev *dev,
+ unsigned selector)
+{
+ int volt, mult = 1, id = rdev_get_id(dev);
+
+ switch (id) {
+ case TPS65910_REG_VDD1:
+ case TPS65910_REG_VDD2:
+ mult = (selector / VDD1_2_NUM_VOLTS) + 1;
+ volt = VDD1_2_MIN_VOLT +
+ (selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET;
+ case TPS65911_REG_VDDCTRL:
+ volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET);
+ }
+
+ return volt * 100 * mult;
+}
+
+static int tps65910_list_voltage(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int id = rdev_get_id(dev), voltage;
+
+ if (id < TPS65910_REG_VIO || id > TPS65910_REG_VMMC)
+ return -EINVAL;
+
+ if (selector >= pmic->info[id]->table_len)
+ return -EINVAL;
+ else
+ voltage = pmic->info[id]->table[selector] * 1000;
+
+ return voltage;
+}
+
+static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector)
+{
+ struct tps65910_reg *pmic = rdev_get_drvdata(dev);
+ int step_mv = 0, id = rdev_get_id(dev);
+
+ switch(id) {
+ case TPS65911_REG_LDO1:
+ case TPS65911_REG_LDO2:
+ case TPS65911_REG_LDO4:
+ /* The first 5 values of the selector correspond to 1V */
+ if (selector < 5)
+ selector = 0;
+ else
+ selector -= 4;
+
+ step_mv = 50;
+ break;
+ case TPS65911_REG_LDO3:
+ case TPS65911_REG_LDO5:
+ case TPS65911_REG_LDO6:
+ case TPS65911_REG_LDO7:
+ case TPS65911_REG_LDO8:
+ /* The first 3 values of the selector correspond to 1V */
+ if (selector < 3)
+ selector = 0;
+ else
+ selector -= 2;
+
+ step_mv = 100;
+ break;
+ case TPS65910_REG_VIO:
+ return pmic->info[id]->table[selector] * 1000;
+ default:
+ return -EINVAL;
+ }
+
+ return (LDO_MIN_VOLT + selector * step_mv) * 1000;
+}
+
+/* Regulator ops (except VRTC) */
+static struct regulator_ops tps65910_ops_dcdc = {
+ .is_enabled = tps65910_is_enabled,
+ .enable = tps65910_enable,
+ .disable = tps65910_disable,
+ .set_mode = tps65910_set_mode,
+ .get_mode = tps65910_get_mode,
+ .get_voltage = tps65910_get_voltage_dcdc,
+ .set_voltage_sel = tps65910_set_voltage_dcdc,
+ .list_voltage = tps65910_list_voltage_dcdc,
+};
+
+static struct regulator_ops tps65910_ops_vdd3 = {
+ .is_enabled = tps65910_is_enabled,
+ .enable = tps65910_enable,
+ .disable = tps65910_disable,
+ .set_mode = tps65910_set_mode,
+ .get_mode = tps65910_get_mode,
+ .get_voltage = tps65910_get_voltage_vdd3,
+ .list_voltage = tps65910_list_voltage,
+};
+
+static struct regulator_ops tps65910_ops = {
+ .is_enabled = tps65910_is_enabled,
+ .enable = tps65910_enable,
+ .disable = tps65910_disable,
+ .set_mode = tps65910_set_mode,
+ .get_mode = tps65910_get_mode,
+ .get_voltage = tps65910_get_voltage,
+ .set_voltage_sel = tps65910_set_voltage,
+ .list_voltage = tps65910_list_voltage,
+};
+
+static struct regulator_ops tps65911_ops = {
+ .is_enabled = tps65910_is_enabled,
+ .enable = tps65910_enable,
+ .disable = tps65910_disable,
+ .set_mode = tps65910_set_mode,
+ .get_mode = tps65910_get_mode,
+ .get_voltage = tps65911_get_voltage,
+ .set_voltage_sel = tps65911_set_voltage,
+ .list_voltage = tps65911_list_voltage,
+};
+
+static __devinit int tps65910_probe(struct platform_device *pdev)
+{
+ struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
+ struct tps_info *info;
+ struct regulator_init_data *reg_data;
+ struct regulator_dev *rdev;
+ struct tps65910_reg *pmic;
+ struct tps65910_board *pmic_plat_data;
+ int i, err;
+
+ pmic_plat_data = dev_get_platdata(tps65910->dev);
+ if (!pmic_plat_data)
+ return -EINVAL;
+
+ reg_data = pmic_plat_data->tps65910_pmic_init_data;
+
+ pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ mutex_init(&pmic->mutex);
+ pmic->mfd = tps65910;
+ platform_set_drvdata(pdev, pmic);
+
+ /* Give control of all register to control port */
+ tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL,
+ DEVCTRL_SR_CTL_I2C_SEL_MASK);
+
+ switch(tps65910_chip_id(tps65910)) {
+ case TPS65910:
+ pmic->get_ctrl_reg = &tps65910_get_ctrl_register;
+ info = tps65910_regs;
+ case TPS65911:
+ pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
+ info = tps65911_regs;
+ default:
+ pr_err("Invalid tps chip version\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < TPS65910_NUM_REGULATOR; i++, info++, reg_data++) {
+ /* Register the regulators */
+ pmic->info[i] = info;
+
+ pmic->desc[i].name = info->name;
+ pmic->desc[i].id = i;
+ pmic->desc[i].n_voltages = info->table_len;
+
+ if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) {
+ pmic->desc[i].ops = &tps65910_ops_dcdc;
+ } else if (i == TPS65910_REG_VDD3) {
+ if (tps65910_chip_id(tps65910) == TPS65910)
+ pmic->desc[i].ops = &tps65910_ops_vdd3;
+ else
+ pmic->desc[i].ops = &tps65910_ops_dcdc;
+ } else {
+ if (tps65910_chip_id(tps65910) == TPS65910)
+ pmic->desc[i].ops = &tps65910_ops;
+ else
+ pmic->desc[i].ops = &tps65911_ops;
+ }
+
+ pmic->desc[i].type = REGULATOR_VOLTAGE;
+ pmic->desc[i].owner = THIS_MODULE;
+
+ rdev = regulator_register(&pmic->desc[i],
+ tps65910->dev, reg_data, pmic);
+ if (IS_ERR(rdev)) {
+ dev_err(tps65910->dev,
+ "failed to register %s regulator\n",
+ pdev->name);
+ err = PTR_ERR(rdev);
+ goto err;
+ }
+
+ /* Save regulator for cleanup */
+ pmic->rdev[i] = rdev;
+ }
+ return 0;
+
+err:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+ return err;
+}
+
+static int __devexit tps65910_remove(struct platform_device *pdev)
+{
+ struct tps65910_reg *tps65910_reg = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < TPS65910_NUM_REGULATOR; i++)
+ regulator_unregister(tps65910_reg->rdev[i]);
+
+ kfree(tps65910_reg);
+ return 0;
+}
+
+static struct platform_driver tps65910_driver = {
+ .driver = {
+ .name = "tps65910-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps65910_probe,
+ .remove = __devexit_p(tps65910_remove),
+};
+
+static int __init tps65910_init(void)
+{
+ return platform_driver_register(&tps65910_driver);
+}
+subsys_initcall(tps65910_init);
+
+static void __exit tps65910_cleanup(void)
+{
+ platform_driver_unregister(&tps65910_driver);
+}
+module_exit(tps65910_cleanup);
+
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_DESCRIPTION("TPS6507x voltage regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65910-pmic");
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 6a29285..87fe0f7 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -51,8 +51,13 @@
u16 min_mV;
u16 max_mV;
+ u8 flags;
+
/* used by regulator core */
struct regulator_desc desc;
+
+ /* chip specific features */
+ unsigned long features;
};
@@ -70,12 +75,35 @@
#define VREG_TRANS 1
#define VREG_STATE 2
#define VREG_VOLTAGE 3
+#define VREG_VOLTAGE_SMPS 4
/* TWL6030 Misc register offsets */
#define VREG_BC_ALL 1
#define VREG_BC_REF 2
#define VREG_BC_PROC 3
#define VREG_BC_CLK_RST 4
+/* TWL6030 LDO register values for CFG_STATE */
+#define TWL6030_CFG_STATE_OFF 0x00
+#define TWL6030_CFG_STATE_ON 0x01
+#define TWL6030_CFG_STATE_OFF2 0x02
+#define TWL6030_CFG_STATE_SLEEP 0x03
+#define TWL6030_CFG_STATE_GRP_SHIFT 5
+#define TWL6030_CFG_STATE_APP_SHIFT 2
+#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT)
+#define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\
+ TWL6030_CFG_STATE_APP_SHIFT)
+
+/* Flags for SMPS Voltage reading */
+#define SMPS_OFFSET_EN BIT(0)
+#define SMPS_EXTENDED_EN BIT(1)
+
+/* twl6025 SMPS EPROM values */
+#define TWL6030_SMPS_OFFSET 0xB0
+#define TWL6030_SMPS_MULT 0xB3
+#define SMPS_MULTOFFSET_SMPS4 BIT(0)
+#define SMPS_MULTOFFSET_VIO BIT(1)
+#define SMPS_MULTOFFSET_SMPS3 BIT(6)
+
static inline int
twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset)
{
@@ -118,21 +146,38 @@
#define P2_GRP_6030 BIT(1) /* "peripherals" */
#define P1_GRP_6030 BIT(0) /* CPU/Linux */
-static int twlreg_is_enabled(struct regulator_dev *rdev)
+static int twl4030reg_is_enabled(struct regulator_dev *rdev)
{
int state = twlreg_grp(rdev);
if (state < 0)
return state;
- if (twl_class_is_4030())
- state &= P1_GRP_4030;
- else
- state &= P1_GRP_6030;
- return state;
+ return state & P1_GRP_4030;
}
-static int twlreg_enable(struct regulator_dev *rdev)
+static int twl6030reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int grp = 0, val;
+
+ if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+ grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
+ if (grp < 0)
+ return grp;
+
+ if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+ grp &= P1_GRP_6030;
+ else
+ grp = 1;
+
+ val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE);
+ val = TWL6030_CFG_STATE_APP(val);
+
+ return grp && (val == TWL6030_CFG_STATE_ON);
+}
+
+static int twl4030reg_enable(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp;
@@ -142,10 +187,7 @@
if (grp < 0)
return grp;
- if (twl_class_is_4030())
- grp |= P1_GRP_4030;
- else
- grp |= P1_GRP_6030;
+ grp |= P1_GRP_4030;
ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
@@ -154,30 +196,64 @@
return ret;
}
-static int twlreg_disable(struct regulator_dev *rdev)
+static int twl6030reg_enable(struct regulator_dev *rdev)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int grp = 0;
+ int ret;
+
+ if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+ grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
+ if (grp < 0)
+ return grp;
+
+ ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+ grp << TWL6030_CFG_STATE_GRP_SHIFT |
+ TWL6030_CFG_STATE_ON);
+
+ udelay(info->delay);
+
+ return ret;
+}
+
+static int twl4030reg_disable(struct regulator_dev *rdev)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
int grp;
+ int ret;
grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
if (grp < 0)
return grp;
- if (twl_class_is_4030())
- grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030);
- else
- grp &= ~(P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030);
+ grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030);
- return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
+ ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
+
+ return ret;
}
-static int twlreg_get_status(struct regulator_dev *rdev)
+static int twl6030reg_disable(struct regulator_dev *rdev)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int grp = 0;
+ int ret;
+
+ if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+ grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030;
+
+ /* For 6030, set the off state for all grps enabled */
+ ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE,
+ (grp) << TWL6030_CFG_STATE_GRP_SHIFT |
+ TWL6030_CFG_STATE_OFF);
+
+ return ret;
+}
+
+static int twl4030reg_get_status(struct regulator_dev *rdev)
{
int state = twlreg_grp(rdev);
- if (twl_class_is_6030())
- return 0; /* FIXME return for 6030 regulator */
-
if (state < 0)
return state;
state &= 0x0f;
@@ -190,15 +266,39 @@
: REGULATOR_STATUS_STANDBY;
}
-static int twlreg_set_mode(struct regulator_dev *rdev, unsigned mode)
+static int twl6030reg_get_status(struct regulator_dev *rdev)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int val;
+
+ val = twlreg_grp(rdev);
+ if (val < 0)
+ return val;
+
+ val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE);
+
+ switch (TWL6030_CFG_STATE_APP(val)) {
+ case TWL6030_CFG_STATE_ON:
+ return REGULATOR_STATUS_NORMAL;
+
+ case TWL6030_CFG_STATE_SLEEP:
+ return REGULATOR_STATUS_STANDBY;
+
+ case TWL6030_CFG_STATE_OFF:
+ case TWL6030_CFG_STATE_OFF2:
+ default:
+ break;
+ }
+
+ return REGULATOR_STATUS_OFF;
+}
+
+static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
{
struct twlreg_info *info = rdev_get_drvdata(rdev);
unsigned message;
int status;
- if (twl_class_is_6030())
- return 0; /* FIXME return for 6030 regulator */
-
/* We can only set the mode through state machine commands... */
switch (mode) {
case REGULATOR_MODE_NORMAL:
@@ -227,6 +327,36 @@
message & 0xff, TWL4030_PM_MASTER_PB_WORD_LSB);
}
+static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int grp = 0;
+ int val;
+
+ if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS)))
+ grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
+
+ if (grp < 0)
+ return grp;
+
+ /* Compose the state register settings */
+ val = grp << TWL6030_CFG_STATE_GRP_SHIFT;
+ /* We can only set the mode through state machine commands... */
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val |= TWL6030_CFG_STATE_ON;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val |= TWL6030_CFG_STATE_SLEEP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val);
+}
+
/*----------------------------------------------------------------------*/
/*
@@ -375,13 +505,13 @@
.set_voltage = twl4030ldo_set_voltage,
.get_voltage = twl4030ldo_get_voltage,
- .enable = twlreg_enable,
- .disable = twlreg_disable,
- .is_enabled = twlreg_is_enabled,
+ .enable = twl4030reg_enable,
+ .disable = twl4030reg_disable,
+ .is_enabled = twl4030reg_is_enabled,
- .set_mode = twlreg_set_mode,
+ .set_mode = twl4030reg_set_mode,
- .get_status = twlreg_get_status,
+ .get_status = twl4030reg_get_status,
};
static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned index)
@@ -433,13 +563,13 @@
.set_voltage = twl6030ldo_set_voltage,
.get_voltage = twl6030ldo_get_voltage,
- .enable = twlreg_enable,
- .disable = twlreg_disable,
- .is_enabled = twlreg_is_enabled,
+ .enable = twl6030reg_enable,
+ .disable = twl6030reg_disable,
+ .is_enabled = twl6030reg_is_enabled,
- .set_mode = twlreg_set_mode,
+ .set_mode = twl6030reg_set_mode,
- .get_status = twlreg_get_status,
+ .get_status = twl6030reg_get_status,
};
/*----------------------------------------------------------------------*/
@@ -461,25 +591,242 @@
return info->min_mV * 1000;
}
-static struct regulator_ops twlfixed_ops = {
+static struct regulator_ops twl4030fixed_ops = {
.list_voltage = twlfixed_list_voltage,
.get_voltage = twlfixed_get_voltage,
- .enable = twlreg_enable,
- .disable = twlreg_disable,
- .is_enabled = twlreg_is_enabled,
+ .enable = twl4030reg_enable,
+ .disable = twl4030reg_disable,
+ .is_enabled = twl4030reg_is_enabled,
- .set_mode = twlreg_set_mode,
+ .set_mode = twl4030reg_set_mode,
- .get_status = twlreg_get_status,
+ .get_status = twl4030reg_get_status,
+};
+
+static struct regulator_ops twl6030fixed_ops = {
+ .list_voltage = twlfixed_list_voltage,
+
+ .get_voltage = twlfixed_get_voltage,
+
+ .enable = twl6030reg_enable,
+ .disable = twl6030reg_disable,
+ .is_enabled = twl6030reg_is_enabled,
+
+ .set_mode = twl6030reg_set_mode,
+
+ .get_status = twl6030reg_get_status,
};
static struct regulator_ops twl6030_fixed_resource = {
- .enable = twlreg_enable,
- .disable = twlreg_disable,
- .is_enabled = twlreg_is_enabled,
- .get_status = twlreg_get_status,
+ .enable = twl6030reg_enable,
+ .disable = twl6030reg_disable,
+ .is_enabled = twl6030reg_is_enabled,
+ .get_status = twl6030reg_get_status,
+};
+
+/*
+ * SMPS status and control
+ */
+
+static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+
+ int voltage = 0;
+
+ switch (info->flags) {
+ case SMPS_OFFSET_EN:
+ voltage = 100000;
+ /* fall through */
+ case 0:
+ switch (index) {
+ case 0:
+ voltage = 0;
+ break;
+ case 58:
+ voltage = 1350 * 1000;
+ break;
+ case 59:
+ voltage = 1500 * 1000;
+ break;
+ case 60:
+ voltage = 1800 * 1000;
+ break;
+ case 61:
+ voltage = 1900 * 1000;
+ break;
+ case 62:
+ voltage = 2100 * 1000;
+ break;
+ default:
+ voltage += (600000 + (12500 * (index - 1)));
+ }
+ break;
+ case SMPS_EXTENDED_EN:
+ switch (index) {
+ case 0:
+ voltage = 0;
+ break;
+ case 58:
+ voltage = 2084 * 1000;
+ break;
+ case 59:
+ voltage = 2315 * 1000;
+ break;
+ case 60:
+ voltage = 2778 * 1000;
+ break;
+ case 61:
+ voltage = 2932 * 1000;
+ break;
+ case 62:
+ voltage = 3241 * 1000;
+ break;
+ default:
+ voltage = (1852000 + (38600 * (index - 1)));
+ }
+ break;
+ case SMPS_OFFSET_EN | SMPS_EXTENDED_EN:
+ switch (index) {
+ case 0:
+ voltage = 0;
+ break;
+ case 58:
+ voltage = 4167 * 1000;
+ break;
+ case 59:
+ voltage = 2315 * 1000;
+ break;
+ case 60:
+ voltage = 2778 * 1000;
+ break;
+ case 61:
+ voltage = 2932 * 1000;
+ break;
+ case 62:
+ voltage = 3241 * 1000;
+ break;
+ default:
+ voltage = (2161000 + (38600 * (index - 1)));
+ }
+ break;
+ }
+
+ return voltage;
+}
+
+static int
+twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
+ unsigned int *selector)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int vsel = 0;
+
+ switch (info->flags) {
+ case 0:
+ if (min_uV == 0)
+ vsel = 0;
+ else if ((min_uV >= 600000) && (max_uV <= 1300000)) {
+ vsel = (min_uV - 600000) / 125;
+ if (vsel % 100)
+ vsel += 100;
+ vsel /= 100;
+ vsel++;
+ }
+ /* Values 1..57 for vsel are linear and can be calculated
+ * values 58..62 are non linear.
+ */
+ else if ((min_uV > 1900000) && (max_uV >= 2100000))
+ vsel = 62;
+ else if ((min_uV > 1800000) && (max_uV >= 1900000))
+ vsel = 61;
+ else if ((min_uV > 1500000) && (max_uV >= 1800000))
+ vsel = 60;
+ else if ((min_uV > 1350000) && (max_uV >= 1500000))
+ vsel = 59;
+ else if ((min_uV > 1300000) && (max_uV >= 1350000))
+ vsel = 58;
+ else
+ return -EINVAL;
+ break;
+ case SMPS_OFFSET_EN:
+ if (min_uV == 0)
+ vsel = 0;
+ else if ((min_uV >= 700000) && (max_uV <= 1420000)) {
+ vsel = (min_uV - 700000) / 125;
+ if (vsel % 100)
+ vsel += 100;
+ vsel /= 100;
+ vsel++;
+ }
+ /* Values 1..57 for vsel are linear and can be calculated
+ * values 58..62 are non linear.
+ */
+ else if ((min_uV > 1900000) && (max_uV >= 2100000))
+ vsel = 62;
+ else if ((min_uV > 1800000) && (max_uV >= 1900000))
+ vsel = 61;
+ else if ((min_uV > 1350000) && (max_uV >= 1800000))
+ vsel = 60;
+ else if ((min_uV > 1350000) && (max_uV >= 1500000))
+ vsel = 59;
+ else if ((min_uV > 1300000) && (max_uV >= 1350000))
+ vsel = 58;
+ else
+ return -EINVAL;
+ break;
+ case SMPS_EXTENDED_EN:
+ if (min_uV == 0)
+ vsel = 0;
+ else if ((min_uV >= 1852000) && (max_uV <= 4013600)) {
+ vsel = (min_uV - 1852000) / 386;
+ if (vsel % 100)
+ vsel += 100;
+ vsel /= 100;
+ vsel++;
+ }
+ break;
+ case SMPS_OFFSET_EN|SMPS_EXTENDED_EN:
+ if (min_uV == 0)
+ vsel = 0;
+ else if ((min_uV >= 2161000) && (max_uV <= 4321000)) {
+ vsel = (min_uV - 1852000) / 386;
+ if (vsel % 100)
+ vsel += 100;
+ vsel /= 100;
+ vsel++;
+ }
+ break;
+ }
+
+ *selector = vsel;
+
+ return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS,
+ vsel);
+}
+
+static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+
+ return twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS);
+}
+
+static struct regulator_ops twlsmps_ops = {
+ .list_voltage = twl6030smps_list_voltage,
+
+ .set_voltage = twl6030smps_set_voltage,
+ .get_voltage_sel = twl6030smps_get_voltage_sel,
+
+ .enable = twl6030reg_enable,
+ .disable = twl6030reg_disable,
+ .is_enabled = twl6030reg_is_enabled,
+
+ .set_mode = twl6030reg_set_mode,
+
+ .get_status = twl6030reg_get_status,
};
/*----------------------------------------------------------------------*/
@@ -487,11 +834,10 @@
#define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
remap_conf) \
TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
- remap_conf, TWL4030)
-#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
- remap_conf) \
+ remap_conf, TWL4030, twl4030fixed_ops)
+#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \
TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
- remap_conf, TWL6030)
+ 0x0, TWL6030, twl6030fixed_ops)
#define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \
.base = offset, \
@@ -510,13 +856,11 @@
}, \
}
-#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num, \
- remap_conf) { \
+#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
.base = offset, \
.id = num, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
- .remap = remap_conf, \
.desc = { \
.name = #label, \
.id = TWL6030_REG_##label, \
@@ -527,9 +871,23 @@
}, \
}
+#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+ .base = offset, \
+ .id = num, \
+ .min_mV = min_mVolts, \
+ .max_mV = max_mVolts, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL6025_REG_##label, \
+ .n_voltages = ((max_mVolts - min_mVolts)/100) + 1, \
+ .ops = &twl6030ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
#define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \
- family) { \
+ family, operations) { \
.base = offset, \
.id = num, \
.min_mV = mVolts, \
@@ -539,17 +897,16 @@
.name = #label, \
.id = family##_REG_##label, \
.n_voltages = 1, \
- .ops = &twlfixed_ops, \
+ .ops = &operations, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
}
-#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay, remap_conf) { \
+#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \
.base = offset, \
.id = num, \
.delay = turnon_delay, \
- .remap = remap_conf, \
.desc = { \
.name = #label, \
.id = TWL6030_REG_##label, \
@@ -559,6 +916,21 @@
}, \
}
+#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \
+ .base = offset, \
+ .id = num, \
+ .min_mV = 600, \
+ .max_mV = 2100, \
+ .desc = { \
+ .name = #label, \
+ .id = TWL6025_REG_##label, \
+ .n_voltages = 63, \
+ .ops = &twlsmps_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
/*
* We list regulators here if systems need some level of
* software control over them after boot.
@@ -589,19 +961,52 @@
/* 6030 REG with base as PMC Slave Misc : 0x0030 */
/* Turnon-delay and remap configuration values for 6030 are not
verified since the specification is not public */
- TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1, 0x21),
- TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2, 0x21),
- TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3, 0x21),
- TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4, 0x21),
- TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5, 0x21),
- TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7, 0x21),
- TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x21),
- TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x21),
- TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x21),
- TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21),
- TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0, 0x21),
+ TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1),
+ TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2),
+ TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3),
+ TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4),
+ TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5),
+ TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7),
+ TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0),
+ TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0),
+ TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0),
+ TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0),
+ TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0),
+
+ /* 6025 are renamed compared to 6030 versions */
+ TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1),
+ TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2),
+ TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3),
+ TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4),
+ TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5),
+ TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7),
+ TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16),
+ TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17),
+ TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18),
+
+ TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1),
+ TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2),
+ TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3),
};
+static u8 twl_get_smps_offset(void)
+{
+ u8 value;
+
+ twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value,
+ TWL6030_SMPS_OFFSET);
+ return value;
+}
+
+static u8 twl_get_smps_mult(void)
+{
+ u8 value;
+
+ twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value,
+ TWL6030_SMPS_MULT);
+ return value;
+}
+
static int __devinit twlreg_probe(struct platform_device *pdev)
{
int i;
@@ -623,6 +1028,9 @@
if (!initdata)
return -EINVAL;
+ /* copy the features into regulator data */
+ info->features = (unsigned long)initdata->driver_data;
+
/* Constrain board-specific capabilities according to what
* this driver and the chip itself can actually do.
*/
@@ -645,6 +1053,27 @@
break;
}
+ switch (pdev->id) {
+ case TWL6025_REG_SMPS3:
+ if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3)
+ info->flags |= SMPS_EXTENDED_EN;
+ if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3)
+ info->flags |= SMPS_OFFSET_EN;
+ break;
+ case TWL6025_REG_SMPS4:
+ if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4)
+ info->flags |= SMPS_EXTENDED_EN;
+ if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4)
+ info->flags |= SMPS_OFFSET_EN;
+ break;
+ case TWL6025_REG_VIO:
+ if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO)
+ info->flags |= SMPS_EXTENDED_EN;
+ if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO)
+ info->flags |= SMPS_OFFSET_EN;
+ break;
+ }
+
rdev = regulator_register(&info->desc, &pdev->dev, initdata, info);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "can't register %s, %ld\n",
@@ -653,7 +1082,8 @@
}
platform_set_drvdata(pdev, rdev);
- twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP,
+ if (twl_class_is_4030())
+ twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP,
info->remap);
/* NOTE: many regulators support short-circuit IRQs (presentable
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index e93453b..a0982e8 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -600,7 +600,6 @@
static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
- struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL);
@@ -776,7 +775,6 @@
static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
- struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
index b42d01c..0f12c70 100644
--- a/drivers/regulator/wm8400-regulator.c
+++ b/drivers/regulator/wm8400-regulator.c
@@ -55,7 +55,7 @@
return 1600000 + ((selector - 14) * 100000);
}
-static int wm8400_ldo_get_voltage(struct regulator_dev *dev)
+static int wm8400_ldo_get_voltage_sel(struct regulator_dev *dev)
{
struct wm8400 *wm8400 = rdev_get_drvdata(dev);
u16 val;
@@ -63,7 +63,7 @@
val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
val &= WM8400_LDO1_VSEL_MASK;
- return wm8400_ldo_list_voltage(dev, val);
+ return val;
}
static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
@@ -104,7 +104,7 @@
.enable = wm8400_ldo_enable,
.disable = wm8400_ldo_disable,
.list_voltage = wm8400_ldo_list_voltage,
- .get_voltage = wm8400_ldo_get_voltage,
+ .get_voltage_sel = wm8400_ldo_get_voltage_sel,
.set_voltage = wm8400_ldo_set_voltage,
};
@@ -145,7 +145,7 @@
return 850000 + (selector * 25000);
}
-static int wm8400_dcdc_get_voltage(struct regulator_dev *dev)
+static int wm8400_dcdc_get_voltage_sel(struct regulator_dev *dev)
{
struct wm8400 *wm8400 = rdev_get_drvdata(dev);
u16 val;
@@ -154,7 +154,7 @@
val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
val &= WM8400_DC1_VSEL_MASK;
- return 850000 + (25000 * val);
+ return val;
}
static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
@@ -261,7 +261,7 @@
.enable = wm8400_dcdc_enable,
.disable = wm8400_dcdc_disable,
.list_voltage = wm8400_dcdc_list_voltage,
- .get_voltage = wm8400_dcdc_get_voltage,
+ .get_voltage_sel = wm8400_dcdc_get_voltage_sel,
.set_voltage = wm8400_dcdc_set_voltage,
.get_mode = wm8400_dcdc_get_mode,
.set_mode = wm8400_dcdc_set_mode,
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index b8f4e9e..f822e13 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -125,6 +125,16 @@
if I2C
+config RTC_DRV_88PM860X
+ tristate "Marvell 88PM860x"
+ depends on RTC_CLASS && I2C && MFD_88PM860X
+ help
+ If you say yes here you get support for RTC function in Marvell
+ 88PM860x chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-88pm860x.
+
config RTC_DRV_DS1307
tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
help
@@ -351,12 +361,39 @@
This driver can also be built as a module. If so, the module
will be called rtc-rx8025.
+config RTC_DRV_EM3027
+ tristate "EM Microelectronic EM3027"
+ help
+ If you say yes here you get support for the EM
+ Microelectronic EM3027 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-em3027.
+
+config RTC_DRV_RV3029C2
+ tristate "Micro Crystal RTC"
+ help
+ If you say yes here you get support for the Micro Crystal
+ RV3029-C2 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rv3029c2.
+
endif # I2C
comment "SPI RTC drivers"
if SPI_MASTER
+config RTC_DRV_M41T93
+ tristate "ST M41T93"
+ help
+ If you say yes here you will get support for the
+ ST M41T93 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m41t93.
+
config RTC_DRV_M41T94
tristate "ST M41T94"
help
@@ -645,6 +682,14 @@
This driver can also be built as a module. If so, the module
will be called "rtc-wm8350".
+config RTC_DRV_SPEAR
+ tristate "SPEAR ST RTC"
+ depends on PLAT_SPEAR
+ default y
+ help
+ If you say Y here you will get support for the RTC found on
+ spear
+
config RTC_DRV_PCF50633
depends on MFD_PCF50633
tristate "NXP PCF50633 RTC"
@@ -874,6 +919,13 @@
This RTC driver uses PXA RTC registers available since pxa27x
series (RDxR, RYxR) instead of legacy RCNR, RTAR.
+config RTC_DRV_VT8500
+ tristate "VIA/WonderMedia 85xx SoC RTC"
+ depends on ARCH_VT8500
+ help
+ If you say Y here you will get access to the real time clock
+ built into your VIA VT8500 SoC or its relatives.
+
config RTC_DRV_SUN4V
bool "SUN4V Hypervisor RTC"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 9574748..213d725 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -15,6 +15,7 @@
# Keep the list ordered.
+obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
@@ -43,6 +44,7 @@
obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
+obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
@@ -52,6 +54,7 @@
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
+obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
@@ -81,12 +84,14 @@
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
+obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
@@ -98,6 +103,7 @@
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c
new file mode 100644
index 0000000..64b847b
--- /dev/null
+++ b/drivers/rtc/rtc-88pm860x.c
@@ -0,0 +1,427 @@
+/*
+ * Real Time Clock driver for Marvell 88PM860x PMIC
+ *
+ * Copyright (c) 2010 Marvell International Ltd.
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm860x.h>
+
+#define VRTC_CALIBRATION
+
+struct pm860x_rtc_info {
+ struct pm860x_chip *chip;
+ struct i2c_client *i2c;
+ struct rtc_device *rtc_dev;
+ struct device *dev;
+ struct delayed_work calib_work;
+
+ int irq;
+ int vrtc;
+ int (*sync)(unsigned int ticks);
+};
+
+#define REG_VRTC_MEAS1 0x7D
+
+#define REG0_ADDR 0xB0
+#define REG1_ADDR 0xB2
+#define REG2_ADDR 0xB4
+#define REG3_ADDR 0xB6
+
+#define REG0_DATA 0xB1
+#define REG1_DATA 0xB3
+#define REG2_DATA 0xB5
+#define REG3_DATA 0xB7
+
+/* bit definitions of Measurement Enable Register 2 (0x51) */
+#define MEAS2_VRTC (1 << 0)
+
+/* bit definitions of RTC Register 1 (0xA0) */
+#define ALARM_EN (1 << 3)
+#define ALARM_WAKEUP (1 << 4)
+#define ALARM (1 << 5)
+#define RTC1_USE_XO (1 << 7)
+
+#define VRTC_CALIB_INTERVAL (HZ * 60 * 10) /* 10 minutes */
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct pm860x_rtc_info *info = (struct pm860x_rtc_info *)data;
+ int mask;
+
+ mask = ALARM | ALARM_WAKEUP;
+ pm860x_set_bits(info->i2c, PM8607_RTC1, mask | ALARM_EN, mask);
+ rtc_update_irq(info->rtc_dev, 1, RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int pm860x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM, ALARM);
+ else
+ pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM, 0);
+ return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ */
+static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
+ struct rtc_time *alrm)
+{
+ unsigned long next_time;
+ unsigned long now_time;
+
+ next->tm_year = now->tm_year;
+ next->tm_mon = now->tm_mon;
+ next->tm_mday = now->tm_mday;
+ next->tm_hour = alrm->tm_hour;
+ next->tm_min = alrm->tm_min;
+ next->tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(now, &now_time);
+ rtc_tm_to_time(next, &next_time);
+
+ if (next_time < now_time) {
+ /* Advance one day */
+ next_time += 60 * 60 * 24;
+ rtc_time_to_tm(next_time, next);
+ }
+}
+
+static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[8];
+ unsigned long ticks, base, data;
+
+ pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
+ dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+
+ /* load 32-bit read-only counter */
+ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, tm);
+
+ return 0;
+}
+
+static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+
+ if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ dev_dbg(info->dev, "Set time %d out of range. "
+ "Please set time between 1970 to 2038.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ rtc_tm_to_time(tm, &ticks);
+
+ /* load 32-bit read-only counter */
+ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ticks - data;
+ dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ pm860x_page_reg_write(info->i2c, REG0_DATA, (base >> 24) & 0xFF);
+ pm860x_page_reg_write(info->i2c, REG1_DATA, (base >> 16) & 0xFF);
+ pm860x_page_reg_write(info->i2c, REG2_DATA, (base >> 8) & 0xFF);
+ pm860x_page_reg_write(info->i2c, REG3_DATA, base & 0xFF);
+
+ if (info->sync)
+ info->sync(ticks);
+ return 0;
+}
+
+static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[8];
+ unsigned long ticks, base, data;
+ int ret;
+
+ pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
+ dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+
+ pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &alrm->time);
+ ret = pm860x_reg_read(info->i2c, PM8607_RTC1);
+ alrm->enabled = (ret & ALARM_EN) ? 1 : 0;
+ alrm->pending = (ret & (ALARM | ALARM_WAKEUP)) ? 1 : 0;
+ return 0;
+}
+
+static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ struct rtc_time now_tm, alarm_tm;
+ unsigned long ticks, base, data;
+ unsigned char buf[8];
+ int mask;
+
+ pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0);
+
+ pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
+ dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+
+ /* load 32-bit read-only counter */
+ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &now_tm);
+ rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
+ /* get new ticks for alarm in 24 hours */
+ rtc_tm_to_time(&alarm_tm, &ticks);
+ data = ticks - base;
+
+ buf[0] = data & 0xff;
+ buf[1] = (data >> 8) & 0xff;
+ buf[2] = (data >> 16) & 0xff;
+ buf[3] = (data >> 24) & 0xff;
+ pm860x_bulk_write(info->i2c, PM8607_RTC_EXPIRE1, 4, buf);
+ if (alrm->enabled) {
+ mask = ALARM | ALARM_WAKEUP | ALARM_EN;
+ pm860x_set_bits(info->i2c, PM8607_RTC1, mask, mask);
+ } else {
+ mask = ALARM | ALARM_WAKEUP | ALARM_EN;
+ pm860x_set_bits(info->i2c, PM8607_RTC1, mask,
+ ALARM | ALARM_WAKEUP);
+ }
+ return 0;
+}
+
+static const struct rtc_class_ops pm860x_rtc_ops = {
+ .read_time = pm860x_rtc_read_time,
+ .set_time = pm860x_rtc_set_time,
+ .read_alarm = pm860x_rtc_read_alarm,
+ .set_alarm = pm860x_rtc_set_alarm,
+ .alarm_irq_enable = pm860x_rtc_alarm_irq_enable,
+};
+
+#ifdef VRTC_CALIBRATION
+static void calibrate_vrtc_work(struct work_struct *work)
+{
+ struct pm860x_rtc_info *info = container_of(work,
+ struct pm860x_rtc_info, calib_work.work);
+ unsigned char buf[2];
+ unsigned int sum, data, mean, vrtc_set;
+ int i;
+
+ for (i = 0, sum = 0; i < 16; i++) {
+ msleep(100);
+ pm860x_bulk_read(info->i2c, REG_VRTC_MEAS1, 2, buf);
+ data = (buf[0] << 4) | buf[1];
+ data = (data * 5400) >> 12; /* convert to mv */
+ sum += data;
+ }
+ mean = sum >> 4;
+ vrtc_set = 2700 + (info->vrtc & 0x3) * 200;
+ dev_dbg(info->dev, "mean:%d, vrtc_set:%d\n", mean, vrtc_set);
+
+ sum = pm860x_reg_read(info->i2c, PM8607_RTC_MISC1);
+ data = sum & 0x3;
+ if ((mean + 200) < vrtc_set) {
+ /* try higher voltage */
+ if (++data == 4)
+ goto out;
+ data = (sum & 0xf8) | (data & 0x3);
+ pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data);
+ } else if ((mean - 200) > vrtc_set) {
+ /* try lower voltage */
+ if (data-- == 0)
+ goto out;
+ data = (sum & 0xf8) | (data & 0x3);
+ pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data);
+ } else
+ goto out;
+ dev_dbg(info->dev, "set 0x%x to RTC_MISC1\n", data);
+ /* trigger next calibration since VRTC is updated */
+ schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL);
+ return;
+out:
+ /* disable measurement */
+ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0);
+ dev_dbg(info->dev, "finish VRTC calibration\n");
+ return;
+}
+#endif
+
+static int __devinit pm860x_rtc_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_rtc_pdata *pdata = NULL;
+ struct pm860x_rtc_info *info;
+ struct rtc_time tm;
+ unsigned long ticks = 0;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL)
+ dev_warn(&pdev->dev, "No platform data!\n");
+
+ info = kzalloc(sizeof(struct pm860x_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ info->chip = chip;
+ info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+ info->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, info);
+
+ ret = request_threaded_irq(info->irq, NULL, rtc_update_handler,
+ IRQF_ONESHOT, "rtc", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, ret);
+ goto out;
+ }
+
+ /* set addresses of 32-bit base value for RTC time */
+ pm860x_page_reg_write(info->i2c, REG0_ADDR, REG0_DATA);
+ pm860x_page_reg_write(info->i2c, REG1_ADDR, REG1_DATA);
+ pm860x_page_reg_write(info->i2c, REG2_ADDR, REG2_DATA);
+ pm860x_page_reg_write(info->i2c, REG3_ADDR, REG3_DATA);
+
+ ret = pm860x_rtc_read_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read initial time.\n");
+ goto out_rtc;
+ }
+ if ((tm.tm_year < 70) || (tm.tm_year > 138)) {
+ tm.tm_year = 70;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ ret = pm860x_rtc_set_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set initial time.\n");
+ goto out_rtc;
+ }
+ }
+ rtc_tm_to_time(&tm, &ticks);
+ if (pdata && pdata->sync) {
+ pdata->sync(ticks);
+ info->sync = pdata->sync;
+ }
+
+ info->rtc_dev = rtc_device_register("88pm860x-rtc", &pdev->dev,
+ &pm860x_rtc_ops, THIS_MODULE);
+ ret = PTR_ERR(info->rtc_dev);
+ if (IS_ERR(info->rtc_dev)) {
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ goto out_rtc;
+ }
+
+ /*
+ * enable internal XO instead of internal 3.25MHz clock since it can
+ * free running in PMIC power-down state.
+ */
+ pm860x_set_bits(info->i2c, PM8607_RTC1, RTC1_USE_XO, RTC1_USE_XO);
+
+#ifdef VRTC_CALIBRATION
+ /* <00> -- 2.7V, <01> -- 2.9V, <10> -- 3.1V, <11> -- 3.3V */
+ if (pdata && pdata->vrtc)
+ info->vrtc = pdata->vrtc & 0x3;
+ else
+ info->vrtc = 1;
+ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, MEAS2_VRTC);
+
+ /* calibrate VRTC */
+ INIT_DELAYED_WORK(&info->calib_work, calibrate_vrtc_work);
+ schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL);
+#endif /* VRTC_CALIBRATION */
+ return 0;
+out_rtc:
+ free_irq(info->irq, info);
+out:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit pm860x_rtc_remove(struct platform_device *pdev)
+{
+ struct pm860x_rtc_info *info = platform_get_drvdata(pdev);
+
+#ifdef VRTC_CALIBRATION
+ flush_scheduled_work();
+ /* disable measurement */
+ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0);
+#endif /* VRTC_CALIBRATION */
+
+ platform_set_drvdata(pdev, NULL);
+ rtc_device_unregister(info->rtc_dev);
+ free_irq(info->irq, info);
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver pm860x_rtc_driver = {
+ .driver = {
+ .name = "88pm860x-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = pm860x_rtc_probe,
+ .remove = __devexit_p(pm860x_rtc_remove),
+};
+
+static int __init pm860x_rtc_init(void)
+{
+ return platform_driver_register(&pm860x_rtc_driver);
+}
+module_init(pm860x_rtc_init);
+
+static void __exit pm860x_rtc_exit(void)
+{
+ platform_driver_unregister(&pm860x_rtc_driver);
+}
+module_exit(pm860x_rtc_exit);
+
+MODULE_DESCRIPTION("Marvell 88PM860x RTC driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c
new file mode 100644
index 0000000..d8e1c25
--- /dev/null
+++ b/drivers/rtc/rtc-em3027.c
@@ -0,0 +1,161 @@
+/*
+ * An rtc/i2c driver for the EM Microelectronic EM3027
+ * Copyright 2011 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on rtc-ds1672.c by Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+/* Registers */
+#define EM3027_REG_ON_OFF_CTRL 0x00
+#define EM3027_REG_IRQ_CTRL 0x01
+#define EM3027_REG_IRQ_FLAGS 0x02
+#define EM3027_REG_STATUS 0x03
+#define EM3027_REG_RST_CTRL 0x04
+
+#define EM3027_REG_WATCH_SEC 0x08
+#define EM3027_REG_WATCH_MIN 0x09
+#define EM3027_REG_WATCH_HOUR 0x0a
+#define EM3027_REG_WATCH_DATE 0x0b
+#define EM3027_REG_WATCH_DAY 0x0c
+#define EM3027_REG_WATCH_MON 0x0d
+#define EM3027_REG_WATCH_YEAR 0x0e
+
+#define EM3027_REG_ALARM_SEC 0x10
+#define EM3027_REG_ALARM_MIN 0x11
+#define EM3027_REG_ALARM_HOUR 0x12
+#define EM3027_REG_ALARM_DATE 0x13
+#define EM3027_REG_ALARM_DAY 0x14
+#define EM3027_REG_ALARM_MON 0x15
+#define EM3027_REG_ALARM_YEAR 0x16
+
+static struct i2c_driver em3027_driver;
+
+static int em3027_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ unsigned char addr = EM3027_REG_WATCH_SEC;
+ unsigned char buf[7];
+
+ struct i2c_msg msgs[] = {
+ {client->addr, 0, 1, &addr}, /* setup read addr */
+ {client->addr, I2C_M_RD, 7, buf}, /* read time/date */
+ };
+
+ /* read time/date registers */
+ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[0]);
+ tm->tm_min = bcd2bin(buf[1]);
+ tm->tm_hour = bcd2bin(buf[2]);
+ tm->tm_mday = bcd2bin(buf[3]);
+ tm->tm_wday = bcd2bin(buf[4]);
+ tm->tm_mon = bcd2bin(buf[5]);
+ tm->tm_year = bcd2bin(buf[6]) + 100;
+
+ return 0;
+}
+
+static int em3027_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[8];
+
+ struct i2c_msg msg = {
+ client->addr, 0, 8, buf, /* write time/date */
+ };
+
+ buf[0] = EM3027_REG_WATCH_SEC;
+ buf[1] = bin2bcd(tm->tm_sec);
+ buf[2] = bin2bcd(tm->tm_min);
+ buf[3] = bin2bcd(tm->tm_hour);
+ buf[4] = bin2bcd(tm->tm_mday);
+ buf[5] = bin2bcd(tm->tm_wday);
+ buf[6] = bin2bcd(tm->tm_mon);
+ buf[7] = bin2bcd(tm->tm_year % 100);
+
+ /* write time/date registers */
+ if ((i2c_transfer(client->adapter, &msg, 1)) != 1) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops em3027_rtc_ops = {
+ .read_time = em3027_get_time,
+ .set_time = em3027_set_time,
+};
+
+static int em3027_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rtc_device *rtc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ rtc = rtc_device_register(em3027_driver.driver.name, &client->dev,
+ &em3027_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static int em3027_remove(struct i2c_client *client)
+{
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct i2c_device_id em3027_id[] = {
+ { "em3027", 0 },
+ { }
+};
+
+static struct i2c_driver em3027_driver = {
+ .driver = {
+ .name = "rtc-em3027",
+ },
+ .probe = &em3027_probe,
+ .remove = &em3027_remove,
+ .id_table = em3027_id,
+};
+
+static int __init em3027_init(void)
+{
+ return i2c_add_driver(&em3027_driver);
+}
+
+static void __exit em3027_exit(void)
+{
+ i2c_del_driver(&em3027_driver);
+}
+
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("EM Microelectronic EM3027 RTC driver");
+MODULE_LICENSE("GPL");
+
+module_init(em3027_init);
+module_exit(em3027_exit);
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
new file mode 100644
index 0000000..1a84b3e
--- /dev/null
+++ b/drivers/rtc/rtc-m41t93.c
@@ -0,0 +1,225 @@
+/*
+ *
+ * Driver for ST M41T93 SPI RTC
+ *
+ * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH
+ *
+ * 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/bcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+
+#define M41T93_REG_SSEC 0
+#define M41T93_REG_ST_SEC 1
+#define M41T93_REG_MIN 2
+#define M41T93_REG_CENT_HOUR 3
+#define M41T93_REG_WDAY 4
+#define M41T93_REG_DAY 5
+#define M41T93_REG_MON 6
+#define M41T93_REG_YEAR 7
+
+
+#define M41T93_REG_ALM_HOUR_HT 0xc
+#define M41T93_REG_FLAGS 0xf
+
+#define M41T93_FLAG_ST (1 << 7)
+#define M41T93_FLAG_OF (1 << 2)
+#define M41T93_FLAG_BL (1 << 4)
+#define M41T93_FLAG_HT (1 << 6)
+
+static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
+{
+ u8 buf[2];
+
+ /* MSB must be '1' to write */
+ buf[0] = addr | 0x80;
+ buf[1] = data;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */
+ u8 * const data = &buf[1]; /* ptr to first data byte */
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (tm->tm_year < 100) {
+ dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n");
+ return -EINVAL;
+ }
+
+ data[M41T93_REG_SSEC] = 0;
+ data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec);
+ data[M41T93_REG_MIN] = bin2bcd(tm->tm_min);
+ data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) |
+ ((tm->tm_year/100-1) << 6);
+ data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday);
+ data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
+ data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1);
+ data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100);
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+
+static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ const u8 start_addr = 0;
+ u8 buf[8];
+ int century_after_1900;
+ int tmp;
+ int ret = 0;
+
+ /* Check status of clock. Two states must be considered:
+ 1. halt bit (HT) is set: the clock is running but update of readout
+ registers has been disabled due to power failure. This is normal
+ case after poweron. Time is valid after resetting HT bit.
+ 2. oscillator fail bit (OF) is set. Oscillator has be stopped and
+ time is invalid:
+ a) OF can be immeditely reset.
+ b) OF cannot be immediately reset: oscillator has to be restarted.
+ */
+ tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
+ if (tmp < 0)
+ return tmp;
+
+ if (tmp & M41T93_FLAG_HT) {
+ dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n");
+ m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT,
+ tmp & ~M41T93_FLAG_HT);
+ }
+
+ tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+ if (tmp < 0)
+ return tmp;
+
+ if (tmp & M41T93_FLAG_OF) {
+ ret = -EINVAL;
+ dev_warn(&spi->dev, "OF bit is set, resetting.\n");
+ m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
+
+ tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+ if (tmp < 0)
+ return tmp;
+ else if (tmp & M41T93_FLAG_OF) {
+ u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
+
+ dev_warn(&spi->dev,
+ "OF bit is still set, kickstarting clock.\n");
+ m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+ reset_osc &= ~M41T93_FLAG_ST;
+ m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+ }
+ }
+
+ if (tmp & M41T93_FLAG_BL)
+ dev_warn(&spi->dev, "BL bit is set, replace battery.\n");
+
+ /* read actual time/date */
+ tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf));
+ if (tmp < 0)
+ return tmp;
+
+ tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]);
+ tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]);
+ tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f);
+ tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]);
+ tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1;
+ tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1;
+
+ century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1;
+ tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ return ret < 0 ? ret : rtc_valid_tm(tm);
+}
+
+
+static const struct rtc_class_ops m41t93_rtc_ops = {
+ .read_time = m41t93_get_time,
+ .set_time = m41t93_set_time,
+};
+
+static struct spi_driver m41t93_driver;
+
+static int __devinit m41t93_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ int res;
+
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ res = spi_w8r8(spi, M41T93_REG_WDAY);
+ if (res < 0 || (res & 0xf8) != 0) {
+ dev_err(&spi->dev, "not found 0x%x.\n", res);
+ return -ENODEV;
+ }
+
+ rtc = rtc_device_register(m41t93_driver.driver.name,
+ &spi->dev, &m41t93_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ dev_set_drvdata(&spi->dev, rtc);
+
+ return 0;
+}
+
+
+static int __devexit m41t93_remove(struct spi_device *spi)
+{
+ struct rtc_device *rtc = platform_get_drvdata(spi);
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct spi_driver m41t93_driver = {
+ .driver = {
+ .name = "rtc-m41t93",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = m41t93_probe,
+ .remove = __devexit_p(m41t93_remove),
+};
+
+static __init int m41t93_init(void)
+{
+ return spi_register_driver(&m41t93_driver);
+}
+module_init(m41t93_init);
+
+static __exit void m41t93_exit(void)
+{
+ spi_unregister_driver(&m41t93_driver);
+}
+module_exit(m41t93_exit);
+
+MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
+MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-m41t93");
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index b2f0968..0cec565 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -380,7 +380,7 @@
cleanup0:
dev_set_drvdata(dev, NULL);
mrst_rtc.dev = NULL;
- release_region(iomem->start, iomem->end + 1 - iomem->start);
+ release_mem_region(iomem->start, resource_size(iomem));
dev_err(dev, "rtc-mrst: unable to initialise\n");
return retval;
}
@@ -406,7 +406,7 @@
mrst->rtc = NULL;
iomem = mrst->iomem;
- release_region(iomem->start, iomem->end + 1 - iomem->start);
+ release_mem_region(iomem->start, resource_size(iomem));
mrst->iomem = NULL;
mrst->dev = NULL;
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index d814417..39e41fb 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -55,12 +55,6 @@
{ MAX_PIE_FREQ, RTC_SAM7_BIT },
};
-/* Those are the bits from a classic RTC we want to mimic */
-#define RTC_IRQF 0x80 /* any of the following 3 is active */
-#define RTC_PF 0x40 /* Periodic interrupt */
-#define RTC_AF 0x20 /* Alarm interrupt */
-#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */
-
#define MXC_RTC_TIME 0
#define MXC_RTC_ALARM 1
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
index f90c574..0c42389 100644
--- a/drivers/rtc/rtc-pcf50633.c
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -58,7 +58,6 @@
struct pcf50633_rtc {
int alarm_enabled;
- int second_enabled;
int alarm_pending;
struct pcf50633 *pcf;
@@ -143,7 +142,7 @@
{
struct pcf50633_rtc *rtc;
struct pcf50633_time pcf_tm;
- int second_masked, alarm_masked, ret = 0;
+ int alarm_masked, ret = 0;
rtc = dev_get_drvdata(dev);
@@ -162,11 +161,8 @@
pcf_tm.time[PCF50633_TI_SEC]);
- second_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_SECOND);
alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
- if (!second_masked)
- pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_SECOND);
if (!alarm_masked)
pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
@@ -175,8 +171,6 @@
PCF50633_TI_EXTENT,
&pcf_tm.time[0]);
- if (!second_masked)
- pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_SECOND);
if (!alarm_masked)
pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
@@ -250,15 +244,8 @@
{
struct pcf50633_rtc *rtc = data;
- switch (irq) {
- case PCF50633_IRQ_ALARM:
- rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
- rtc->alarm_pending = 1;
- break;
- case PCF50633_IRQ_SECOND:
- rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
- break;
- }
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ rtc->alarm_pending = 1;
}
static int __devinit pcf50633_rtc_probe(struct platform_device *pdev)
@@ -282,9 +269,6 @@
pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,
pcf50633_rtc_irq, rtc);
- pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_SECOND,
- pcf50633_rtc_irq, rtc);
-
return 0;
}
@@ -295,7 +279,6 @@
rtc = platform_get_drvdata(pdev);
pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
- pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_SECOND);
rtc_device_unregister(rtc->rtc_dev);
kfree(rtc);
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
new file mode 100644
index 0000000..ea09ff2
--- /dev/null
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -0,0 +1,454 @@
+/*
+ * Micro Crystal RV-3029C2 rtc class driver
+ *
+ * Author: Gregory Hermant <gregory.hermant@calao-systems.com>
+ *
+ * based on previously existing rtc class drivers
+ *
+ * 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.
+ *
+ * NOTE: Currently this driver only supports the bare minimum for read
+ * and write the RTC and alarms. The extra features provided by this chip
+ * (trickle charger, eeprom, T° compensation) are unavailable.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
+/* Register map */
+/* control section */
+#define RV3029C2_ONOFF_CTRL 0x00
+#define RV3029C2_IRQ_CTRL 0x01
+#define RV3029C2_IRQ_CTRL_AIE (1 << 0)
+#define RV3029C2_IRQ_FLAGS 0x02
+#define RV3029C2_IRQ_FLAGS_AF (1 << 0)
+#define RV3029C2_STATUS 0x03
+#define RV3029C2_STATUS_VLOW1 (1 << 2)
+#define RV3029C2_STATUS_VLOW2 (1 << 3)
+#define RV3029C2_STATUS_SR (1 << 4)
+#define RV3029C2_STATUS_PON (1 << 5)
+#define RV3029C2_STATUS_EEBUSY (1 << 7)
+#define RV3029C2_RST_CTRL 0x04
+#define RV3029C2_CONTROL_SECTION_LEN 0x05
+
+/* watch section */
+#define RV3029C2_W_SEC 0x08
+#define RV3029C2_W_MINUTES 0x09
+#define RV3029C2_W_HOURS 0x0A
+#define RV3029C2_REG_HR_12_24 (1<<6) /* 24h/12h mode */
+#define RV3029C2_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
+#define RV3029C2_W_DATE 0x0B
+#define RV3029C2_W_DAYS 0x0C
+#define RV3029C2_W_MONTHS 0x0D
+#define RV3029C2_W_YEARS 0x0E
+#define RV3029C2_WATCH_SECTION_LEN 0x07
+
+/* alarm section */
+#define RV3029C2_A_SC 0x10
+#define RV3029C2_A_MN 0x11
+#define RV3029C2_A_HR 0x12
+#define RV3029C2_A_DT 0x13
+#define RV3029C2_A_DW 0x14
+#define RV3029C2_A_MO 0x15
+#define RV3029C2_A_YR 0x16
+#define RV3029C2_ALARM_SECTION_LEN 0x07
+
+/* timer section */
+#define RV3029C2_TIMER_LOW 0x18
+#define RV3029C2_TIMER_HIGH 0x19
+
+/* temperature section */
+#define RV3029C2_TEMP_PAGE 0x20
+
+/* eeprom data section */
+#define RV3029C2_E2P_EEDATA1 0x28
+#define RV3029C2_E2P_EEDATA2 0x29
+
+/* eeprom control section */
+#define RV3029C2_CONTROL_E2P_EECTRL 0x30
+#define RV3029C2_TRICKLE_1K (1<<0) /* 1K resistance */
+#define RV3029C2_TRICKLE_5K (1<<1) /* 5K resistance */
+#define RV3029C2_TRICKLE_20K (1<<2) /* 20K resistance */
+#define RV3029C2_TRICKLE_80K (1<<3) /* 80K resistance */
+#define RV3029C2_CONTROL_E2P_XTALOFFSET 0x31
+#define RV3029C2_CONTROL_E2P_QCOEF 0x32
+#define RV3029C2_CONTROL_E2P_TURNOVER 0x33
+
+/* user ram section */
+#define RV3029C2_USR1_RAM_PAGE 0x38
+#define RV3029C2_USR1_SECTION_LEN 0x04
+#define RV3029C2_USR2_RAM_PAGE 0x3C
+#define RV3029C2_USR2_SECTION_LEN 0x04
+
+static int
+rv3029c2_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf,
+ unsigned len)
+{
+ int ret;
+
+ if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
+ (reg + len > RV3029C2_USR1_RAM_PAGE + 8))
+ return -EINVAL;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+ if (ret < 0)
+ return ret;
+ if (ret < len)
+ return -EIO;
+ return 0;
+}
+
+static int
+rv3029c2_i2c_write_regs(struct i2c_client *client, u8 reg, u8 const buf[],
+ unsigned len)
+{
+ if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
+ (reg + len > RV3029C2_USR1_RAM_PAGE + 8))
+ return -EINVAL;
+
+ return i2c_smbus_write_i2c_block_data(client, reg, len, buf);
+}
+
+static int
+rv3029c2_i2c_get_sr(struct i2c_client *client, u8 *buf)
+{
+ int ret = rv3029c2_i2c_read_regs(client, RV3029C2_STATUS, buf, 1);
+
+ if (ret < 0)
+ return -EIO;
+ dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]);
+ return 0;
+}
+
+static int
+rv3029c2_i2c_set_sr(struct i2c_client *client, u8 val)
+{
+ u8 buf[1];
+ int sr;
+
+ buf[0] = val;
+ sr = rv3029c2_i2c_write_regs(client, RV3029C2_STATUS, buf, 1);
+ dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]);
+ if (sr < 0)
+ return -EIO;
+ return 0;
+}
+
+static int
+rv3029c2_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+{
+ u8 buf[1];
+ int ret;
+ u8 regs[RV3029C2_WATCH_SECTION_LEN] = { 0, };
+
+ ret = rv3029c2_i2c_get_sr(client, buf);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_W_SEC , regs,
+ RV3029C2_WATCH_SECTION_LEN);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading RTC section failed\n",
+ __func__);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(regs[RV3029C2_W_SEC-RV3029C2_W_SEC]);
+ tm->tm_min = bcd2bin(regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC]);
+
+ /* HR field has a more complex interpretation */
+ {
+ const u8 _hr = regs[RV3029C2_W_HOURS-RV3029C2_W_SEC];
+ if (_hr & RV3029C2_REG_HR_12_24) {
+ /* 12h format */
+ tm->tm_hour = bcd2bin(_hr & 0x1f);
+ if (_hr & RV3029C2_REG_HR_PM) /* PM flag set */
+ tm->tm_hour += 12;
+ } else /* 24h format */
+ tm->tm_hour = bcd2bin(_hr & 0x3f);
+ }
+
+ tm->tm_mday = bcd2bin(regs[RV3029C2_W_DATE-RV3029C2_W_SEC]);
+ tm->tm_mon = bcd2bin(regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC]) - 1;
+ tm->tm_year = bcd2bin(regs[RV3029C2_W_YEARS-RV3029C2_W_SEC]) + 100;
+ tm->tm_wday = bcd2bin(regs[RV3029C2_W_DAYS-RV3029C2_W_SEC]) - 1;
+
+ return 0;
+}
+
+static int rv3029c2_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return rv3029c2_i2c_read_time(to_i2c_client(dev), tm);
+}
+
+static int
+rv3029c2_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
+{
+ struct rtc_time *const tm = &alarm->time;
+ int ret;
+ u8 regs[8];
+
+ ret = rv3029c2_i2c_get_sr(client, regs);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_A_SC, regs,
+ RV3029C2_ALARM_SECTION_LEN);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading alarm section failed\n",
+ __func__);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(regs[RV3029C2_A_SC-RV3029C2_A_SC] & 0x7f);
+ tm->tm_min = bcd2bin(regs[RV3029C2_A_MN-RV3029C2_A_SC] & 0x7f);
+ tm->tm_hour = bcd2bin(regs[RV3029C2_A_HR-RV3029C2_A_SC] & 0x3f);
+ tm->tm_mday = bcd2bin(regs[RV3029C2_A_DT-RV3029C2_A_SC] & 0x3f);
+ tm->tm_mon = bcd2bin(regs[RV3029C2_A_MO-RV3029C2_A_SC] & 0x1f) - 1;
+ tm->tm_year = bcd2bin(regs[RV3029C2_A_YR-RV3029C2_A_SC] & 0x7f) + 100;
+ tm->tm_wday = bcd2bin(regs[RV3029C2_A_DW-RV3029C2_A_SC] & 0x07) - 1;
+
+ return 0;
+}
+
+static int
+rv3029c2_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ return rv3029c2_i2c_read_alarm(to_i2c_client(dev), alarm);
+}
+
+static int rv3029c2_rtc_i2c_alarm_set_irq(struct i2c_client *client,
+ int enable)
+{
+ int ret;
+ u8 buf[1];
+
+ /* enable AIE irq */
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't read INT reg\n");
+ return ret;
+ }
+ if (enable)
+ buf[0] |= RV3029C2_IRQ_CTRL_AIE;
+ else
+ buf[0] &= ~RV3029C2_IRQ_CTRL_AIE;
+
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't set INT reg\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client,
+ struct rtc_wkalrm *alarm)
+{
+ struct rtc_time *const tm = &alarm->time;
+ int ret;
+ u8 regs[8];
+
+ /*
+ * The clock has an 8 bit wide bcd-coded register (they never learn)
+ * for the year. tm_year is an offset from 1900 and we are interested
+ * in the 2000-2099 range, so any value less than 100 is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ ret = rv3029c2_i2c_get_sr(client, regs);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+ regs[RV3029C2_A_SC-RV3029C2_A_SC] = bin2bcd(tm->tm_sec & 0x7f);
+ regs[RV3029C2_A_MN-RV3029C2_A_SC] = bin2bcd(tm->tm_min & 0x7f);
+ regs[RV3029C2_A_HR-RV3029C2_A_SC] = bin2bcd(tm->tm_hour & 0x3f);
+ regs[RV3029C2_A_DT-RV3029C2_A_SC] = bin2bcd(tm->tm_mday & 0x3f);
+ regs[RV3029C2_A_MO-RV3029C2_A_SC] = bin2bcd((tm->tm_mon & 0x1f) - 1);
+ regs[RV3029C2_A_DW-RV3029C2_A_SC] = bin2bcd((tm->tm_wday & 7) - 1);
+ regs[RV3029C2_A_YR-RV3029C2_A_SC] = bin2bcd((tm->tm_year & 0x7f) - 100);
+
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_A_SC, regs,
+ RV3029C2_ALARM_SECTION_LEN);
+ if (ret < 0)
+ return ret;
+
+ if (alarm->enabled) {
+ u8 buf[1];
+
+ /* clear AF flag */
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_FLAGS,
+ buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't read alarm flag\n");
+ return ret;
+ }
+ buf[0] &= ~RV3029C2_IRQ_FLAGS_AF;
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_FLAGS,
+ buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't set alarm flag\n");
+ return ret;
+ }
+ /* enable AIE irq */
+ ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1);
+ if (ret)
+ return ret;
+
+ dev_dbg(&client->dev, "alarm IRQ armed\n");
+ } else {
+ /* disable AIE irq */
+ ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1);
+ if (ret)
+ return ret;
+
+ dev_dbg(&client->dev, "alarm IRQ disabled\n");
+ }
+
+ return 0;
+}
+
+static int rv3029c2_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ return rv3029c2_rtc_i2c_set_alarm(to_i2c_client(dev), alarm);
+}
+
+static int
+rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
+{
+ u8 regs[8];
+ int ret;
+
+ /*
+ * The clock has an 8 bit wide bcd-coded register (they never learn)
+ * for the year. tm_year is an offset from 1900 and we are interested
+ * in the 2000-2099 range, so any value less than 100 is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ regs[RV3029C2_W_SEC-RV3029C2_W_SEC] = bin2bcd(tm->tm_sec);
+ regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC] = bin2bcd(tm->tm_min);
+ regs[RV3029C2_W_HOURS-RV3029C2_W_SEC] = bin2bcd(tm->tm_hour);
+ regs[RV3029C2_W_DATE-RV3029C2_W_SEC] = bin2bcd(tm->tm_mday);
+ regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC] = bin2bcd(tm->tm_mon+1);
+ regs[RV3029C2_W_DAYS-RV3029C2_W_SEC] = bin2bcd((tm->tm_wday & 7)+1);
+ regs[RV3029C2_W_YEARS-RV3029C2_W_SEC] = bin2bcd(tm->tm_year - 100);
+
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_W_SEC, regs,
+ RV3029C2_WATCH_SECTION_LEN);
+ if (ret < 0)
+ return ret;
+
+ ret = rv3029c2_i2c_get_sr(client, regs);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return ret;
+ }
+ /* clear PON bit */
+ ret = rv3029c2_i2c_set_sr(client, (regs[0] & ~RV3029C2_STATUS_PON));
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rv3029c2_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return rv3029c2_i2c_set_time(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops rv3029c2_rtc_ops = {
+ .read_time = rv3029c2_rtc_read_time,
+ .set_time = rv3029c2_rtc_set_time,
+ .read_alarm = rv3029c2_rtc_read_alarm,
+ .set_alarm = rv3029c2_rtc_set_alarm,
+};
+
+static struct i2c_device_id rv3029c2_id[] = {
+ { "rv3029c2", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rv3029c2_id);
+
+static int __devinit
+rv3029c2_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct rtc_device *rtc;
+ int rc = 0;
+ u8 buf[1];
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL))
+ return -ENODEV;
+
+ rtc = rtc_device_register(client->name,
+ &client->dev, &rv3029c2_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ rc = rv3029c2_i2c_get_sr(client, buf);
+ if (rc < 0) {
+ dev_err(&client->dev, "reading status failed\n");
+ goto exit_unregister;
+ }
+
+ return 0;
+
+exit_unregister:
+ rtc_device_unregister(rtc);
+
+ return rc;
+}
+
+static int __devexit rv3029c2_remove(struct i2c_client *client)
+{
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct i2c_driver rv3029c2_driver = {
+ .driver = {
+ .name = "rtc-rv3029c2",
+ },
+ .probe = rv3029c2_probe,
+ .remove = __devexit_p(rv3029c2_remove),
+ .id_table = rv3029c2_id,
+};
+
+static int __init rv3029c2_init(void)
+{
+ return i2c_add_driver(&rv3029c2_driver);
+}
+
+static void __exit rv3029c2_exit(void)
+{
+ i2c_del_driver(&rv3029c2_driver);
+}
+
+module_init(rv3029c2_init);
+module_exit(rv3029c2_exit);
+
+MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>");
+MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
new file mode 100644
index 0000000..893bac2
--- /dev/null
+++ b/drivers/rtc/rtc-spear.c
@@ -0,0 +1,534 @@
+/*
+ * drivers/rtc/rtc-spear.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar<rajeev-dlh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* RTC registers */
+#define TIME_REG 0x00
+#define DATE_REG 0x04
+#define ALARM_TIME_REG 0x08
+#define ALARM_DATE_REG 0x0C
+#define CTRL_REG 0x10
+#define STATUS_REG 0x14
+
+/* TIME_REG & ALARM_TIME_REG */
+#define SECONDS_UNITS (0xf<<0) /* seconds units position */
+#define SECONDS_TENS (0x7<<4) /* seconds tens position */
+#define MINUTES_UNITS (0xf<<8) /* minutes units position */
+#define MINUTES_TENS (0x7<<12) /* minutes tens position */
+#define HOURS_UNITS (0xf<<16) /* hours units position */
+#define HOURS_TENS (0x3<<20) /* hours tens position */
+
+/* DATE_REG & ALARM_DATE_REG */
+#define DAYS_UNITS (0xf<<0) /* days units position */
+#define DAYS_TENS (0x3<<4) /* days tens position */
+#define MONTHS_UNITS (0xf<<8) /* months units position */
+#define MONTHS_TENS (0x1<<12) /* months tens position */
+#define YEARS_UNITS (0xf<<16) /* years units position */
+#define YEARS_TENS (0xf<<20) /* years tens position */
+#define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */
+#define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */
+
+/* MASK SHIFT TIME_REG & ALARM_TIME_REG*/
+#define SECOND_SHIFT 0x00 /* seconds units */
+#define MINUTE_SHIFT 0x08 /* minutes units position */
+#define HOUR_SHIFT 0x10 /* hours units position */
+#define MDAY_SHIFT 0x00 /* Month day shift */
+#define MONTH_SHIFT 0x08 /* Month shift */
+#define YEAR_SHIFT 0x10 /* Year shift */
+
+#define SECOND_MASK 0x7F
+#define MIN_MASK 0x7F
+#define HOUR_MASK 0x3F
+#define DAY_MASK 0x3F
+#define MONTH_MASK 0x7F
+#define YEAR_MASK 0xFFFF
+
+/* date reg equal to time reg, for debug only */
+#define TIME_BYP (1<<9)
+#define INT_ENABLE (1<<31) /* interrupt enable */
+
+/* STATUS_REG */
+#define CLK_UNCONNECTED (1<<0)
+#define PEND_WR_TIME (1<<2)
+#define PEND_WR_DATE (1<<3)
+#define LOST_WR_TIME (1<<4)
+#define LOST_WR_DATE (1<<5)
+#define RTC_INT_MASK (1<<31)
+#define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE)
+#define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE)
+
+struct spear_rtc_config {
+ struct clk *clk;
+ spinlock_t lock;
+ void __iomem *ioaddr;
+};
+
+static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&config->lock, flags);
+ val = readl(config->ioaddr + STATUS_REG);
+ val |= RTC_INT_MASK;
+ writel(val, config->ioaddr + STATUS_REG);
+ spin_unlock_irqrestore(&config->lock, flags);
+}
+
+static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config)
+{
+ unsigned int val;
+
+ val = readl(config->ioaddr + CTRL_REG);
+ if (!(val & INT_ENABLE)) {
+ spear_rtc_clear_interrupt(config);
+ val |= INT_ENABLE;
+ writel(val, config->ioaddr + CTRL_REG);
+ }
+}
+
+static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config)
+{
+ unsigned int val;
+
+ val = readl(config->ioaddr + CTRL_REG);
+ if (val & INT_ENABLE) {
+ val &= ~INT_ENABLE;
+ writel(val, config->ioaddr + CTRL_REG);
+ }
+}
+
+static inline int is_write_complete(struct spear_rtc_config *config)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&config->lock, flags);
+ if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL)
+ ret = -EIO;
+ spin_unlock_irqrestore(&config->lock, flags);
+
+ return ret;
+}
+
+static void rtc_wait_not_busy(struct spear_rtc_config *config)
+{
+ int status, count = 0;
+ unsigned long flags;
+
+ /* Assuming BUSY may stay active for 80 msec) */
+ for (count = 0; count < 80; count++) {
+ spin_lock_irqsave(&config->lock, flags);
+ status = readl(config->ioaddr + STATUS_REG);
+ spin_unlock_irqrestore(&config->lock, flags);
+ if ((status & STATUS_BUSY) == 0)
+ break;
+ /* check status busy, after each msec */
+ msleep(1);
+ }
+}
+
+static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
+{
+ struct rtc_device *rtc = (struct rtc_device *)dev_id;
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ unsigned long flags, events = 0;
+ unsigned int irq_data;
+
+ spin_lock_irqsave(&config->lock, flags);
+ irq_data = readl(config->ioaddr + STATUS_REG);
+ spin_unlock_irqrestore(&config->lock, flags);
+
+ if ((irq_data & RTC_INT_MASK)) {
+ spear_rtc_clear_interrupt(config);
+ events = RTC_IRQF | RTC_AF;
+ rtc_update_irq(rtc, 1, events);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+
+}
+
+static int tm2bcd(struct rtc_time *tm)
+{
+ if (rtc_valid_tm(tm) != 0)
+ return -EINVAL;
+ tm->tm_sec = bin2bcd(tm->tm_sec);
+ tm->tm_min = bin2bcd(tm->tm_min);
+ tm->tm_hour = bin2bcd(tm->tm_hour);
+ tm->tm_mday = bin2bcd(tm->tm_mday);
+ tm->tm_mon = bin2bcd(tm->tm_mon + 1);
+ tm->tm_year = bin2bcd(tm->tm_year);
+
+ return 0;
+}
+
+static void bcd2tm(struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(tm->tm_sec);
+ tm->tm_min = bcd2bin(tm->tm_min);
+ tm->tm_hour = bcd2bin(tm->tm_hour);
+ tm->tm_mday = bcd2bin(tm->tm_mday);
+ tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
+ /* epoch == 1900 */
+ tm->tm_year = bcd2bin(tm->tm_year);
+}
+
+/*
+ * spear_rtc_read_time - set the time
+ * @dev: rtc device in use
+ * @tm: holds date and time
+ *
+ * This function read time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ unsigned int time, date;
+
+ /* we don't report wday/yday/isdst ... */
+ rtc_wait_not_busy(config);
+
+ time = readl(config->ioaddr + TIME_REG);
+ date = readl(config->ioaddr + DATE_REG);
+ tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
+ tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
+ tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
+ tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
+ tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
+ tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
+
+ bcd2tm(tm);
+ return 0;
+}
+
+/*
+ * spear_rtc_set_time - set the time
+ * @dev: rtc device in use
+ * @tm: holds date and time
+ *
+ * This function set time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ unsigned int time, date, err = 0;
+
+ if (tm2bcd(tm) < 0)
+ return -EINVAL;
+
+ rtc_wait_not_busy(config);
+ time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) |
+ (tm->tm_hour << HOUR_SHIFT);
+ date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) |
+ (tm->tm_year << YEAR_SHIFT);
+ writel(time, config->ioaddr + TIME_REG);
+ writel(date, config->ioaddr + DATE_REG);
+ err = is_write_complete(config);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * spear_rtc_read_alarm - read the alarm time
+ * @dev: rtc device in use
+ * @alm: holds alarm date and time
+ *
+ * This function read alarm time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ unsigned int time, date;
+
+ rtc_wait_not_busy(config);
+
+ time = readl(config->ioaddr + ALARM_TIME_REG);
+ date = readl(config->ioaddr + ALARM_DATE_REG);
+ alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
+ alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
+ alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
+ alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
+ alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
+ alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
+
+ bcd2tm(&alm->time);
+ alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE;
+
+ return 0;
+}
+
+/*
+ * spear_rtc_set_alarm - set the alarm time
+ * @dev: rtc device in use
+ * @alm: holds alarm date and time
+ *
+ * This function set alarm time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ unsigned int time, date, err = 0;
+
+ if (tm2bcd(&alm->time) < 0)
+ return -EINVAL;
+
+ rtc_wait_not_busy(config);
+
+ time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min <<
+ MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT);
+ date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon <<
+ MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT);
+
+ writel(time, config->ioaddr + ALARM_TIME_REG);
+ writel(date, config->ioaddr + ALARM_DATE_REG);
+ err = is_write_complete(config);
+ if (err < 0)
+ return err;
+
+ if (alm->enabled)
+ spear_rtc_enable_interrupt(config);
+ else
+ spear_rtc_disable_interrupt(config);
+
+ return 0;
+}
+static struct rtc_class_ops spear_rtc_ops = {
+ .read_time = spear_rtc_read_time,
+ .set_time = spear_rtc_set_time,
+ .read_alarm = spear_rtc_read_alarm,
+ .set_alarm = spear_rtc_set_alarm,
+};
+
+static int __devinit spear_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct rtc_device *rtc;
+ struct spear_rtc_config *config;
+ unsigned int status = 0;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ return -EBUSY;
+ }
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "rtc region already claimed\n");
+ return -EBUSY;
+ }
+
+ config = kzalloc(sizeof(*config), GFP_KERNEL);
+ if (!config) {
+ dev_err(&pdev->dev, "out of memory\n");
+ status = -ENOMEM;
+ goto err_release_region;
+ }
+
+ config->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(config->clk)) {
+ status = PTR_ERR(config->clk);
+ goto err_kfree;
+ }
+
+ status = clk_enable(config->clk);
+ if (status < 0)
+ goto err_clk_put;
+
+ config->ioaddr = ioremap(res->start, resource_size(res));
+ if (!config->ioaddr) {
+ dev_err(&pdev->dev, "ioremap fail\n");
+ status = -ENOMEM;
+ goto err_disable_clock;
+ }
+
+ spin_lock_init(&config->lock);
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+ PTR_ERR(rtc));
+ status = PTR_ERR(rtc);
+ goto err_iounmap;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+ dev_set_drvdata(&rtc->dev, config);
+
+ /* alarm irqs */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no update irq?\n");
+ status = irq;
+ goto err_clear_platdata;
+ }
+
+ status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc);
+ if (status) {
+ dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \
+ claimed\n", irq);
+ goto err_clear_platdata;
+ }
+
+ if (!device_can_wakeup(&pdev->dev))
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+err_clear_platdata:
+ platform_set_drvdata(pdev, NULL);
+ dev_set_drvdata(&rtc->dev, NULL);
+ rtc_device_unregister(rtc);
+err_iounmap:
+ iounmap(config->ioaddr);
+err_disable_clock:
+ clk_disable(config->clk);
+err_clk_put:
+ clk_put(config->clk);
+err_kfree:
+ kfree(config);
+err_release_region:
+ release_mem_region(res->start, resource_size(res));
+
+ return status;
+}
+
+static int __devexit spear_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ int irq;
+ struct resource *res;
+
+ /* leave rtc running, but disable irqs */
+ spear_rtc_disable_interrupt(config);
+ device_init_wakeup(&pdev->dev, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (irq)
+ free_irq(irq, pdev);
+ clk_disable(config->clk);
+ clk_put(config->clk);
+ iounmap(config->ioaddr);
+ kfree(config);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+ dev_set_drvdata(&rtc->dev, NULL);
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(irq);
+ else {
+ spear_rtc_disable_interrupt(config);
+ clk_disable(config->clk);
+ }
+
+ return 0;
+}
+
+static int spear_rtc_resume(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(irq);
+ else {
+ clk_enable(config->clk);
+ spear_rtc_enable_interrupt(config);
+ }
+
+ return 0;
+}
+
+#else
+#define spear_rtc_suspend NULL
+#define spear_rtc_resume NULL
+#endif
+
+static void spear_rtc_shutdown(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+
+ spear_rtc_disable_interrupt(config);
+ clk_disable(config->clk);
+}
+
+static struct platform_driver spear_rtc_driver = {
+ .probe = spear_rtc_probe,
+ .remove = __devexit_p(spear_rtc_remove),
+ .suspend = spear_rtc_suspend,
+ .resume = spear_rtc_resume,
+ .shutdown = spear_rtc_shutdown,
+ .driver = {
+ .name = "rtc-spear",
+ },
+};
+
+static int __init rtc_init(void)
+{
+ return platform_driver_register(&spear_rtc_driver);
+}
+module_init(rtc_init);
+
+static void __exit rtc_exit(void)
+{
+ platform_driver_unregister(&spear_rtc_driver);
+}
+module_exit(rtc_exit);
+
+MODULE_ALIAS("platform:rtc-spear");
+MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
+MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
new file mode 100644
index 0000000..b8bc862
--- /dev/null
+++ b/drivers/rtc/rtc-vt8500.c
@@ -0,0 +1,366 @@
+/*
+ * drivers/rtc/rtc-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on rtc-pxa.c
+ *
+ * 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/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/*
+ * Register definitions
+ */
+#define VT8500_RTC_TS 0x00 /* Time set */
+#define VT8500_RTC_DS 0x04 /* Date set */
+#define VT8500_RTC_AS 0x08 /* Alarm set */
+#define VT8500_RTC_CR 0x0c /* Control */
+#define VT8500_RTC_TR 0x10 /* Time read */
+#define VT8500_RTC_DR 0x14 /* Date read */
+#define VT8500_RTC_WS 0x18 /* Write status */
+#define VT8500_RTC_CL 0x20 /* Calibration */
+#define VT8500_RTC_IS 0x24 /* Interrupt status */
+#define VT8500_RTC_ST 0x28 /* Status */
+
+#define INVALID_TIME_BIT (1 << 31)
+
+#define DATE_CENTURY_S 19
+#define DATE_YEAR_S 11
+#define DATE_YEAR_MASK (0xff << DATE_YEAR_S)
+#define DATE_MONTH_S 6
+#define DATE_MONTH_MASK (0x1f << DATE_MONTH_S)
+#define DATE_DAY_MASK 0x3f
+
+#define TIME_DOW_S 20
+#define TIME_DOW_MASK (0x07 << TIME_DOW_S)
+#define TIME_HOUR_S 14
+#define TIME_HOUR_MASK (0x3f << TIME_HOUR_S)
+#define TIME_MIN_S 7
+#define TIME_MIN_MASK (0x7f << TIME_MIN_S)
+#define TIME_SEC_MASK 0x7f
+
+#define ALARM_DAY_S 20
+#define ALARM_DAY_MASK (0x3f << ALARM_DAY_S)
+
+#define ALARM_DAY_BIT (1 << 29)
+#define ALARM_HOUR_BIT (1 << 28)
+#define ALARM_MIN_BIT (1 << 27)
+#define ALARM_SEC_BIT (1 << 26)
+
+#define ALARM_ENABLE_MASK (ALARM_DAY_BIT \
+ | ALARM_HOUR_BIT \
+ | ALARM_MIN_BIT \
+ | ALARM_SEC_BIT)
+
+#define VT8500_RTC_CR_ENABLE (1 << 0) /* Enable RTC */
+#define VT8500_RTC_CR_24H (1 << 1) /* 24h time format */
+#define VT8500_RTC_CR_SM_ENABLE (1 << 2) /* Enable periodic irqs */
+#define VT8500_RTC_CR_SM_SEC (1 << 3) /* 0: 1Hz/60, 1: 1Hz */
+#define VT8500_RTC_CR_CALIB (1 << 4) /* Enable calibration */
+
+struct vt8500_rtc {
+ void __iomem *regbase;
+ struct resource *res;
+ int irq_alarm;
+ int irq_hz;
+ struct rtc_device *rtc;
+ spinlock_t lock; /* Protects this structure */
+};
+
+static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_id;
+ u32 isr;
+ unsigned long events = 0;
+
+ spin_lock(&vt8500_rtc->lock);
+
+ /* clear interrupt sources */
+ isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+ writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+ spin_unlock(&vt8500_rtc->lock);
+
+ if (isr & 1)
+ events |= RTC_AF | RTC_IRQF;
+
+ /* Only second/minute interrupts are supported */
+ if (isr & 2)
+ events |= RTC_UF | RTC_IRQF;
+
+ rtc_update_irq(vt8500_rtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ u32 date, time;
+
+ date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
+ time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
+
+ tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
+ tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
+ tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
+ tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
+ tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S);
+ tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S)
+ + ((date >> DATE_CENTURY_S) & 1 ? 200 : 100);
+ tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
+
+ return 0;
+}
+
+static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+ if (tm->tm_year < 100) {
+ dev_warn(dev, "Only years 2000-2199 are supported by the "
+ "hardware!\n");
+ return -EINVAL;
+ }
+
+ writel((bin2bcd(tm->tm_year - 100) << DATE_YEAR_S)
+ | (bin2bcd(tm->tm_mon) << DATE_MONTH_S)
+ | (bin2bcd(tm->tm_mday)),
+ vt8500_rtc->regbase + VT8500_RTC_DS);
+ writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
+ | (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
+ | (bin2bcd(tm->tm_min) << TIME_MIN_S)
+ | (bin2bcd(tm->tm_sec)),
+ vt8500_rtc->regbase + VT8500_RTC_TS);
+
+ return 0;
+}
+
+static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ u32 isr, alarm;
+
+ alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+ isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+
+ alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
+ alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
+ alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
+ alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
+
+ alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
+
+ alrm->pending = (isr & 1) ? 1 : 0;
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+ writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
+ | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
+ | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
+ | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
+ | (bin2bcd(alrm->time.tm_sec)),
+ vt8500_rtc->regbase + VT8500_RTC_AS);
+
+ return 0;
+}
+
+static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+
+ if (enabled)
+ tmp |= ALARM_ENABLE_MASK;
+ else
+ tmp &= ~ALARM_ENABLE_MASK;
+
+ writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS);
+ return 0;
+}
+
+static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR);
+
+ if (enabled)
+ tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE;
+ else
+ tmp &= ~VT8500_RTC_CR_SM_ENABLE;
+
+ writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR);
+ return 0;
+}
+
+static const struct rtc_class_ops vt8500_rtc_ops = {
+ .read_time = vt8500_rtc_read_time,
+ .set_time = vt8500_rtc_set_time,
+ .read_alarm = vt8500_rtc_read_alarm,
+ .set_alarm = vt8500_rtc_set_alarm,
+ .alarm_irq_enable = vt8500_alarm_irq_enable,
+ .update_irq_enable = vt8500_update_irq_enable,
+};
+
+static int __devinit vt8500_rtc_probe(struct platform_device *pdev)
+{
+ struct vt8500_rtc *vt8500_rtc;
+ int ret;
+
+ vt8500_rtc = kzalloc(sizeof(struct vt8500_rtc), GFP_KERNEL);
+ if (!vt8500_rtc)
+ return -ENOMEM;
+
+ spin_lock_init(&vt8500_rtc->lock);
+ platform_set_drvdata(pdev, vt8500_rtc);
+
+ vt8500_rtc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!vt8500_rtc->res) {
+ dev_err(&pdev->dev, "No I/O memory resource defined\n");
+ ret = -ENXIO;
+ goto err_free;
+ }
+
+ vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
+ if (vt8500_rtc->irq_alarm < 0) {
+ dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
+ ret = -ENXIO;
+ goto err_free;
+ }
+
+ vt8500_rtc->irq_hz = platform_get_irq(pdev, 1);
+ if (vt8500_rtc->irq_hz < 0) {
+ dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n");
+ ret = -ENXIO;
+ goto err_free;
+ }
+
+ vt8500_rtc->res = request_mem_region(vt8500_rtc->res->start,
+ resource_size(vt8500_rtc->res),
+ "vt8500-rtc");
+ if (vt8500_rtc->res == NULL) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ ret = -EBUSY;
+ goto err_free;
+ }
+
+ vt8500_rtc->regbase = ioremap(vt8500_rtc->res->start,
+ resource_size(vt8500_rtc->res));
+ if (!vt8500_rtc->regbase) {
+ dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
+ ret = -EBUSY;
+ goto err_release;
+ }
+
+ /* Enable the second/minute interrupt generation and enable RTC */
+ writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H
+ | VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC,
+ vt8500_rtc->regbase + VT8500_RTC_CR);
+
+ vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev,
+ &vt8500_rtc_ops, THIS_MODULE);
+ if (IS_ERR(vt8500_rtc->rtc)) {
+ ret = PTR_ERR(vt8500_rtc->rtc);
+ dev_err(&pdev->dev,
+ "Failed to register RTC device -> %d\n", ret);
+ goto err_unmap;
+ }
+
+ ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0,
+ "rtc 1Hz", vt8500_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+ vt8500_rtc->irq_hz, ret);
+ goto err_unreg;
+ }
+
+ ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,
+ "rtc alarm", vt8500_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+ vt8500_rtc->irq_alarm, ret);
+ goto err_free_hz;
+ }
+
+ return 0;
+
+err_free_hz:
+ free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+err_unreg:
+ rtc_device_unregister(vt8500_rtc->rtc);
+err_unmap:
+ iounmap(vt8500_rtc->regbase);
+err_release:
+ release_mem_region(vt8500_rtc->res->start,
+ resource_size(vt8500_rtc->res));
+err_free:
+ kfree(vt8500_rtc);
+ return ret;
+}
+
+static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
+{
+ struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+
+ free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
+ free_irq(vt8500_rtc->irq_hz, vt8500_rtc);
+
+ rtc_device_unregister(vt8500_rtc->rtc);
+
+ /* Disable alarm matching */
+ writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
+ iounmap(vt8500_rtc->regbase);
+ release_mem_region(vt8500_rtc->res->start,
+ resource_size(vt8500_rtc->res));
+
+ kfree(vt8500_rtc);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver vt8500_rtc_driver = {
+ .probe = vt8500_rtc_probe,
+ .remove = __devexit_p(vt8500_rtc_remove),
+ .driver = {
+ .name = "vt8500-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init vt8500_rtc_init(void)
+{
+ return platform_driver_register(&vt8500_rtc_driver);
+}
+module_init(vt8500_rtc_init);
+
+static void __exit vt8500_rtc_exit(void)
+{
+ platform_driver_unregister(&vt8500_rtc_driver);
+}
+module_exit(vt8500_rtc_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vt8500-rtc");
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 4ff2652..3382475 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -59,7 +59,6 @@
#ifndef AAC_DRIVER_BRANCH
#define AAC_DRIVER_BRANCH ""
#endif
-#define AAC_DRIVER_BUILD_DATE __DATE__ " " __TIME__
#define AAC_DRIVERNAME "aacraid"
#ifdef AAC_DRIVER_BUILD
@@ -67,7 +66,7 @@
#define str(x) _str(x)
#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION "[" str(AAC_DRIVER_BUILD) "]" AAC_DRIVER_BRANCH
#else
-#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION AAC_DRIVER_BRANCH " " AAC_DRIVER_BUILD_DATE
+#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION AAC_DRIVER_BRANCH
#endif
MODULE_AUTHOR("Red Hat Inc and Adaptec");
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 92109b1..112f1be 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -2227,7 +2227,7 @@
bp = buf;
*bp = '\0';
if (hd->proc & PR_VERSION) {
- sprintf(tbuf, "\nVersion %s - %s. Compiled %s %s", IN2000_VERSION, IN2000_DATE, __DATE__, __TIME__);
+ sprintf(tbuf, "\nVersion %s - %s.", IN2000_VERSION, IN2000_DATE);
strcat(bp, tbuf);
}
if (hd->proc & PR_INFO) {
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index 7f636b1..fca6a89 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -4252,8 +4252,8 @@
char *buf
)
{
- return snprintf(buf, PAGE_SIZE, "version: %s, build date: %s\n",
- PMCRAID_DRIVER_VERSION, PMCRAID_DRIVER_DATE);
+ return snprintf(buf, PAGE_SIZE, "version: %s\n",
+ PMCRAID_DRIVER_VERSION);
}
static struct device_attribute pmcraid_driver_version_attr = {
@@ -6096,9 +6096,8 @@
dev_t dev;
int error;
- pmcraid_info("%s Device Driver version: %s %s\n",
- PMCRAID_DRIVER_NAME,
- PMCRAID_DRIVER_VERSION, PMCRAID_DRIVER_DATE);
+ pmcraid_info("%s Device Driver version: %s\n",
+ PMCRAID_DRIVER_NAME, PMCRAID_DRIVER_VERSION);
error = alloc_chrdev_region(&dev, 0,
PMCRAID_MAX_ADAPTERS,
diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h
index 34e4c91..f920baf 100644
--- a/drivers/scsi/pmcraid.h
+++ b/drivers/scsi/pmcraid.h
@@ -43,7 +43,6 @@
#define PMCRAID_DRIVER_NAME "PMC MaxRAID"
#define PMCRAID_DEVFILE "pmcsas"
#define PMCRAID_DRIVER_VERSION "1.0.3"
-#define PMCRAID_DRIVER_DATE __DATE__
#define PMCRAID_FW_VERSION_1 0x002
diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c
index 97ae716..c0ee4ea 100644
--- a/drivers/scsi/wd33c93.c
+++ b/drivers/scsi/wd33c93.c
@@ -2051,8 +2051,7 @@
for (i = 0; i < MAX_SETUP_ARGS; i++)
printk("%s,", setup_args[i]);
printk("\n");
- printk(" Version %s - %s, Compiled %s at %s\n",
- WD33C93_VERSION, WD33C93_DATE, __DATE__, __TIME__);
+ printk(" Version %s - %s\n", WD33C93_VERSION, WD33C93_DATE);
}
int
@@ -2132,8 +2131,8 @@
bp = buf;
*bp = '\0';
if (hd->proc & PR_VERSION) {
- sprintf(tbuf, "\nVersion %s - %s. Compiled %s %s",
- WD33C93_VERSION, WD33C93_DATE, __DATE__, __TIME__);
+ sprintf(tbuf, "\nVersion %s - %s.",
+ WD33C93_VERSION, WD33C93_DATE);
strcat(bp, tbuf);
}
if (hd->proc & PR_INFO) {
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fc14b8d..fbd96b2 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -271,8 +271,8 @@
This enables using the SPI master controller on the Orion chips.
config SPI_PL022
- tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)"
- depends on ARM_AMBA && EXPERIMENTAL
+ tristate "ARM AMBA PL022 SSP controller"
+ depends on ARM_AMBA
default y if MACH_U300
default y if ARCH_REALVIEW
default y if INTEGRATOR_IMPD1
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index 08de58e..6a9e58d 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -24,11 +24,6 @@
* GNU General Public License for more details.
*/
-/*
- * TODO:
- * - add timeout on polled transfers
- */
-
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -287,6 +282,8 @@
#define CLEAR_ALL_INTERRUPTS 0x3
+#define SPI_POLLING_TIMEOUT 1000
+
/*
* The type of reading going on on this chip
@@ -1063,7 +1060,7 @@
pl022->master_info->dma_filter,
pl022->master_info->dma_rx_param);
if (!pl022->dma_rx_channel) {
- dev_err(&pl022->adev->dev, "no RX DMA channel!\n");
+ dev_dbg(&pl022->adev->dev, "no RX DMA channel!\n");
goto err_no_rxchan;
}
@@ -1071,13 +1068,13 @@
pl022->master_info->dma_filter,
pl022->master_info->dma_tx_param);
if (!pl022->dma_tx_channel) {
- dev_err(&pl022->adev->dev, "no TX DMA channel!\n");
+ dev_dbg(&pl022->adev->dev, "no TX DMA channel!\n");
goto err_no_txchan;
}
pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!pl022->dummypage) {
- dev_err(&pl022->adev->dev, "no DMA dummypage!\n");
+ dev_dbg(&pl022->adev->dev, "no DMA dummypage!\n");
goto err_no_dummypage;
}
@@ -1093,6 +1090,8 @@
dma_release_channel(pl022->dma_rx_channel);
pl022->dma_rx_channel = NULL;
err_no_rxchan:
+ dev_err(&pl022->adev->dev,
+ "Failed to work in dma mode, work without dma!\n");
return -ENODEV;
}
@@ -1378,6 +1377,7 @@
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
struct chip_data *chip;
+ unsigned long time, timeout;
chip = pl022->cur_chip;
message = pl022->cur_msg;
@@ -1415,9 +1415,19 @@
SSP_CR1(pl022->virtbase));
dev_dbg(&pl022->adev->dev, "polling transfer ongoing ...\n");
- /* FIXME: insert a timeout so we don't hang here indefinitely */
- while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end)
+
+ timeout = jiffies + msecs_to_jiffies(SPI_POLLING_TIMEOUT);
+ while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end) {
+ time = jiffies;
readwriter(pl022);
+ if (time_after(time, timeout)) {
+ dev_warn(&pl022->adev->dev,
+ "%s: timeout!\n", __func__);
+ message->state = STATE_ERROR;
+ goto out;
+ }
+ cpu_relax();
+ }
/* Update total byte transferred */
message->actual_length += pl022->cur_transfer->len;
@@ -1426,7 +1436,7 @@
/* Move to next transfer */
message->state = next_transfer(pl022);
}
-
+out:
/* Handle end of message */
if (message->state == STATE_DONE)
message->status = 0;
@@ -2107,7 +2117,7 @@
if (platform_info->enable_dma) {
status = pl022_dma_probe(pl022);
if (status != 0)
- goto err_no_dma;
+ platform_info->enable_dma = 0;
}
/* Initialize and start queue */
@@ -2143,7 +2153,6 @@
err_init_queue:
destroy_queue(pl022);
pl022_dma_remove(pl022);
- err_no_dma:
free_irq(adev->irq[0], pl022);
err_no_irq:
clk_put(pl022->clk);
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c
index 871e337..919fa9d 100644
--- a/drivers/spi/dw_spi.c
+++ b/drivers/spi/dw_spi.c
@@ -58,8 +58,6 @@
u8 bits_per_word;
u16 clk_div; /* baud rate divider */
u32 speed_hz; /* baud rate */
- int (*write)(struct dw_spi *dws);
- int (*read)(struct dw_spi *dws);
void (*cs_control)(u32 command);
};
@@ -162,107 +160,70 @@
}
#endif /* CONFIG_DEBUG_FS */
-static void wait_till_not_busy(struct dw_spi *dws)
+/* Return the max entries we can fill into tx fifo */
+static inline u32 tx_max(struct dw_spi *dws)
{
- unsigned long end = jiffies + 1 + usecs_to_jiffies(5000);
+ u32 tx_left, tx_room, rxtx_gap;
- while (time_before(jiffies, end)) {
- if (!(dw_readw(dws, sr) & SR_BUSY))
- return;
- cpu_relax();
+ tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
+ tx_room = dws->fifo_len - dw_readw(dws, txflr);
+
+ /*
+ * Another concern is about the tx/rx mismatch, we
+ * though to use (dws->fifo_len - rxflr - txflr) as
+ * one maximum value for tx, but it doesn't cover the
+ * data which is out of tx/rx fifo and inside the
+ * shift registers. So a control from sw point of
+ * view is taken.
+ */
+ rxtx_gap = ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx))
+ / dws->n_bytes;
+
+ return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap));
+}
+
+/* Return the max entries we should read out of rx fifo */
+static inline u32 rx_max(struct dw_spi *dws)
+{
+ u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
+
+ return min(rx_left, (u32)dw_readw(dws, rxflr));
+}
+
+static void dw_writer(struct dw_spi *dws)
+{
+ u32 max = tx_max(dws);
+ u16 txw = 0;
+
+ while (max--) {
+ /* Set the tx word if the transfer's original "tx" is not null */
+ if (dws->tx_end - dws->len) {
+ if (dws->n_bytes == 1)
+ txw = *(u8 *)(dws->tx);
+ else
+ txw = *(u16 *)(dws->tx);
+ }
+ dw_writew(dws, dr, txw);
+ dws->tx += dws->n_bytes;
}
- dev_err(&dws->master->dev,
- "DW SPI: Status keeps busy for 5000us after a read/write!\n");
}
-static void flush(struct dw_spi *dws)
+static void dw_reader(struct dw_spi *dws)
{
- while (dw_readw(dws, sr) & SR_RF_NOT_EMPT) {
- dw_readw(dws, dr);
- cpu_relax();
+ u32 max = rx_max(dws);
+ u16 rxw;
+
+ while (max--) {
+ rxw = dw_readw(dws, dr);
+ /* Care rx only if the transfer's original "rx" is not null */
+ if (dws->rx_end - dws->len) {
+ if (dws->n_bytes == 1)
+ *(u8 *)(dws->rx) = rxw;
+ else
+ *(u16 *)(dws->rx) = rxw;
+ }
+ dws->rx += dws->n_bytes;
}
-
- wait_till_not_busy(dws);
-}
-
-static int null_writer(struct dw_spi *dws)
-{
- u8 n_bytes = dws->n_bytes;
-
- if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
- || (dws->tx == dws->tx_end))
- return 0;
- dw_writew(dws, dr, 0);
- dws->tx += n_bytes;
-
- wait_till_not_busy(dws);
- return 1;
-}
-
-static int null_reader(struct dw_spi *dws)
-{
- u8 n_bytes = dws->n_bytes;
-
- while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
- && (dws->rx < dws->rx_end)) {
- dw_readw(dws, dr);
- dws->rx += n_bytes;
- }
- wait_till_not_busy(dws);
- return dws->rx == dws->rx_end;
-}
-
-static int u8_writer(struct dw_spi *dws)
-{
- if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
- || (dws->tx == dws->tx_end))
- return 0;
-
- dw_writew(dws, dr, *(u8 *)(dws->tx));
- ++dws->tx;
-
- wait_till_not_busy(dws);
- return 1;
-}
-
-static int u8_reader(struct dw_spi *dws)
-{
- while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
- && (dws->rx < dws->rx_end)) {
- *(u8 *)(dws->rx) = dw_readw(dws, dr);
- ++dws->rx;
- }
-
- wait_till_not_busy(dws);
- return dws->rx == dws->rx_end;
-}
-
-static int u16_writer(struct dw_spi *dws)
-{
- if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
- || (dws->tx == dws->tx_end))
- return 0;
-
- dw_writew(dws, dr, *(u16 *)(dws->tx));
- dws->tx += 2;
-
- wait_till_not_busy(dws);
- return 1;
-}
-
-static int u16_reader(struct dw_spi *dws)
-{
- u16 temp;
-
- while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
- && (dws->rx < dws->rx_end)) {
- temp = dw_readw(dws, dr);
- *(u16 *)(dws->rx) = temp;
- dws->rx += 2;
- }
-
- wait_till_not_busy(dws);
- return dws->rx == dws->rx_end;
}
static void *next_transfer(struct dw_spi *dws)
@@ -334,8 +295,7 @@
static void int_error_stop(struct dw_spi *dws, const char *msg)
{
- /* Stop and reset hw */
- flush(dws);
+ /* Stop the hw */
spi_enable_chip(dws, 0);
dev_err(&dws->master->dev, "%s\n", msg);
@@ -362,35 +322,28 @@
static irqreturn_t interrupt_transfer(struct dw_spi *dws)
{
- u16 irq_status, irq_mask = 0x3f;
- u32 int_level = dws->fifo_len / 2;
- u32 left;
+ u16 irq_status = dw_readw(dws, isr);
- irq_status = dw_readw(dws, isr) & irq_mask;
/* Error handling */
if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
dw_readw(dws, txoicr);
dw_readw(dws, rxoicr);
dw_readw(dws, rxuicr);
- int_error_stop(dws, "interrupt_transfer: fifo overrun");
+ int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
return IRQ_HANDLED;
}
+ dw_reader(dws);
+ if (dws->rx_end == dws->rx) {
+ spi_mask_intr(dws, SPI_INT_TXEI);
+ dw_spi_xfer_done(dws);
+ return IRQ_HANDLED;
+ }
if (irq_status & SPI_INT_TXEI) {
spi_mask_intr(dws, SPI_INT_TXEI);
-
- left = (dws->tx_end - dws->tx) / dws->n_bytes;
- left = (left > int_level) ? int_level : left;
-
- while (left--)
- dws->write(dws);
- dws->read(dws);
-
- /* Re-enable the IRQ if there is still data left to tx */
- if (dws->tx_end > dws->tx)
- spi_umask_intr(dws, SPI_INT_TXEI);
- else
- dw_spi_xfer_done(dws);
+ dw_writer(dws);
+ /* Enable TX irq always, it will be disabled when RX finished */
+ spi_umask_intr(dws, SPI_INT_TXEI);
}
return IRQ_HANDLED;
@@ -399,15 +352,13 @@
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
struct dw_spi *dws = dev_id;
- u16 irq_status, irq_mask = 0x3f;
+ u16 irq_status = dw_readw(dws, isr) & 0x3f;
- irq_status = dw_readw(dws, isr) & irq_mask;
if (!irq_status)
return IRQ_NONE;
if (!dws->cur_msg) {
spi_mask_intr(dws, SPI_INT_TXEI);
- /* Never fail */
return IRQ_HANDLED;
}
@@ -417,13 +368,11 @@
/* Must be called inside pump_transfers() */
static void poll_transfer(struct dw_spi *dws)
{
- while (dws->write(dws))
- dws->read(dws);
- /*
- * There is a possibility that the last word of a transaction
- * will be lost if data is not ready. Re-read to solve this issue.
- */
- dws->read(dws);
+ do {
+ dw_writer(dws);
+ dw_reader(dws);
+ cpu_relax();
+ } while (dws->rx_end > dws->rx);
dw_spi_xfer_done(dws);
}
@@ -483,8 +432,6 @@
dws->tx_end = dws->tx + transfer->len;
dws->rx = transfer->rx_buf;
dws->rx_end = dws->rx + transfer->len;
- dws->write = dws->tx ? chip->write : null_writer;
- dws->read = dws->rx ? chip->read : null_reader;
dws->cs_change = transfer->cs_change;
dws->len = dws->cur_transfer->len;
if (chip != dws->prev_chip)
@@ -518,20 +465,8 @@
switch (bits) {
case 8:
- dws->n_bytes = 1;
- dws->dma_width = 1;
- dws->read = (dws->read != null_reader) ?
- u8_reader : null_reader;
- dws->write = (dws->write != null_writer) ?
- u8_writer : null_writer;
- break;
case 16:
- dws->n_bytes = 2;
- dws->dma_width = 2;
- dws->read = (dws->read != null_reader) ?
- u16_reader : null_reader;
- dws->write = (dws->write != null_writer) ?
- u16_writer : null_writer;
+ dws->n_bytes = dws->dma_width = bits >> 3;
break;
default:
printk(KERN_ERR "MRST SPI0: unsupported bits:"
@@ -575,7 +510,7 @@
txint_level = dws->fifo_len / 2;
txint_level = (templen > txint_level) ? txint_level : templen;
- imask |= SPI_INT_TXEI;
+ imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI;
dws->transfer_handler = interrupt_transfer;
}
@@ -733,13 +668,9 @@
if (spi->bits_per_word <= 8) {
chip->n_bytes = 1;
chip->dma_width = 1;
- chip->read = u8_reader;
- chip->write = u8_writer;
} else if (spi->bits_per_word <= 16) {
chip->n_bytes = 2;
chip->dma_width = 2;
- chip->read = u16_reader;
- chip->write = u16_writer;
} else {
/* Never take >16b case for MRST SPIC */
dev_err(&spi->dev, "invalid wordsize\n");
@@ -851,7 +782,6 @@
spi_enable_chip(dws, 0);
spi_mask_intr(dws, 0xff);
spi_enable_chip(dws, 1);
- flush(dws);
/*
* Try to detect the FIFO depth if not set by interface driver,
diff --git a/drivers/spi/dw_spi.h b/drivers/spi/dw_spi.h
index b23e452..7a5e78d 100644
--- a/drivers/spi/dw_spi.h
+++ b/drivers/spi/dw_spi.h
@@ -137,8 +137,6 @@
u8 max_bits_per_word; /* maxim is 16b */
u32 dma_width;
int cs_change;
- int (*write)(struct dw_spi *dws);
- int (*read)(struct dw_spi *dws);
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
void (*cs_control)(u32 command);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 82b9a42..2e13a14 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1047,8 +1047,8 @@
* spi_{async,sync}() calls with dma-safe buffers.
*/
int spi_write_then_read(struct spi_device *spi,
- const u8 *txbuf, unsigned n_tx,
- u8 *rxbuf, unsigned n_rx)
+ const void *txbuf, unsigned n_tx,
+ void *rxbuf, unsigned n_rx)
{
static DEFINE_MUTEX(lock);
diff --git a/drivers/spi/spi_nuc900.c b/drivers/spi/spi_nuc900.c
index d5be18b..3cd15f6 100644
--- a/drivers/spi/spi_nuc900.c
+++ b/drivers/spi/spi_nuc900.c
@@ -463,7 +463,7 @@
platform_set_drvdata(dev, NULL);
- spi_unregister_master(hw->master);
+ spi_bitbang_stop(&hw->bitbang);
clk_disable(hw->clk);
clk_put(hw->clk);
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 151a95e..1a5fcab 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -668,7 +668,7 @@
platform_set_drvdata(dev, NULL);
- spi_unregister_master(hw->master);
+ spi_bitbang_stop(&hw->bitbang);
clk_disable(hw->clk);
clk_put(hw->clk);
diff --git a/drivers/spi/spi_sh.c b/drivers/spi/spi_sh.c
index 869a07d..9eedd71 100644
--- a/drivers/spi/spi_sh.c
+++ b/drivers/spi/spi_sh.c
@@ -427,10 +427,10 @@
{
struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev);
+ spi_unregister_master(ss->master);
destroy_workqueue(ss->workqueue);
free_irq(ss->irq, ss);
iounmap(ss->addr);
- spi_master_put(ss->master);
return 0;
}
diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c
index 891e590..6c3aa6e 100644
--- a/drivers/spi/spi_tegra.c
+++ b/drivers/spi/spi_tegra.c
@@ -578,6 +578,7 @@
master = dev_get_drvdata(&pdev->dev);
tspi = spi_master_get_devdata(master);
+ spi_unregister_master(master);
tegra_dma_free_channel(tspi->rx_dma);
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
@@ -586,7 +587,6 @@
clk_put(tspi->clk);
iounmap(tspi->base);
- spi_master_put(master);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(r->start, (r->end - r->start) + 1);
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index c69c6f2..4d2c75d 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -18,7 +18,6 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/xilinx_spi.h>
@@ -471,7 +470,7 @@
struct spi_master *master;
u8 i;
- pdata = mfd_get_data(dev);
+ pdata = dev->dev.platform_data;
if (pdata) {
num_cs = pdata->num_chipselect;
little_endian = pdata->little_endian;
diff --git a/drivers/staging/generic_serial/rio/rioinit.c b/drivers/staging/generic_serial/rio/rioinit.c
index 24a282b..fb62b38 100644
--- a/drivers/staging/generic_serial/rio/rioinit.c
+++ b/drivers/staging/generic_serial/rio/rioinit.c
@@ -381,7 +381,7 @@
{
strlcpy(stVersion.version, "RIO driver for linux V1.0",
sizeof(stVersion.version));
- strlcpy(stVersion.buildDate, __DATE__,
+ strlcpy(stVersion.buildDate, "Aug 15 2010",
sizeof(stVersion.buildDate));
return &stVersion;
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
index bfa05e8..c0e8f2e 100644
--- a/drivers/tty/cyclades.c
+++ b/drivers/tty/cyclades.c
@@ -4096,8 +4096,7 @@
if (!cy_serial_driver)
goto err;
- printk(KERN_INFO "Cyclades driver " CY_VERSION " (built %s %s)\n",
- __DATE__, __TIME__);
+ printk(KERN_INFO "Cyclades driver " CY_VERSION "\n");
/* Initialize the tty_driver structure */
diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c
index b1aecc7..fd347ff 100644
--- a/drivers/tty/nozomi.c
+++ b/drivers/tty/nozomi.c
@@ -61,8 +61,7 @@
#include <linux/delay.h>
-#define VERSION_STRING DRIVER_DESC " 2.1d (build date: " \
- __DATE__ " " __TIME__ ")"
+#define VERSION_STRING DRIVER_DESC " 2.1d"
/* Macros definitions */
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
index bea5c21..84db732 100644
--- a/drivers/tty/serial/m32r_sio.c
+++ b/drivers/tty/serial/m32r_sio.c
@@ -907,9 +907,10 @@
return ret;
}
-static void m32r_sio_config_port(struct uart_port *port, int flags)
+static void m32r_sio_config_port(struct uart_port *port, int unused)
{
struct uart_sio_port *up = (struct uart_sio_port *)port;
+ unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 3f2e0701..cfb5aa7 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -100,6 +100,7 @@
u8 linkstat;
u8 asleep;
bool irq_enabled;
+ unsigned long features;
};
#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg)
@@ -204,6 +205,12 @@
static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
{
+ char *regulator_name;
+
+ if (twl->features & TWL6025_SUBCLASS)
+ regulator_name = "ldousb";
+ else
+ regulator_name = "vusb";
/* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
@@ -214,7 +221,7 @@
/* Program MISC2 register and set bit VUSB_IN_VBAT */
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
- twl->usb3v3 = regulator_get(twl->dev, "vusb");
+ twl->usb3v3 = regulator_get(twl->dev, regulator_name);
if (IS_ERR(twl->usb3v3))
return -ENODEV;
@@ -409,6 +416,7 @@
twl->dev = &pdev->dev;
twl->irq1 = platform_get_irq(pdev, 0);
twl->irq2 = platform_get_irq(pdev, 1);
+ twl->features = pdata->features;
twl->otg.dev = twl->dev;
twl->otg.label = "twl6030";
twl->otg.set_host = twl6030_set_host;
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index c8b520e..c04b94d 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -16,7 +16,6 @@
#include <linux/fb.h>
#include <linux/i2c.h>
#include <linux/backlight.h>
-#include <linux/mfd/core.h>
#include <linux/mfd/88pm860x.h>
#define MAX_BRIGHTNESS (0xFF)
@@ -168,7 +167,6 @@
struct pm860x_backlight_pdata *pdata = NULL;
struct pm860x_backlight_data *data;
struct backlight_device *bl;
- struct mfd_cell *cell;
struct resource *res;
struct backlight_properties props;
unsigned char value;
@@ -181,10 +179,7 @@
return -EINVAL;
}
- cell = pdev->dev.platform_data;
- if (cell == NULL)
- return -ENODEV;
- pdata = cell->mfd_data;
+ pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"backlight\n");
diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c
index ea39336..f70bd63 100644
--- a/drivers/video/mb862xx/mb862xxfbdrv.c
+++ b/drivers/video/mb862xx/mb862xxfbdrv.c
@@ -16,6 +16,7 @@
#include <linux/fb.h>
#include <linux/delay.h>
+#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile
index 49226a1..25db556 100644
--- a/drivers/video/omap/Makefile
+++ b/drivers/video/omap/Makefile
@@ -30,7 +30,6 @@
objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o
objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o
objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o
-objs-y$(CONFIG_MACH_OMAP2EVM) += lcd_omap2evm.o
objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o
objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o
objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c
deleted file mode 100644
index 7e7a65c..0000000
--- a/drivers/video/omap/lcd_omap2evm.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * LCD panel support for the MISTRAL OMAP2EVM board
- *
- * Author: Arun C <arunedarath@mistralsolutions.com>
- *
- * Derived from drivers/video/omap/lcd_omap3evm.c
- * Derived from drivers/video/omap/lcd-apollon.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/i2c/twl.h>
-
-#include <plat/mux.h>
-#include <asm/mach-types.h>
-
-#include "omapfb.h"
-
-#define LCD_PANEL_ENABLE_GPIO 154
-#define LCD_PANEL_LR 128
-#define LCD_PANEL_UD 129
-#define LCD_PANEL_INI 152
-#define LCD_PANEL_QVGA 148
-#define LCD_PANEL_RESB 153
-
-#define TWL_LED_LEDEN 0x00
-#define TWL_PWMA_PWMAON 0x00
-#define TWL_PWMA_PWMAOFF 0x01
-
-static unsigned int bklight_level;
-
-static int omap2evm_panel_init(struct lcd_panel *panel,
- struct omapfb_device *fbdev)
-{
- gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable");
- gpio_request(LCD_PANEL_LR, "LCD lr");
- gpio_request(LCD_PANEL_UD, "LCD ud");
- gpio_request(LCD_PANEL_INI, "LCD ini");
- gpio_request(LCD_PANEL_QVGA, "LCD qvga");
- gpio_request(LCD_PANEL_RESB, "LCD resb");
-
- gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1);
- gpio_direction_output(LCD_PANEL_RESB, 1);
- gpio_direction_output(LCD_PANEL_INI, 1);
- gpio_direction_output(LCD_PANEL_QVGA, 0);
- gpio_direction_output(LCD_PANEL_LR, 1);
- gpio_direction_output(LCD_PANEL_UD, 1);
-
- twl_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN);
- twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON);
- twl_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF);
- bklight_level = 100;
-
- return 0;
-}
-
-static void omap2evm_panel_cleanup(struct lcd_panel *panel)
-{
- gpio_free(LCD_PANEL_RESB);
- gpio_free(LCD_PANEL_QVGA);
- gpio_free(LCD_PANEL_INI);
- gpio_free(LCD_PANEL_UD);
- gpio_free(LCD_PANEL_LR);
- gpio_free(LCD_PANEL_ENABLE_GPIO);
-}
-
-static int omap2evm_panel_enable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0);
- return 0;
-}
-
-static void omap2evm_panel_disable(struct lcd_panel *panel)
-{
- gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1);
-}
-
-static unsigned long omap2evm_panel_get_caps(struct lcd_panel *panel)
-{
- return 0;
-}
-
-static int omap2evm_bklight_setlevel(struct lcd_panel *panel,
- unsigned int level)
-{
- u8 c;
- if ((level >= 0) && (level <= 100)) {
- c = (125 * (100 - level)) / 100 + 2;
- twl_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF);
- bklight_level = level;
- }
- return 0;
-}
-
-static unsigned int omap2evm_bklight_getlevel(struct lcd_panel *panel)
-{
- return bklight_level;
-}
-
-static unsigned int omap2evm_bklight_getmaxlevel(struct lcd_panel *panel)
-{
- return 100;
-}
-
-struct lcd_panel omap2evm_panel = {
- .name = "omap2evm",
- .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
- OMAP_LCDC_INV_HSYNC,
-
- .bpp = 16,
- .data_lines = 18,
- .x_res = 480,
- .y_res = 640,
- .hsw = 3,
- .hfp = 0,
- .hbp = 28,
- .vsw = 2,
- .vfp = 1,
- .vbp = 0,
-
- .pixel_clock = 20000,
-
- .init = omap2evm_panel_init,
- .cleanup = omap2evm_panel_cleanup,
- .enable = omap2evm_panel_enable,
- .disable = omap2evm_panel_disable,
- .get_caps = omap2evm_panel_get_caps,
- .set_bklight_level = omap2evm_bklight_setlevel,
- .get_bklight_level = omap2evm_bklight_getlevel,
- .get_bklight_max = omap2evm_bklight_getmaxlevel,
-};
-
-static int omap2evm_panel_probe(struct platform_device *pdev)
-{
- omapfb_register_panel(&omap2evm_panel);
- return 0;
-}
-
-static int omap2evm_panel_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static int omap2evm_panel_suspend(struct platform_device *pdev,
- pm_message_t mesg)
-{
- return 0;
-}
-
-static int omap2evm_panel_resume(struct platform_device *pdev)
-{
- return 0;
-}
-
-struct platform_driver omap2evm_panel_driver = {
- .probe = omap2evm_panel_probe,
- .remove = omap2evm_panel_remove,
- .suspend = omap2evm_panel_suspend,
- .resume = omap2evm_panel_resume,
- .driver = {
- .name = "omap2evm_lcd",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init omap2evm_panel_drv_init(void)
-{
- return platform_driver_register(&omap2evm_panel_driver);
-}
-
-static void __exit omap2evm_panel_drv_exit(void)
-{
- platform_driver_unregister(&omap2evm_panel_driver);
-}
-
-module_init(omap2evm_panel_drv_init);
-module_exit(omap2evm_panel_drv_exit);
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
index 0c341d7..cd1c4dce 100644
--- a/drivers/video/tmiofb.c
+++ b/drivers/video/tmiofb.c
@@ -250,7 +250,7 @@
*/
static int tmiofb_hw_stop(struct platform_device *dev)
{
- struct tmio_fb_data *data = mfd_get_data(dev);
+ struct tmio_fb_data *data = dev->dev.platform_data;
struct fb_info *info = platform_get_drvdata(dev);
struct tmiofb_par *par = info->par;
@@ -311,7 +311,7 @@
*/
static void tmiofb_hw_mode(struct platform_device *dev)
{
- struct tmio_fb_data *data = mfd_get_data(dev);
+ struct tmio_fb_data *data = dev->dev.platform_data;
struct fb_info *info = platform_get_drvdata(dev);
struct fb_videomode *mode = info->mode;
struct tmiofb_par *par = info->par;
@@ -557,8 +557,7 @@
static struct fb_videomode *
tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var)
{
- struct tmio_fb_data *data =
- mfd_get_data(to_platform_device(info->device));
+ struct tmio_fb_data *data = info->device->platform_data;
struct fb_videomode *best = NULL;
int i;
@@ -578,8 +577,7 @@
{
struct fb_videomode *mode;
- struct tmio_fb_data *data =
- mfd_get_data(to_platform_device(info->device));
+ struct tmio_fb_data *data = info->device->platform_data;
mode = tmiofb_find_mode(info, var);
if (!mode || var->bits_per_pixel > 16)
@@ -680,7 +678,7 @@
static int __devinit tmiofb_probe(struct platform_device *dev)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
- struct tmio_fb_data *data = mfd_get_data(dev);
+ struct tmio_fb_data *data = dev->dev.platform_data;
struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1);
struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0);
struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2);
diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c
index c2a0a1cf..ab53418 100644
--- a/drivers/video/via/via-gpio.c
+++ b/drivers/video/via/via-gpio.c
@@ -145,7 +145,7 @@
}
-static struct viafb_gpio_cfg gpio_config = {
+static struct viafb_gpio_cfg viafb_gpio_config = {
.gpio_chip = {
.label = "VIAFB onboard GPIO",
.owner = THIS_MODULE,
@@ -183,8 +183,8 @@
{
int i;
- for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2)
- viafb_gpio_enable(gpio_config.active_gpios[i]);
+ for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2)
+ viafb_gpio_enable(viafb_gpio_config.active_gpios[i]);
return 0;
}
@@ -201,9 +201,9 @@
{
int i;
- for (i = 0; i < gpio_config.gpio_chip.ngpio; i++)
- if (!strcmp(name, gpio_config.active_gpios[i]->vg_name))
- return gpio_config.gpio_chip.base + i;
+ for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i++)
+ if (!strcmp(name, viafb_gpio_config.active_gpios[i]->vg_name))
+ return viafb_gpio_config.gpio_chip.base + i;
return -1;
}
EXPORT_SYMBOL_GPL(viafb_gpio_lookup);
@@ -229,14 +229,15 @@
for (gpio = viafb_all_gpios;
gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++)
if (gpio->vg_port_index == port_cfg[i].ioport_index) {
- gpio_config.active_gpios[ngpio] = gpio;
- gpio_config.gpio_names[ngpio] = gpio->vg_name;
+ viafb_gpio_config.active_gpios[ngpio] = gpio;
+ viafb_gpio_config.gpio_names[ngpio] =
+ gpio->vg_name;
ngpio++;
}
}
- gpio_config.gpio_chip.ngpio = ngpio;
- gpio_config.gpio_chip.names = gpio_config.gpio_names;
- gpio_config.vdev = vdev;
+ viafb_gpio_config.gpio_chip.ngpio = ngpio;
+ viafb_gpio_config.gpio_chip.names = viafb_gpio_config.gpio_names;
+ viafb_gpio_config.vdev = vdev;
if (ngpio == 0) {
printk(KERN_INFO "viafb: no GPIOs configured\n");
return 0;
@@ -245,18 +246,18 @@
* Enable the ports. They come in pairs, with a single
* enable bit for both.
*/
- spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags);
+ spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags);
for (i = 0; i < ngpio; i += 2)
- viafb_gpio_enable(gpio_config.active_gpios[i]);
- spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags);
+ viafb_gpio_enable(viafb_gpio_config.active_gpios[i]);
+ spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
/*
* Get registered.
*/
- gpio_config.gpio_chip.base = -1; /* Dynamic */
- ret = gpiochip_add(&gpio_config.gpio_chip);
+ viafb_gpio_config.gpio_chip.base = -1; /* Dynamic */
+ ret = gpiochip_add(&viafb_gpio_config.gpio_chip);
if (ret) {
printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret);
- gpio_config.gpio_chip.ngpio = 0;
+ viafb_gpio_config.gpio_chip.ngpio = 0;
}
#ifdef CONFIG_PM
viafb_pm_register(&viafb_gpio_pm_hooks);
@@ -277,8 +278,8 @@
/*
* Get unregistered.
*/
- if (gpio_config.gpio_chip.ngpio > 0) {
- ret = gpiochip_remove(&gpio_config.gpio_chip);
+ if (viafb_gpio_config.gpio_chip.ngpio > 0) {
+ ret = gpiochip_remove(&viafb_gpio_config.gpio_chip);
if (ret) { /* Somebody still using it? */
printk(KERN_ERR "Viafb: GPIO remove failed\n");
return ret;
@@ -287,11 +288,11 @@
/*
* Disable the ports.
*/
- spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags);
- for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2)
- viafb_gpio_disable(gpio_config.active_gpios[i]);
- gpio_config.gpio_chip.ngpio = 0;
- spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags);
+ spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags);
+ for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2)
+ viafb_gpio_disable(viafb_gpio_config.active_gpios[i]);
+ viafb_gpio_config.gpio_chip.ngpio = 0;
+ spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags);
return ret;
}
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 7c608c5..00d615d 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -42,7 +42,7 @@
config W1_MASTER_DS1WM
tristate "Maxim DS1WM 1-wire busmaster"
- depends on W1 && ARM && HAVE_CLK
+ depends on W1
help
Say Y here to enable the DS1WM 1-wire driver, such as that
in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like
diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c
index 2f4fa02..ad57593 100644
--- a/drivers/w1/masters/ds1wm.c
+++ b/drivers/w1/masters/ds1wm.c
@@ -33,6 +33,7 @@
#define DS1WM_INT 0x02 /* R/W interrupt status */
#define DS1WM_INT_EN 0x03 /* R/W interrupt enable */
#define DS1WM_CLKDIV 0x04 /* R/W 5 bits of divisor and pre-scale */
+#define DS1WM_CNTRL 0x05 /* R/W master control register (not used yet) */
#define DS1WM_CMD_1W_RESET (1 << 0) /* force reset on 1-wire bus */
#define DS1WM_CMD_SRA (1 << 1) /* enable Search ROM accelerator mode */
@@ -56,6 +57,7 @@
#define DS1WM_INTEN_ERSRF (1 << 5) /* enable rx shift register full int */
#define DS1WM_INTEN_DQO (1 << 6) /* enable direct bus driving ops */
+#define DS1WM_INTEN_NOT_IAS (~DS1WM_INTEN_IAS) /* all but INTR active state */
#define DS1WM_TIMEOUT (HZ * 5)
@@ -63,41 +65,50 @@
unsigned long freq;
unsigned long divisor;
} freq[] = {
- { 4000000, 0x8 },
- { 5000000, 0x2 },
- { 6000000, 0x5 },
- { 7000000, 0x3 },
- { 8000000, 0xc },
- { 10000000, 0x6 },
- { 12000000, 0x9 },
- { 14000000, 0x7 },
- { 16000000, 0x10 },
- { 20000000, 0xa },
- { 24000000, 0xd },
- { 28000000, 0xb },
- { 32000000, 0x14 },
- { 40000000, 0xe },
- { 48000000, 0x11 },
- { 56000000, 0xf },
- { 64000000, 0x18 },
- { 80000000, 0x12 },
- { 96000000, 0x15 },
- { 112000000, 0x13 },
- { 128000000, 0x1c },
+ { 1000000, 0x80 },
+ { 2000000, 0x84 },
+ { 3000000, 0x81 },
+ { 4000000, 0x88 },
+ { 5000000, 0x82 },
+ { 6000000, 0x85 },
+ { 7000000, 0x83 },
+ { 8000000, 0x8c },
+ { 10000000, 0x86 },
+ { 12000000, 0x89 },
+ { 14000000, 0x87 },
+ { 16000000, 0x90 },
+ { 20000000, 0x8a },
+ { 24000000, 0x8d },
+ { 28000000, 0x8b },
+ { 32000000, 0x94 },
+ { 40000000, 0x8e },
+ { 48000000, 0x91 },
+ { 56000000, 0x8f },
+ { 64000000, 0x98 },
+ { 80000000, 0x92 },
+ { 96000000, 0x95 },
+ { 112000000, 0x93 },
+ { 128000000, 0x9c },
+/* you can continue this table, consult the OPERATION - CLOCK DIVISOR
+ section of the ds1wm spec sheet. */
};
struct ds1wm_data {
- void __iomem *map;
- int bus_shift; /* # of shifts to calc register offsets */
+ void __iomem *map;
+ int bus_shift; /* # of shifts to calc register offsets */
struct platform_device *pdev;
- const struct mfd_cell *cell;
- int irq;
- int active_high;
- int slave_present;
- void *reset_complete;
- void *read_complete;
- void *write_complete;
- u8 read_byte; /* last byte received */
+ const struct mfd_cell *cell;
+ int irq;
+ int slave_present;
+ void *reset_complete;
+ void *read_complete;
+ void *write_complete;
+ int read_error;
+ /* last byte received */
+ u8 read_byte;
+ /* byte to write that makes all intr disabled, */
+ /* considering active_state (IAS) (optimization) */
+ u8 int_en_reg_none;
};
static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg,
@@ -115,23 +126,39 @@
static irqreturn_t ds1wm_isr(int isr, void *data)
{
struct ds1wm_data *ds1wm_data = data;
- u8 intr = ds1wm_read_register(ds1wm_data, DS1WM_INT);
+ u8 intr;
+ u8 inten = ds1wm_read_register(ds1wm_data, DS1WM_INT_EN);
+ /* if no bits are set in int enable register (except the IAS)
+ than go no further, reading the regs below has side effects */
+ if (!(inten & DS1WM_INTEN_NOT_IAS))
+ return IRQ_NONE;
+
+ ds1wm_write_register(ds1wm_data,
+ DS1WM_INT_EN, ds1wm_data->int_en_reg_none);
+
+ /* this read action clears the INTR and certain flags in ds1wm */
+ intr = ds1wm_read_register(ds1wm_data, DS1WM_INT);
ds1wm_data->slave_present = (intr & DS1WM_INT_PDR) ? 0 : 1;
- if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete)
- complete(ds1wm_data->reset_complete);
-
- if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete)
+ if ((intr & DS1WM_INT_TSRE) && ds1wm_data->write_complete) {
+ inten &= ~DS1WM_INTEN_ETMT;
complete(ds1wm_data->write_complete);
-
+ }
if (intr & DS1WM_INT_RBF) {
+ /* this read clears the RBF flag */
ds1wm_data->read_byte = ds1wm_read_register(ds1wm_data,
- DS1WM_DATA);
+ DS1WM_DATA);
+ inten &= ~DS1WM_INTEN_ERBF;
if (ds1wm_data->read_complete)
complete(ds1wm_data->read_complete);
}
+ if ((intr & DS1WM_INT_PD) && ds1wm_data->reset_complete) {
+ inten &= ~DS1WM_INTEN_EPD;
+ complete(ds1wm_data->reset_complete);
+ }
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, inten);
return IRQ_HANDLED;
}
@@ -142,33 +169,19 @@
ds1wm_data->reset_complete = &reset_done;
+ /* enable Presence detect only */
ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, DS1WM_INTEN_EPD |
- (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
+ ds1wm_data->int_en_reg_none);
ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_1W_RESET);
timeleft = wait_for_completion_timeout(&reset_done, DS1WM_TIMEOUT);
ds1wm_data->reset_complete = NULL;
if (!timeleft) {
- dev_err(&ds1wm_data->pdev->dev, "reset failed\n");
+ dev_err(&ds1wm_data->pdev->dev, "reset failed, timed out\n");
return 1;
}
- /* Wait for the end of the reset. According to the specs, the time
- * from when the interrupt is asserted to the end of the reset is:
- * tRSTH - tPDH - tPDL - tPDI
- * 625 us - 60 us - 240 us - 100 ns = 324.9 us
- *
- * We'll wait a bit longer just to be sure.
- * Was udelay(500), but if it is going to busywait the cpu that long,
- * might as well come back later.
- */
- msleep(1);
-
- ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
- DS1WM_INTEN_ERBF | DS1WM_INTEN_ETMT | DS1WM_INTEN_EPD |
- (ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0));
-
if (!ds1wm_data->slave_present) {
dev_dbg(&ds1wm_data->pdev->dev, "reset: no devices found\n");
return 1;
@@ -179,26 +192,47 @@
static int ds1wm_write(struct ds1wm_data *ds1wm_data, u8 data)
{
+ unsigned long timeleft;
DECLARE_COMPLETION_ONSTACK(write_done);
ds1wm_data->write_complete = &write_done;
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
+ ds1wm_data->int_en_reg_none | DS1WM_INTEN_ETMT);
+
ds1wm_write_register(ds1wm_data, DS1WM_DATA, data);
- wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT);
+ timeleft = wait_for_completion_timeout(&write_done, DS1WM_TIMEOUT);
+
ds1wm_data->write_complete = NULL;
+ if (!timeleft) {
+ dev_err(&ds1wm_data->pdev->dev, "write failed, timed out\n");
+ return -ETIMEDOUT;
+ }
return 0;
}
-static int ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)
+static u8 ds1wm_read(struct ds1wm_data *ds1wm_data, unsigned char write_data)
{
+ unsigned long timeleft;
+ u8 intEnable = DS1WM_INTEN_ERBF | ds1wm_data->int_en_reg_none;
DECLARE_COMPLETION_ONSTACK(read_done);
+
+ ds1wm_read_register(ds1wm_data, DS1WM_DATA);
+
ds1wm_data->read_complete = &read_done;
+ ds1wm_write_register(ds1wm_data, DS1WM_INT_EN, intEnable);
- ds1wm_write(ds1wm_data, write_data);
- wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT);
+ ds1wm_write_register(ds1wm_data, DS1WM_DATA, write_data);
+ timeleft = wait_for_completion_timeout(&read_done, DS1WM_TIMEOUT);
+
ds1wm_data->read_complete = NULL;
-
+ if (!timeleft) {
+ dev_err(&ds1wm_data->pdev->dev, "read failed, timed out\n");
+ ds1wm_data->read_error = -ETIMEDOUT;
+ return 0xFF;
+ }
+ ds1wm_data->read_error = 0;
return ds1wm_data->read_byte;
}
@@ -206,8 +240,8 @@
{
int i;
- for (i = 0; i < ARRAY_SIZE(freq); i++)
- if (gclk <= freq[i].freq)
+ for (i = ARRAY_SIZE(freq)-1; i >= 0; --i)
+ if (gclk >= freq[i].freq)
return freq[i].divisor;
return 0;
@@ -216,12 +250,14 @@
static void ds1wm_up(struct ds1wm_data *ds1wm_data)
{
int divisor;
- struct ds1wm_driver_data *plat = mfd_get_data(ds1wm_data->pdev);
+ struct ds1wm_driver_data *plat = ds1wm_data->pdev->dev.platform_data;
if (ds1wm_data->cell->enable)
ds1wm_data->cell->enable(ds1wm_data->pdev);
divisor = ds1wm_find_divisor(plat->clock_rate);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "found divisor 0x%x for clock %d\n", divisor, plat->clock_rate);
if (divisor == 0) {
dev_err(&ds1wm_data->pdev->dev,
"no suitable divisor for %dHz clock\n",
@@ -242,7 +278,7 @@
/* Disable interrupts. */
ds1wm_write_register(ds1wm_data, DS1WM_INT_EN,
- ds1wm_data->active_high ? DS1WM_INTEN_IAS : 0);
+ ds1wm_data->int_en_reg_none);
if (ds1wm_data->cell->disable)
ds1wm_data->cell->disable(ds1wm_data->pdev);
@@ -279,41 +315,121 @@
{
struct ds1wm_data *ds1wm_data = data;
int i;
- unsigned long long rom_id;
+ int ms_discrep_bit = -1;
+ u64 r = 0; /* holds the progress of the search */
+ u64 r_prime, d;
+ unsigned slaves_found = 0;
+ unsigned int pass = 0;
- /* XXX We need to iterate for multiple devices per the DS1WM docs.
- * See http://www.maxim-ic.com/appnotes.cfm/appnote_number/120. */
- if (ds1wm_reset(ds1wm_data))
- return;
+ dev_dbg(&ds1wm_data->pdev->dev, "search begin\n");
+ while (true) {
+ ++pass;
+ if (pass > 100) {
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "too many attempts (100), search aborted\n");
+ return;
+ }
- ds1wm_write(ds1wm_data, search_type);
- ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
+ if (ds1wm_reset(ds1wm_data)) {
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d reset error (or no slaves)\n", pass);
+ break;
+ }
- for (rom_id = 0, i = 0; i < 16; i++) {
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d r : %0#18llx writing SEARCH_ROM\n", pass, r);
+ ds1wm_write(ds1wm_data, search_type);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d entering ASM\n", pass);
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d begining nibble loop\n", pass);
- unsigned char resp, r, d;
+ r_prime = 0;
+ d = 0;
+ /* we work one nibble at a time */
+ /* each nibble is interleaved to form a byte */
+ for (i = 0; i < 16; i++) {
- resp = ds1wm_read(ds1wm_data, 0x00);
+ unsigned char resp, _r, _r_prime, _d;
- r = ((resp & 0x02) >> 1) |
- ((resp & 0x08) >> 2) |
- ((resp & 0x20) >> 3) |
- ((resp & 0x80) >> 4);
+ _r = (r >> (4*i)) & 0xf;
+ _r = ((_r & 0x1) << 1) |
+ ((_r & 0x2) << 2) |
+ ((_r & 0x4) << 3) |
+ ((_r & 0x8) << 4);
- d = ((resp & 0x01) >> 0) |
- ((resp & 0x04) >> 1) |
- ((resp & 0x10) >> 2) |
- ((resp & 0x40) >> 3);
+ /* writes _r, then reads back: */
+ resp = ds1wm_read(ds1wm_data, _r);
- rom_id |= (unsigned long long) r << (i * 4);
+ if (ds1wm_data->read_error) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "pass: %d nibble: %d read error\n", pass, i);
+ break;
+ }
- }
- dev_dbg(&ds1wm_data->pdev->dev, "found 0x%08llX\n", rom_id);
+ _r_prime = ((resp & 0x02) >> 1) |
+ ((resp & 0x08) >> 2) |
+ ((resp & 0x20) >> 3) |
+ ((resp & 0x80) >> 4);
- ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA);
- ds1wm_reset(ds1wm_data);
+ _d = ((resp & 0x01) >> 0) |
+ ((resp & 0x04) >> 1) |
+ ((resp & 0x10) >> 2) |
+ ((resp & 0x40) >> 3);
- slave_found(master_dev, rom_id);
+ r_prime |= (unsigned long long) _r_prime << (i * 4);
+ d |= (unsigned long long) _d << (i * 4);
+
+ }
+ if (ds1wm_data->read_error) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "pass: %d read error, retrying\n", pass);
+ break;
+ }
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d r\': %0#18llx d:%0#18llx\n",
+ pass, r_prime, d);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d nibble loop complete, exiting ASM\n", pass);
+ ds1wm_write_register(ds1wm_data, DS1WM_CMD, ~DS1WM_CMD_SRA);
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d resetting bus\n", pass);
+ ds1wm_reset(ds1wm_data);
+ if ((r_prime & ((u64)1 << 63)) && (d & ((u64)1 << 63))) {
+ dev_err(&ds1wm_data->pdev->dev,
+ "pass: %d bus error, retrying\n", pass);
+ continue; /* start over */
+ }
+
+
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d found %0#18llx\n", pass, r_prime);
+ slave_found(master_dev, r_prime);
+ ++slaves_found;
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d complete, preparing next pass\n", pass);
+
+ /* any discrepency found which we already choose the
+ '1' branch is now is now irrelevant we reveal the
+ next branch with this: */
+ d &= ~r;
+ /* find last bit set, i.e. the most signif. bit set */
+ ms_discrep_bit = fls64(d) - 1;
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d new d:%0#18llx MS discrep bit:%d\n",
+ pass, d, ms_discrep_bit);
+
+ /* prev_ms_discrep_bit = ms_discrep_bit;
+ prepare for next ROM search: */
+ if (ms_discrep_bit == -1)
+ break;
+
+ r = (r & ~(~0ull << (ms_discrep_bit))) | 1 << ms_discrep_bit;
+ } /* end while true */
+ dev_dbg(&ds1wm_data->pdev->dev,
+ "pass: %d total: %d search done ms d bit pos: %d\n", pass,
+ slaves_found, ms_discrep_bit);
}
/* --------------------------------------------------------------------- */
@@ -351,13 +467,21 @@
ret = -ENOMEM;
goto err0;
}
- plat = mfd_get_data(pdev);
/* calculate bus shift from mem resource */
ds1wm_data->bus_shift = resource_size(res) >> 3;
ds1wm_data->pdev = pdev;
ds1wm_data->cell = mfd_get_cell(pdev);
+ if (!ds1wm_data->cell) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ plat = pdev->dev.platform_data;
+ if (!plat) {
+ ret = -ENODEV;
+ goto err1;
+ }
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -365,15 +489,15 @@
goto err1;
}
ds1wm_data->irq = res->start;
- ds1wm_data->active_high = plat->active_high;
+ ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0);
if (res->flags & IORESOURCE_IRQ_HIGHEDGE)
irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING);
if (res->flags & IORESOURCE_IRQ_LOWEDGE)
irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING);
- ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED,
- "ds1wm", ds1wm_data);
+ ret = request_irq(ds1wm_data->irq, ds1wm_isr,
+ IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data);
if (ret)
goto err1;
@@ -460,5 +584,6 @@
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
- "Matt Reimer <mreimer@vpop.net>");
+ "Matt Reimer <mreimer@vpop.net>,"
+ "Jean-Francois Dagenais <dagenaisj@sonatest.com>");
MODULE_DESCRIPTION("DS1WM w1 busmaster driver");
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index f0c9096..d0cb01b 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -16,6 +16,13 @@
Say Y here if you want to connect 1-wire
simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
+config W1_SLAVE_DS2408
+ tristate "8-Channel Addressable Switch (IO Expander) 0x29 family support (DS2408)"
+ help
+ Say Y here if you want to use a 1-wire
+
+ DS2408 8-Channel Addressable Switch device support
+
config W1_SLAVE_DS2423
tristate "Counter 1-wire device (DS2423)"
select CRC16
@@ -61,6 +68,19 @@
If you are unsure, say N.
+config W1_SLAVE_DS2780
+ tristate "Dallas 2780 battery monitor chip"
+ depends on W1
+ help
+ If you enable this you will have the DS2780 battery monitor
+ chip support.
+
+ The battery monitor chip is used in many batteries/devices
+ as the one who is responsible for charging/discharging/monitoring
+ Li+ batteries.
+
+ If you are unsure, say N.
+
config W1_SLAVE_BQ27000
tristate "BQ27000 slave support"
depends on W1
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index 3c76350..1f31e9f 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -4,8 +4,10 @@
obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
+obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o
obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o
obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
+obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o
obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c
new file mode 100644
index 0000000..c377818
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2408.c
@@ -0,0 +1,402 @@
+/*
+ * w1_ds2408.c - w1 family 29 (DS2408) driver
+ *
+ * Copyright (c) 2010 Jean-Francois Dagenais <dagenaisj@sonatest.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jean-Francois Dagenais <dagenaisj@sonatest.com>");
+MODULE_DESCRIPTION("w1 family 29 driver for DS2408 8 Pin IO");
+
+
+#define W1_F29_RETRIES 3
+
+#define W1_F29_REG_LOGIG_STATE 0x88 /* R */
+#define W1_F29_REG_OUTPUT_LATCH_STATE 0x89 /* R */
+#define W1_F29_REG_ACTIVITY_LATCH_STATE 0x8A /* R */
+#define W1_F29_REG_COND_SEARCH_SELECT_MASK 0x8B /* RW */
+#define W1_F29_REG_COND_SEARCH_POL_SELECT 0x8C /* RW */
+#define W1_F29_REG_CONTROL_AND_STATUS 0x8D /* RW */
+
+#define W1_F29_FUNC_READ_PIO_REGS 0xF0
+#define W1_F29_FUNC_CHANN_ACCESS_READ 0xF5
+#define W1_F29_FUNC_CHANN_ACCESS_WRITE 0x5A
+/* also used to write the control/status reg (0x8D): */
+#define W1_F29_FUNC_WRITE_COND_SEARCH_REG 0xCC
+#define W1_F29_FUNC_RESET_ACTIVITY_LATCHES 0xC3
+
+#define W1_F29_SUCCESS_CONFIRM_BYTE 0xAA
+
+static int _read_reg(struct w1_slave *sl, u8 address, unsigned char* buf)
+{
+ u8 wrbuf[3];
+ dev_dbg(&sl->dev,
+ "Reading with slave: %p, reg addr: %0#4x, buff addr: %p",
+ sl, (unsigned int)address, buf);
+
+ if (!buf)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl)) {
+ mutex_unlock(&sl->master->mutex);
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ wrbuf[1] = address;
+ wrbuf[2] = 0;
+ w1_write_block(sl->master, wrbuf, 3);
+ *buf = w1_read_8(sl->master);
+
+ mutex_unlock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex unlocked");
+ return 1;
+}
+
+static ssize_t w1_f29_read_state(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj), W1_F29_REG_LOGIG_STATE, buf);
+}
+
+static ssize_t w1_f29_read_output(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_OUTPUT_LATCH_STATE, buf);
+}
+
+static ssize_t w1_f29_read_activity(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_ACTIVITY_LATCH_STATE, buf);
+}
+
+static ssize_t w1_f29_read_cond_search_mask(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ dev_dbg(&kobj_to_w1_slave(kobj)->dev,
+ "Reading %s kobj: %p, off: %0#10x, count: %zu, buff addr: %p",
+ bin_attr->attr.name, kobj, (unsigned int)off, count, buf);
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_COND_SEARCH_SELECT_MASK, buf);
+}
+
+static ssize_t w1_f29_read_cond_search_polarity(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_COND_SEARCH_POL_SELECT, buf);
+}
+
+static ssize_t w1_f29_read_status_control(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ if (count != 1 || off != 0)
+ return -EFAULT;
+ return _read_reg(kobj_to_w1_slave(kobj),
+ W1_F29_REG_CONTROL_AND_STATUS, buf);
+}
+
+
+
+
+static ssize_t w1_f29_write_output(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[3];
+ u8 readBack;
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ dev_dbg(&sl->dev, "locking mutex for write_output");
+ mutex_lock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex locked");
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_buf[0] = W1_F29_FUNC_CHANN_ACCESS_WRITE;
+ w1_buf[1] = *buf;
+ w1_buf[2] = ~(*buf);
+ w1_write_block(sl->master, w1_buf, 3);
+
+ readBack = w1_read_8(sl->master);
+ /* here the master could read another byte which
+ would be the PIO reg (the actual pin logic state)
+ since in this driver we don't know which pins are
+ in and outs, there's no value to read the state and
+ compare. with (*buf) so end this command abruptly: */
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+
+ if (readBack != 0xAA) {
+ /* try again, the slave is ready for a command */
+ continue;
+ }
+
+ /* go read back the output latches */
+ /* (the direct effect of the write above) */
+ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ w1_buf[1] = W1_F29_REG_OUTPUT_LATCH_STATE;
+ w1_buf[2] = 0;
+ w1_write_block(sl->master, w1_buf, 3);
+ /* read the result of the READ_PIO_REGS command */
+ if (w1_read_8(sl->master) == *buf) {
+ /* success! */
+ mutex_unlock(&sl->master->mutex);
+ dev_dbg(&sl->dev,
+ "mutex unlocked, retries:%d", retries);
+ return 1;
+ }
+ }
+error:
+ mutex_unlock(&sl->master->mutex);
+ dev_dbg(&sl->dev, "mutex unlocked in error, retries:%d", retries);
+
+ return -EIO;
+}
+
+
+/**
+ * Writing to the activity file resets the activity latches.
+ */
+static ssize_t w1_f29_write_activity(
+ struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_write_8(sl->master, W1_F29_FUNC_RESET_ACTIVITY_LATCHES);
+ if (w1_read_8(sl->master) == W1_F29_SUCCESS_CONFIRM_BYTE) {
+ mutex_unlock(&sl->master->mutex);
+ return 1;
+ }
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+ }
+
+error:
+ mutex_unlock(&sl->master->mutex);
+ return -EIO;
+}
+
+static ssize_t w1_f29_write_status_control(
+ struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf,
+ loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ u8 w1_buf[4];
+ unsigned int retries = W1_F29_RETRIES;
+
+ if (count != 1 || off != 0)
+ return -EFAULT;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_reset_select_slave(sl))
+ goto error;
+
+ while (retries--) {
+ w1_buf[0] = W1_F29_FUNC_WRITE_COND_SEARCH_REG;
+ w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS;
+ w1_buf[2] = 0;
+ w1_buf[3] = *buf;
+
+ w1_write_block(sl->master, w1_buf, 4);
+ if (w1_reset_resume_command(sl->master))
+ goto error;
+
+ w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
+ w1_buf[1] = W1_F29_REG_CONTROL_AND_STATUS;
+ w1_buf[2] = 0;
+
+ w1_write_block(sl->master, w1_buf, 3);
+ if (w1_read_8(sl->master) == *buf) {
+ /* success! */
+ mutex_unlock(&sl->master->mutex);
+ return 1;
+ }
+ }
+error:
+ mutex_unlock(&sl->master->mutex);
+
+ return -EIO;
+}
+
+
+
+#define NB_SYSFS_BIN_FILES 6
+static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
+ {
+ .attr = {
+ .name = "state",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_state,
+ },
+ {
+ .attr = {
+ .name = "output",
+ .mode = S_IRUGO | S_IWUSR | S_IWGRP,
+ },
+ .size = 1,
+ .read = w1_f29_read_output,
+ .write = w1_f29_write_output,
+ },
+ {
+ .attr = {
+ .name = "activity",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_activity,
+ .write = w1_f29_write_activity,
+ },
+ {
+ .attr = {
+ .name = "cond_search_mask",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_cond_search_mask,
+ .write = 0,
+ },
+ {
+ .attr = {
+ .name = "cond_search_polarity",
+ .mode = S_IRUGO,
+ },
+ .size = 1,
+ .read = w1_f29_read_cond_search_polarity,
+ .write = 0,
+ },
+ {
+ .attr = {
+ .name = "status_control",
+ .mode = S_IRUGO | S_IWUSR | S_IWGRP,
+ },
+ .size = 1,
+ .read = w1_f29_read_status_control,
+ .write = w1_f29_write_status_control,
+ }
+};
+
+static int w1_f29_add_slave(struct w1_slave *sl)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
+ err = sysfs_create_bin_file(
+ &sl->dev.kobj,
+ &(w1_f29_sysfs_bin_files[i]));
+ if (err)
+ while (--i >= 0)
+ sysfs_remove_bin_file(&sl->dev.kobj,
+ &(w1_f29_sysfs_bin_files[i]));
+ return err;
+}
+
+static void w1_f29_remove_slave(struct w1_slave *sl)
+{
+ int i;
+ for (i = NB_SYSFS_BIN_FILES; i <= 0; --i)
+ sysfs_remove_bin_file(&sl->dev.kobj,
+ &(w1_f29_sysfs_bin_files[i]));
+}
+
+static struct w1_family_ops w1_f29_fops = {
+ .add_slave = w1_f29_add_slave,
+ .remove_slave = w1_f29_remove_slave,
+};
+
+static struct w1_family w1_family_29 = {
+ .fid = W1_FAMILY_DS2408,
+ .fops = &w1_f29_fops,
+};
+
+static int __init w1_f29_init(void)
+{
+ return w1_register_family(&w1_family_29);
+}
+
+static void __exit w1_f29_exit(void)
+{
+ w1_unregister_family(&w1_family_29);
+}
+
+module_init(w1_f29_init);
+module_exit(w1_f29_exit);
diff --git a/drivers/w1/slaves/w1_ds2780.c b/drivers/w1/slaves/w1_ds2780.c
new file mode 100644
index 0000000..274c8f3
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2780.c
@@ -0,0 +1,217 @@
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on w1-ds2760 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/idr.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2780.h"
+
+int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (addr > DS2780_DATA_SIZE || addr < 0) {
+ count = 0;
+ goto out;
+ }
+ count = min_t(int, count, DS2780_DATA_SIZE - addr);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ if (io) {
+ w1_write_8(sl->master, W1_DS2780_WRITE_DATA);
+ w1_write_8(sl->master, addr);
+ w1_write_block(sl->master, buf, count);
+ /* XXX w1_write_block returns void, not n_written */
+ } else {
+ w1_write_8(sl->master, W1_DS2780_READ_DATA);
+ w1_write_8(sl->master, addr);
+ count = w1_read_block(sl->master, buf, count);
+ }
+ }
+
+out:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+EXPORT_SYMBOL(w1_ds2780_io);
+
+int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ if (!dev)
+ return -EINVAL;
+
+ mutex_lock(&sl->master->mutex);
+
+ if (w1_reset_select_slave(sl) == 0) {
+ w1_write_8(sl->master, cmd);
+ w1_write_8(sl->master, addr);
+ }
+
+ mutex_unlock(&sl->master->mutex);
+ return 0;
+}
+EXPORT_SYMBOL(w1_ds2780_eeprom_cmd);
+
+static ssize_t w1_ds2780_read_bin(struct file *filp,
+ struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ return w1_ds2780_io(dev, buf, off, count, 0);
+}
+
+static struct bin_attribute w1_ds2780_bin_attr = {
+ .attr = {
+ .name = "w1_slave",
+ .mode = S_IRUGO,
+ },
+ .size = DS2780_DATA_SIZE,
+ .read = w1_ds2780_read_bin,
+};
+
+static DEFINE_IDR(bat_idr);
+static DEFINE_MUTEX(bat_idr_lock);
+
+static int new_bat_id(void)
+{
+ int ret;
+
+ while (1) {
+ int id;
+
+ ret = idr_pre_get(&bat_idr, GFP_KERNEL);
+ if (ret == 0)
+ return -ENOMEM;
+
+ mutex_lock(&bat_idr_lock);
+ ret = idr_get_new(&bat_idr, NULL, &id);
+ mutex_unlock(&bat_idr_lock);
+
+ if (ret == 0) {
+ ret = id & MAX_ID_MASK;
+ break;
+ } else if (ret == -EAGAIN) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void release_bat_id(int id)
+{
+ mutex_lock(&bat_idr_lock);
+ idr_remove(&bat_idr, id);
+ mutex_unlock(&bat_idr_lock);
+}
+
+static int w1_ds2780_add_slave(struct w1_slave *sl)
+{
+ int ret;
+ int id;
+ struct platform_device *pdev;
+
+ id = new_bat_id();
+ if (id < 0) {
+ ret = id;
+ goto noid;
+ }
+
+ pdev = platform_device_alloc("ds2780-battery", id);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto pdev_alloc_failed;
+ }
+ pdev->dev.parent = &sl->dev;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto pdev_add_failed;
+
+ ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
+ if (ret)
+ goto bin_attr_failed;
+
+ dev_set_drvdata(&sl->dev, pdev);
+
+ return 0;
+
+bin_attr_failed:
+pdev_add_failed:
+ platform_device_unregister(pdev);
+pdev_alloc_failed:
+ release_bat_id(id);
+noid:
+ return ret;
+}
+
+static void w1_ds2780_remove_slave(struct w1_slave *sl)
+{
+ struct platform_device *pdev = dev_get_drvdata(&sl->dev);
+ int id = pdev->id;
+
+ platform_device_unregister(pdev);
+ release_bat_id(id);
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2780_bin_attr);
+}
+
+static struct w1_family_ops w1_ds2780_fops = {
+ .add_slave = w1_ds2780_add_slave,
+ .remove_slave = w1_ds2780_remove_slave,
+};
+
+static struct w1_family w1_ds2780_family = {
+ .fid = W1_FAMILY_DS2780,
+ .fops = &w1_ds2780_fops,
+};
+
+static int __init w1_ds2780_init(void)
+{
+ idr_init(&bat_idr);
+ return w1_register_family(&w1_ds2780_family);
+}
+
+static void __exit w1_ds2780_exit(void)
+{
+ w1_unregister_family(&w1_ds2780_family);
+ idr_destroy(&bat_idr);
+}
+
+module_init(w1_ds2780_init);
+module_exit(w1_ds2780_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
+MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC");
diff --git a/drivers/w1/slaves/w1_ds2780.h b/drivers/w1/slaves/w1_ds2780.h
new file mode 100644
index 0000000..a1fba79
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2780.h
@@ -0,0 +1,129 @@
+/*
+ * 1-Wire implementation for the ds2780 chip
+ *
+ * Copyright (C) 2010 Indesign, LLC
+ *
+ * Author: Clifton Barnes <cabarnes@indesign-llc.com>
+ *
+ * Based on w1-ds2760 driver
+ *
+ * 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 _W1_DS2780_H
+#define _W1_DS2780_H
+
+/* Function commands */
+#define W1_DS2780_READ_DATA 0x69
+#define W1_DS2780_WRITE_DATA 0x6C
+#define W1_DS2780_COPY_DATA 0x48
+#define W1_DS2780_RECALL_DATA 0xB8
+#define W1_DS2780_LOCK 0x6A
+
+/* Register map */
+/* Register 0x00 Reserved */
+#define DS2780_STATUS_REG 0x01
+#define DS2780_RAAC_MSB_REG 0x02
+#define DS2780_RAAC_LSB_REG 0x03
+#define DS2780_RSAC_MSB_REG 0x04
+#define DS2780_RSAC_LSB_REG 0x05
+#define DS2780_RARC_REG 0x06
+#define DS2780_RSRC_REG 0x07
+#define DS2780_IAVG_MSB_REG 0x08
+#define DS2780_IAVG_LSB_REG 0x09
+#define DS2780_TEMP_MSB_REG 0x0A
+#define DS2780_TEMP_LSB_REG 0x0B
+#define DS2780_VOLT_MSB_REG 0x0C
+#define DS2780_VOLT_LSB_REG 0x0D
+#define DS2780_CURRENT_MSB_REG 0x0E
+#define DS2780_CURRENT_LSB_REG 0x0F
+#define DS2780_ACR_MSB_REG 0x10
+#define DS2780_ACR_LSB_REG 0x11
+#define DS2780_ACRL_MSB_REG 0x12
+#define DS2780_ACRL_LSB_REG 0x13
+#define DS2780_AS_REG 0x14
+#define DS2780_SFR_REG 0x15
+#define DS2780_FULL_MSB_REG 0x16
+#define DS2780_FULL_LSB_REG 0x17
+#define DS2780_AE_MSB_REG 0x18
+#define DS2780_AE_LSB_REG 0x19
+#define DS2780_SE_MSB_REG 0x1A
+#define DS2780_SE_LSB_REG 0x1B
+/* Register 0x1C - 0x1E Reserved */
+#define DS2780_EEPROM_REG 0x1F
+#define DS2780_EEPROM_BLOCK0_START 0x20
+/* Register 0x20 - 0x2F User EEPROM */
+#define DS2780_EEPROM_BLOCK0_END 0x2F
+/* Register 0x30 - 0x5F Reserved */
+#define DS2780_EEPROM_BLOCK1_START 0x60
+#define DS2780_CONTROL_REG 0x60
+#define DS2780_AB_REG 0x61
+#define DS2780_AC_MSB_REG 0x62
+#define DS2780_AC_LSB_REG 0x63
+#define DS2780_VCHG_REG 0x64
+#define DS2780_IMIN_REG 0x65
+#define DS2780_VAE_REG 0x66
+#define DS2780_IAE_REG 0x67
+#define DS2780_AE_40_REG 0x68
+#define DS2780_RSNSP_REG 0x69
+#define DS2780_FULL_40_MSB_REG 0x6A
+#define DS2780_FULL_40_LSB_REG 0x6B
+#define DS2780_FULL_3040_SLOPE_REG 0x6C
+#define DS2780_FULL_2030_SLOPE_REG 0x6D
+#define DS2780_FULL_1020_SLOPE_REG 0x6E
+#define DS2780_FULL_0010_SLOPE_REG 0x6F
+#define DS2780_AE_3040_SLOPE_REG 0x70
+#define DS2780_AE_2030_SLOPE_REG 0x71
+#define DS2780_AE_1020_SLOPE_REG 0x72
+#define DS2780_AE_0010_SLOPE_REG 0x73
+#define DS2780_SE_3040_SLOPE_REG 0x74
+#define DS2780_SE_2030_SLOPE_REG 0x75
+#define DS2780_SE_1020_SLOPE_REG 0x76
+#define DS2780_SE_0010_SLOPE_REG 0x77
+#define DS2780_RSGAIN_MSB_REG 0x78
+#define DS2780_RSGAIN_LSB_REG 0x79
+#define DS2780_RSTC_REG 0x7A
+#define DS2780_FRSGAIN_MSB_REG 0x7B
+#define DS2780_FRSGAIN_LSB_REG 0x7C
+#define DS2780_EEPROM_BLOCK1_END 0x7C
+/* Register 0x7D - 0xFF Reserved */
+
+/* Number of valid register addresses */
+#define DS2780_DATA_SIZE 0x80
+
+/* Status register bits */
+#define DS2780_STATUS_REG_CHGTF (1 << 7)
+#define DS2780_STATUS_REG_AEF (1 << 6)
+#define DS2780_STATUS_REG_SEF (1 << 5)
+#define DS2780_STATUS_REG_LEARNF (1 << 4)
+/* Bit 3 Reserved */
+#define DS2780_STATUS_REG_UVF (1 << 2)
+#define DS2780_STATUS_REG_PORF (1 << 1)
+/* Bit 0 Reserved */
+
+/* Control register bits */
+/* Bit 7 Reserved */
+#define DS2780_CONTROL_REG_UVEN (1 << 6)
+#define DS2780_CONTROL_REG_PMOD (1 << 5)
+#define DS2780_CONTROL_REG_RNAOP (1 << 4)
+/* Bit 0 - 3 Reserved */
+
+/* Special feature register bits */
+/* Bit 1 - 7 Reserved */
+#define DS2780_SFR_REG_PIOSC (1 << 0)
+
+/* EEPROM register bits */
+#define DS2780_EEPROM_REG_EEC (1 << 7)
+#define DS2780_EEPROM_REG_LOCK (1 << 6)
+/* Bit 2 - 6 Reserved */
+#define DS2780_EEPROM_REG_BL1 (1 << 1)
+#define DS2780_EEPROM_REG_BL0 (1 << 0)
+
+extern int w1_ds2780_io(struct device *dev, char *buf, int addr, size_t count,
+ int io);
+extern int w1_ds2780_eeprom_cmd(struct device *dev, int addr, int cmd);
+
+#endif /* !_W1_DS2780_H */
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index b7b5014..10606c8 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -827,7 +827,7 @@
mutex_unlock(&w1_mlock);
}
-static void w1_slave_found(struct w1_master *dev, u64 rn)
+void w1_slave_found(struct w1_master *dev, u64 rn)
{
struct w1_slave *sl;
struct w1_reg_num *tmp;
@@ -933,14 +933,15 @@
}
}
-void w1_search_process(struct w1_master *dev, u8 search_type)
+void w1_search_process_cb(struct w1_master *dev, u8 search_type,
+ w1_slave_found_callback cb)
{
struct w1_slave *sl, *sln;
list_for_each_entry(sl, &dev->slist, w1_slave_entry)
clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
- w1_search_devices(dev, search_type, w1_slave_found);
+ w1_search_devices(dev, search_type, cb);
list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl)
@@ -953,6 +954,11 @@
dev->search_count--;
}
+static void w1_search_process(struct w1_master *dev, u8 search_type)
+{
+ w1_search_process_cb(dev, search_type, w1_slave_found);
+}
+
int w1_process(void *data)
{
struct w1_master *dev = (struct w1_master *) data;
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h
index d8a9709..1ce23fc 100644
--- a/drivers/w1/w1.h
+++ b/drivers/w1/w1.h
@@ -55,6 +55,7 @@
#define W1_READ_ROM 0x33
#define W1_READ_PSUPPLY 0xB4
#define W1_MATCH_ROM 0x55
+#define W1_RESUME_CMD 0xA5
#define W1_SLAVE_ACTIVE 0
@@ -193,7 +194,9 @@
void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);
void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb);
struct w1_slave *w1_search_slave(struct w1_reg_num *id);
-void w1_search_process(struct w1_master *dev, u8 search_type);
+void w1_slave_found(struct w1_master *dev, u64 rn);
+void w1_search_process_cb(struct w1_master *dev, u8 search_type,
+ w1_slave_found_callback cb);
struct w1_master *w1_search_master_id(u32 id);
/* Disconnect and reconnect devices in the given family. Used for finding
@@ -213,6 +216,7 @@
void w1_touch_block(struct w1_master *, u8 *, int);
u8 w1_read_block(struct w1_master *, u8 *, int);
int w1_reset_select_slave(struct w1_slave *sl);
+int w1_reset_resume_command(struct w1_master *);
void w1_next_pullup(struct w1_master *, int);
static inline struct w1_slave* dev_to_w1_slave(struct device *dev)
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index f3b636d..97479ae 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -34,8 +34,10 @@
#define W1_THERM_DS1822 0x22
#define W1_EEPROM_DS2433 0x23
#define W1_THERM_DS18B20 0x28
+#define W1_FAMILY_DS2408 0x29
#define W1_EEPROM_DS2431 0x2D
#define W1_FAMILY_DS2760 0x30
+#define W1_FAMILY_DS2780 0x32
#define MAXNAMELEN 32
diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
index 3ebe972..8e8b64c 100644
--- a/drivers/w1/w1_io.c
+++ b/drivers/w1/w1_io.c
@@ -390,6 +390,32 @@
EXPORT_SYMBOL_GPL(w1_reset_select_slave);
/**
+ * When the workflow with a slave amongst many requires several
+ * successive commands a reset between each, this function is similar
+ * to doing a reset then a match ROM for the last matched ROM. The
+ * advantage being that the matched ROM step is skipped in favor of the
+ * resume command. The slave must support the command of course.
+ *
+ * If the bus has only one slave, traditionnaly the match ROM is skipped
+ * and a "SKIP ROM" is done for efficiency. On multi-slave busses, this
+ * doesn't work of course, but the resume command is the next best thing.
+ *
+ * The w1 master lock must be held.
+ *
+ * @param dev the master device
+ */
+int w1_reset_resume_command(struct w1_master *dev)
+{
+ if (w1_reset_bus(dev))
+ return -1;
+
+ /* This will make only the last matched slave perform a skip ROM. */
+ w1_write_8(dev, W1_RESUME_CMD);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(w1_reset_resume_command);
+
+/**
* Put out a strong pull-up of the specified duration after the next write
* operation. Not all hardware supports strong pullups. Hardware that
* doesn't support strong pullups will sleep for the given time after the
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c
index 7e667bc..55aabd9 100644
--- a/drivers/w1/w1_netlink.c
+++ b/drivers/w1/w1_netlink.c
@@ -55,6 +55,9 @@
struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1);
int avail;
+ /* update kernel slave list */
+ w1_slave_found(dev, rn);
+
avail = dev->priv_size - cmd->len;
if (avail > 8) {
@@ -85,7 +88,7 @@
dev->priv = msg;
dev->priv_size = avail;
- w1_search_devices(dev, search_type, w1_send_slave);
+ w1_search_process_cb(dev, search_type, w1_send_slave);
msg->ack = 0;
cn_netlink_send(msg, 0, GFP_KERNEL);
diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c
index d8e7250..428f8a1 100644
--- a/drivers/watchdog/rdc321x_wdt.c
+++ b/drivers/watchdog/rdc321x_wdt.c
@@ -37,7 +37,6 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/mfd/rdc321x.h>
-#include <linux/mfd/core.h>
#define RDC_WDT_MASK 0x80000000 /* Mask */
#define RDC_WDT_EN 0x00800000 /* Enable bit */
@@ -232,7 +231,7 @@
struct resource *r;
struct rdc321x_wdt_pdata *pdata;
- pdata = mfd_get_data(pdev);
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data supplied\n");
return -ENODEV;
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 4781f80..bbc1825 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,5 +1,6 @@
obj-y += grant-table.o features.o events.o manage.o balloon.o
obj-y += xenbus/
+obj-y += tmem.o
nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_features.o := $(nostackp)
diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c
new file mode 100644
index 0000000..816a449
--- /dev/null
+++ b/drivers/xen/tmem.c
@@ -0,0 +1,264 @@
+/*
+ * Xen implementation for transcendent memory (tmem)
+ *
+ * Copyright (C) 2009-2010 Oracle Corp. All rights reserved.
+ * Author: Dan Magenheimer
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/cleancache.h>
+
+#include <xen/xen.h>
+#include <xen/interface/xen.h>
+#include <asm/xen/hypercall.h>
+#include <asm/xen/page.h>
+#include <asm/xen/hypervisor.h>
+
+#define TMEM_CONTROL 0
+#define TMEM_NEW_POOL 1
+#define TMEM_DESTROY_POOL 2
+#define TMEM_NEW_PAGE 3
+#define TMEM_PUT_PAGE 4
+#define TMEM_GET_PAGE 5
+#define TMEM_FLUSH_PAGE 6
+#define TMEM_FLUSH_OBJECT 7
+#define TMEM_READ 8
+#define TMEM_WRITE 9
+#define TMEM_XCHG 10
+
+/* Bits for HYPERVISOR_tmem_op(TMEM_NEW_POOL) */
+#define TMEM_POOL_PERSIST 1
+#define TMEM_POOL_SHARED 2
+#define TMEM_POOL_PAGESIZE_SHIFT 4
+#define TMEM_VERSION_SHIFT 24
+
+
+struct tmem_pool_uuid {
+ u64 uuid_lo;
+ u64 uuid_hi;
+};
+
+struct tmem_oid {
+ u64 oid[3];
+};
+
+#define TMEM_POOL_PRIVATE_UUID { 0, 0 }
+
+/* flags for tmem_ops.new_pool */
+#define TMEM_POOL_PERSIST 1
+#define TMEM_POOL_SHARED 2
+
+/* xen tmem foundation ops/hypercalls */
+
+static inline int xen_tmem_op(u32 tmem_cmd, u32 tmem_pool, struct tmem_oid oid,
+ u32 index, unsigned long gmfn, u32 tmem_offset, u32 pfn_offset, u32 len)
+{
+ struct tmem_op op;
+ int rc = 0;
+
+ op.cmd = tmem_cmd;
+ op.pool_id = tmem_pool;
+ op.u.gen.oid[0] = oid.oid[0];
+ op.u.gen.oid[1] = oid.oid[1];
+ op.u.gen.oid[2] = oid.oid[2];
+ op.u.gen.index = index;
+ op.u.gen.tmem_offset = tmem_offset;
+ op.u.gen.pfn_offset = pfn_offset;
+ op.u.gen.len = len;
+ set_xen_guest_handle(op.u.gen.gmfn, (void *)gmfn);
+ rc = HYPERVISOR_tmem_op(&op);
+ return rc;
+}
+
+static int xen_tmem_new_pool(struct tmem_pool_uuid uuid,
+ u32 flags, unsigned long pagesize)
+{
+ struct tmem_op op;
+ int rc = 0, pageshift;
+
+ for (pageshift = 0; pagesize != 1; pageshift++)
+ pagesize >>= 1;
+ flags |= (pageshift - 12) << TMEM_POOL_PAGESIZE_SHIFT;
+ flags |= TMEM_SPEC_VERSION << TMEM_VERSION_SHIFT;
+ op.cmd = TMEM_NEW_POOL;
+ op.u.new.uuid[0] = uuid.uuid_lo;
+ op.u.new.uuid[1] = uuid.uuid_hi;
+ op.u.new.flags = flags;
+ rc = HYPERVISOR_tmem_op(&op);
+ return rc;
+}
+
+/* xen generic tmem ops */
+
+static int xen_tmem_put_page(u32 pool_id, struct tmem_oid oid,
+ u32 index, unsigned long pfn)
+{
+ unsigned long gmfn = xen_pv_domain() ? pfn_to_mfn(pfn) : pfn;
+
+ return xen_tmem_op(TMEM_PUT_PAGE, pool_id, oid, index,
+ gmfn, 0, 0, 0);
+}
+
+static int xen_tmem_get_page(u32 pool_id, struct tmem_oid oid,
+ u32 index, unsigned long pfn)
+{
+ unsigned long gmfn = xen_pv_domain() ? pfn_to_mfn(pfn) : pfn;
+
+ return xen_tmem_op(TMEM_GET_PAGE, pool_id, oid, index,
+ gmfn, 0, 0, 0);
+}
+
+static int xen_tmem_flush_page(u32 pool_id, struct tmem_oid oid, u32 index)
+{
+ return xen_tmem_op(TMEM_FLUSH_PAGE, pool_id, oid, index,
+ 0, 0, 0, 0);
+}
+
+static int xen_tmem_flush_object(u32 pool_id, struct tmem_oid oid)
+{
+ return xen_tmem_op(TMEM_FLUSH_OBJECT, pool_id, oid, 0, 0, 0, 0, 0);
+}
+
+static int xen_tmem_destroy_pool(u32 pool_id)
+{
+ struct tmem_oid oid = { { 0 } };
+
+ return xen_tmem_op(TMEM_DESTROY_POOL, pool_id, oid, 0, 0, 0, 0, 0);
+}
+
+int tmem_enabled;
+
+static int __init enable_tmem(char *s)
+{
+ tmem_enabled = 1;
+ return 1;
+}
+
+__setup("tmem", enable_tmem);
+
+/* cleancache ops */
+
+static void tmem_cleancache_put_page(int pool, struct cleancache_filekey key,
+ pgoff_t index, struct page *page)
+{
+ u32 ind = (u32) index;
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+ unsigned long pfn = page_to_pfn(page);
+
+ if (pool < 0)
+ return;
+ if (ind != index)
+ return;
+ mb(); /* ensure page is quiescent; tmem may address it with an alias */
+ (void)xen_tmem_put_page((u32)pool, oid, ind, pfn);
+}
+
+static int tmem_cleancache_get_page(int pool, struct cleancache_filekey key,
+ pgoff_t index, struct page *page)
+{
+ u32 ind = (u32) index;
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+ unsigned long pfn = page_to_pfn(page);
+ int ret;
+
+ /* translate return values to linux semantics */
+ if (pool < 0)
+ return -1;
+ if (ind != index)
+ return -1;
+ ret = xen_tmem_get_page((u32)pool, oid, ind, pfn);
+ if (ret == 1)
+ return 0;
+ else
+ return -1;
+}
+
+static void tmem_cleancache_flush_page(int pool, struct cleancache_filekey key,
+ pgoff_t index)
+{
+ u32 ind = (u32) index;
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+ if (pool < 0)
+ return;
+ if (ind != index)
+ return;
+ (void)xen_tmem_flush_page((u32)pool, oid, ind);
+}
+
+static void tmem_cleancache_flush_inode(int pool, struct cleancache_filekey key)
+{
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+ if (pool < 0)
+ return;
+ (void)xen_tmem_flush_object((u32)pool, oid);
+}
+
+static void tmem_cleancache_flush_fs(int pool)
+{
+ if (pool < 0)
+ return;
+ (void)xen_tmem_destroy_pool((u32)pool);
+}
+
+static int tmem_cleancache_init_fs(size_t pagesize)
+{
+ struct tmem_pool_uuid uuid_private = TMEM_POOL_PRIVATE_UUID;
+
+ return xen_tmem_new_pool(uuid_private, 0, pagesize);
+}
+
+static int tmem_cleancache_init_shared_fs(char *uuid, size_t pagesize)
+{
+ struct tmem_pool_uuid shared_uuid;
+
+ shared_uuid.uuid_lo = *(u64 *)uuid;
+ shared_uuid.uuid_hi = *(u64 *)(&uuid[8]);
+ return xen_tmem_new_pool(shared_uuid, TMEM_POOL_SHARED, pagesize);
+}
+
+static int use_cleancache = 1;
+
+static int __init no_cleancache(char *s)
+{
+ use_cleancache = 0;
+ return 1;
+}
+
+__setup("nocleancache", no_cleancache);
+
+static struct cleancache_ops tmem_cleancache_ops = {
+ .put_page = tmem_cleancache_put_page,
+ .get_page = tmem_cleancache_get_page,
+ .flush_page = tmem_cleancache_flush_page,
+ .flush_inode = tmem_cleancache_flush_inode,
+ .flush_fs = tmem_cleancache_flush_fs,
+ .init_shared_fs = tmem_cleancache_init_shared_fs,
+ .init_fs = tmem_cleancache_init_fs
+};
+
+static int __init xen_tmem_init(void)
+{
+ struct cleancache_ops old_ops;
+
+ if (!xen_domain())
+ return 0;
+#ifdef CONFIG_CLEANCACHE
+ BUG_ON(sizeof(struct cleancache_filekey) != sizeof(struct tmem_oid));
+ if (tmem_enabled && use_cleancache) {
+ char *s = "";
+ old_ops = cleancache_register_ops(&tmem_cleancache_ops);
+ if (old_ops.init_fs != NULL)
+ s = " (WARNING: cleancache_ops overridden)";
+ printk(KERN_INFO "cleancache enabled, RAM provided by "
+ "Xen Transcendent Memory%s\n", s);
+ }
+#endif
+ return 0;
+}
+
+module_init(xen_tmem_init)
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 7f6c677..8d7f3e6 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -814,6 +814,7 @@
int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
{
+ dentry_unhash(d);
return v9fs_remove(i, d, 1);
}
@@ -839,6 +840,9 @@
struct p9_fid *newdirfid;
struct p9_wstat wstat;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
P9_DPRINTK(P9_DEBUG_VFS, "\n");
retval = 0;
old_inode = old_dentry->d_inode;
diff --git a/fs/Kconfig b/fs/Kconfig
index f6edba2..19891aa 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -47,7 +47,7 @@
def_bool n
config EXPORTFS
- bool
+ tristate
config FILE_LOCKING
bool "Enable POSIX file locking API" if EXPERT
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index e3e9efc..03330e2 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -320,6 +320,8 @@
dentry->d_inode->i_ino,
(int)dentry->d_name.len, dentry->d_name.name);
+ dentry_unhash(dentry);
+
return affs_remove_header(dentry);
}
@@ -417,6 +419,9 @@
struct buffer_head *bh = NULL;
int retval;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
(u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
(u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 20c106f..2c4e051 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -845,6 +845,8 @@
_enter("{%x:%u},{%s}",
dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name);
+ dentry_unhash(dentry);
+
ret = -ENAMETOOLONG;
if (dentry->d_name.len >= AFSNAMEMAX)
goto error;
@@ -1146,6 +1148,9 @@
struct key *key;
int ret;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
vnode = AFS_FS_I(old_dentry->d_inode);
orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir);
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index f55ae23..87d95a8 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -583,6 +583,8 @@
if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
return -EACCES;
+ dentry_unhash(dentry);
+
if (atomic_dec_and_test(&ino->count)) {
p_ino = autofs4_dentry_ino(dentry->d_parent);
if (p_ino && dentry->d_parent != dentry)
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index b14cebf..c7d1d06 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -224,6 +224,9 @@
struct bfs_sb_info *info;
int error = -ENOENT;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_bh = new_bh = NULL;
old_inode = old_dentry->d_inode;
if (S_ISDIR(old_inode->i_mode))
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 96fcfa5..4f98932 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -11,6 +11,7 @@
#include <linux/writeback.h>
#include <linux/pagevec.h>
#include <linux/prefetch.h>
+#include <linux/cleancache.h>
#include "extent_io.h"
#include "extent_map.h"
#include "compat.h"
@@ -2016,6 +2017,13 @@
set_page_extent_mapped(page);
+ if (!PageUptodate(page)) {
+ if (cleancache_get_page(page) == 0) {
+ BUG_ON(blocksize != PAGE_SIZE);
+ goto out;
+ }
+ }
+
end = page_end;
while (1) {
lock_extent(tree, start, end, GFP_NOFS);
@@ -2149,6 +2157,7 @@
cur = cur + iosize;
page_offset += iosize;
}
+out:
if (!nr) {
if (!PageError(page))
SetPageUptodate(page);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 0ac712e..be4ffa1 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -39,6 +39,7 @@
#include <linux/miscdevice.h>
#include <linux/magic.h>
#include <linux/slab.h>
+#include <linux/cleancache.h>
#include "compat.h"
#include "ctree.h"
#include "disk-io.h"
@@ -624,6 +625,7 @@
sb->s_root = root_dentry;
save_mount_options(sb, data);
+ cleancache_init_fs(sb);
return 0;
fail_close:
diff --git a/fs/buffer.c b/fs/buffer.c
index a08bb8e..698c6b2 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -41,6 +41,7 @@
#include <linux/bitops.h>
#include <linux/mpage.h>
#include <linux/bit_spinlock.h>
+#include <linux/cleancache.h>
static int fsync_buffers_list(spinlock_t *lock, struct list_head *list);
@@ -269,6 +270,10 @@
invalidate_bh_lrus();
lru_add_drain_all(); /* make sure all lru add caches are flushed */
invalidate_mapping_pages(mapping, 0, -1);
+ /* 99% of the time, we don't need to flush the cleancache on the bdev.
+ * But, for the strange corners, lets be cautious
+ */
+ cleancache_flush_inode(mapping);
}
EXPORT_SYMBOL(invalidate_bdev);
@@ -2331,24 +2336,26 @@
* page lock we can determine safely if the page is beyond EOF. If it is not
* beyond EOF, then the page is guaranteed safe against truncation until we
* unlock the page.
+ *
+ * Direct callers of this function should call vfs_check_frozen() so that page
+ * fault does not busyloop until the fs is thawed.
*/
-int
-block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
- get_block_t get_block)
+int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+ get_block_t get_block)
{
struct page *page = vmf->page;
struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
unsigned long end;
loff_t size;
- int ret = VM_FAULT_NOPAGE; /* make the VM retry the fault */
+ int ret;
lock_page(page);
size = i_size_read(inode);
if ((page->mapping != inode->i_mapping) ||
(page_offset(page) > size)) {
- /* page got truncated out from underneath us */
- unlock_page(page);
- goto out;
+ /* We overload EFAULT to mean page got truncated */
+ ret = -EFAULT;
+ goto out_unlock;
}
/* page is wholly or partially inside EOF */
@@ -2361,18 +2368,41 @@
if (!ret)
ret = block_commit_write(page, 0, end);
- if (unlikely(ret)) {
- unlock_page(page);
- if (ret == -ENOMEM)
- ret = VM_FAULT_OOM;
- else /* -ENOSPC, -EIO, etc */
- ret = VM_FAULT_SIGBUS;
- } else
- ret = VM_FAULT_LOCKED;
-
-out:
+ if (unlikely(ret < 0))
+ goto out_unlock;
+ /*
+ * Freezing in progress? We check after the page is marked dirty and
+ * with page lock held so if the test here fails, we are sure freezing
+ * code will wait during syncing until the page fault is done - at that
+ * point page will be dirty and unlocked so freezing code will write it
+ * and writeprotect it again.
+ */
+ set_page_dirty(page);
+ if (inode->i_sb->s_frozen != SB_UNFROZEN) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+ return 0;
+out_unlock:
+ unlock_page(page);
return ret;
}
+EXPORT_SYMBOL(__block_page_mkwrite);
+
+int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+ get_block_t get_block)
+{
+ int ret;
+ struct super_block *sb = vma->vm_file->f_path.dentry->d_inode->i_sb;
+
+ /*
+ * This check is racy but catches the common case. The check in
+ * __block_page_mkwrite() is reliable.
+ */
+ vfs_check_frozen(sb, SB_FREEZE_WRITE);
+ ret = __block_page_mkwrite(vma, vmf, get_block);
+ return block_page_mkwrite_return(ret);
+}
EXPORT_SYMBOL(block_page_mkwrite);
/*
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 2b8dae4..a46126f 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -336,6 +336,8 @@
int len = de->d_name.len;
int error;
+ dentry_unhash(de);
+
error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
if (!error) {
/* VFS may delete the child */
@@ -359,6 +361,9 @@
int new_length = new_dentry->d_name.len;
int error;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
coda_i2f(new_dir), old_length, new_length,
(const char *) old_name, (const char *)new_name);
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 9a37a9b..9d17d35 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -1359,6 +1359,8 @@
struct module *subsys_owner = NULL, *dead_item_owner = NULL;
int ret;
+ dentry_unhash(dentry);
+
if (dentry->d_parent == configfs_sb->s_root)
return -EPERM;
diff --git a/fs/dlm/main.c b/fs/dlm/main.c
index b80e0aa..5a59efa 100644
--- a/fs/dlm/main.c
+++ b/fs/dlm/main.c
@@ -50,7 +50,7 @@
if (error)
goto out_netlink;
- printk("DLM (built %s %s) installed\n", __DATE__, __TIME__);
+ printk("DLM installed\n");
return 0;
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index 4d4cc6a..227b409 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -521,6 +521,8 @@
struct dentry *lower_dir_dentry;
int rc;
+ dentry_unhash(dentry);
+
lower_dentry = ecryptfs_dentry_to_lower(dentry);
dget(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
@@ -571,6 +573,9 @@
struct dentry *lower_new_dir_dentry;
struct dentry *trap = NULL;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
dget(lower_old_dentry);
diff --git a/fs/exec.c b/fs/exec.c
index 936f577..ea5f748 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -42,7 +42,6 @@
#include <linux/pid_namespace.h>
#include <linux/module.h>
#include <linux/namei.h>
-#include <linux/proc_fs.h>
#include <linux/mount.h>
#include <linux/security.h>
#include <linux/syscalls.h>
@@ -1624,6 +1623,41 @@
return ret;
}
+static int cn_print_exe_file(struct core_name *cn)
+{
+ struct file *exe_file;
+ char *pathbuf, *path, *p;
+ int ret;
+
+ exe_file = get_mm_exe_file(current->mm);
+ if (!exe_file)
+ return cn_printf(cn, "(unknown)");
+
+ pathbuf = kmalloc(PATH_MAX, GFP_TEMPORARY);
+ if (!pathbuf) {
+ ret = -ENOMEM;
+ goto put_exe_file;
+ }
+
+ path = d_path(&exe_file->f_path, pathbuf, PATH_MAX);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ goto free_buf;
+ }
+
+ for (p = path; *p; p++)
+ if (*p == '/')
+ *p = '!';
+
+ ret = cn_printf(cn, "%s", path);
+
+free_buf:
+ kfree(pathbuf);
+put_exe_file:
+ fput(exe_file);
+ return ret;
+}
+
/* format_corename will inspect the pattern parameter, and output a
* name into corename, which must have space for at least
* CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
@@ -1695,6 +1729,9 @@
case 'e':
err = cn_printf(cn, "%s", current->comm);
break;
+ case 'E':
+ err = cn_print_exe_file(cn);
+ break;
/* core limit size */
case 'c':
err = cn_printf(cn, "%lu",
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 3c6a9e0..aad153e 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -36,6 +36,7 @@
#include <linux/quotaops.h>
#include <linux/seq_file.h>
#include <linux/log2.h>
+#include <linux/cleancache.h>
#include <asm/uaccess.h>
@@ -1367,6 +1368,7 @@
} else {
ext3_msg(sb, KERN_INFO, "using internal journal");
}
+ cleancache_init_fs(sb);
return res;
}
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index c947e36..04109460 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -6,7 +6,8 @@
ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
- ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o
+ ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
+ mmp.o
ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 1c67139..264f694 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -362,130 +362,6 @@
}
/**
- * ext4_add_groupblocks() -- Add given blocks to an existing group
- * @handle: handle to this transaction
- * @sb: super block
- * @block: start physcial block to add to the block group
- * @count: number of blocks to free
- *
- * This marks the blocks as free in the bitmap. We ask the
- * mballoc to reload the buddy after this by setting group
- * EXT4_GROUP_INFO_NEED_INIT_BIT flag
- */
-void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
- ext4_fsblk_t block, unsigned long count)
-{
- struct buffer_head *bitmap_bh = NULL;
- struct buffer_head *gd_bh;
- ext4_group_t block_group;
- ext4_grpblk_t bit;
- unsigned int i;
- struct ext4_group_desc *desc;
- struct ext4_sb_info *sbi = EXT4_SB(sb);
- int err = 0, ret, blk_free_count;
- ext4_grpblk_t blocks_freed;
- struct ext4_group_info *grp;
-
- ext4_debug("Adding block(s) %llu-%llu\n", block, block + count - 1);
-
- ext4_get_group_no_and_offset(sb, block, &block_group, &bit);
- grp = ext4_get_group_info(sb, block_group);
- /*
- * Check to see if we are freeing blocks across a group
- * boundary.
- */
- if (bit + count > EXT4_BLOCKS_PER_GROUP(sb)) {
- goto error_return;
- }
- bitmap_bh = ext4_read_block_bitmap(sb, block_group);
- if (!bitmap_bh)
- goto error_return;
- desc = ext4_get_group_desc(sb, block_group, &gd_bh);
- if (!desc)
- goto error_return;
-
- if (in_range(ext4_block_bitmap(sb, desc), block, count) ||
- in_range(ext4_inode_bitmap(sb, desc), block, count) ||
- in_range(block, ext4_inode_table(sb, desc), sbi->s_itb_per_group) ||
- in_range(block + count - 1, ext4_inode_table(sb, desc),
- sbi->s_itb_per_group)) {
- ext4_error(sb, "Adding blocks in system zones - "
- "Block = %llu, count = %lu",
- block, count);
- goto error_return;
- }
-
- /*
- * We are about to add blocks to the bitmap,
- * so we need undo access.
- */
- BUFFER_TRACE(bitmap_bh, "getting undo access");
- err = ext4_journal_get_undo_access(handle, bitmap_bh);
- if (err)
- goto error_return;
-
- /*
- * We are about to modify some metadata. Call the journal APIs
- * to unshare ->b_data if a currently-committing transaction is
- * using it
- */
- BUFFER_TRACE(gd_bh, "get_write_access");
- err = ext4_journal_get_write_access(handle, gd_bh);
- if (err)
- goto error_return;
- /*
- * make sure we don't allow a parallel init on other groups in the
- * same buddy cache
- */
- down_write(&grp->alloc_sem);
- for (i = 0, blocks_freed = 0; i < count; i++) {
- BUFFER_TRACE(bitmap_bh, "clear bit");
- if (!ext4_clear_bit_atomic(ext4_group_lock_ptr(sb, block_group),
- bit + i, bitmap_bh->b_data)) {
- ext4_error(sb, "bit already cleared for block %llu",
- (ext4_fsblk_t)(block + i));
- BUFFER_TRACE(bitmap_bh, "bit already cleared");
- } else {
- blocks_freed++;
- }
- }
- ext4_lock_group(sb, block_group);
- blk_free_count = blocks_freed + ext4_free_blks_count(sb, desc);
- ext4_free_blks_set(sb, desc, blk_free_count);
- desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc);
- ext4_unlock_group(sb, block_group);
- percpu_counter_add(&sbi->s_freeblocks_counter, blocks_freed);
-
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t flex_group = ext4_flex_group(sbi, block_group);
- atomic_add(blocks_freed,
- &sbi->s_flex_groups[flex_group].free_blocks);
- }
- /*
- * request to reload the buddy with the
- * new bitmap information
- */
- set_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &(grp->bb_state));
- grp->bb_free += blocks_freed;
- up_write(&grp->alloc_sem);
-
- /* We dirtied the bitmap block */
- BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
- err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
-
- /* And the group descriptor block */
- BUFFER_TRACE(gd_bh, "dirtied group descriptor block");
- ret = ext4_handle_dirty_metadata(handle, NULL, gd_bh);
- if (!err)
- err = ret;
-
-error_return:
- brelse(bitmap_bh);
- ext4_std_error(sb, err);
- return;
-}
-
-/**
* ext4_has_free_blocks()
* @sbi: in-core super block structure.
* @nblocks: number of needed blocks
@@ -493,7 +369,8 @@
* Check if filesystem has nblocks free & available for allocation.
* On success return 1, return 0 on failure.
*/
-static int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks)
+static int ext4_has_free_blocks(struct ext4_sb_info *sbi,
+ s64 nblocks, unsigned int flags)
{
s64 free_blocks, dirty_blocks, root_blocks;
struct percpu_counter *fbc = &sbi->s_freeblocks_counter;
@@ -507,11 +384,6 @@
EXT4_FREEBLOCKS_WATERMARK) {
free_blocks = percpu_counter_sum_positive(fbc);
dirty_blocks = percpu_counter_sum_positive(dbc);
- if (dirty_blocks < 0) {
- printk(KERN_CRIT "Dirty block accounting "
- "went wrong %lld\n",
- (long long)dirty_blocks);
- }
}
/* Check whether we have space after
* accounting for current dirty blocks & root reserved blocks.
@@ -522,7 +394,9 @@
/* Hm, nope. Are (enough) root reserved blocks available? */
if (sbi->s_resuid == current_fsuid() ||
((sbi->s_resgid != 0) && in_group_p(sbi->s_resgid)) ||
- capable(CAP_SYS_RESOURCE)) {
+ capable(CAP_SYS_RESOURCE) ||
+ (flags & EXT4_MB_USE_ROOT_BLOCKS)) {
+
if (free_blocks >= (nblocks + dirty_blocks))
return 1;
}
@@ -531,9 +405,9 @@
}
int ext4_claim_free_blocks(struct ext4_sb_info *sbi,
- s64 nblocks)
+ s64 nblocks, unsigned int flags)
{
- if (ext4_has_free_blocks(sbi, nblocks)) {
+ if (ext4_has_free_blocks(sbi, nblocks, flags)) {
percpu_counter_add(&sbi->s_dirtyblocks_counter, nblocks);
return 0;
} else
@@ -554,7 +428,7 @@
*/
int ext4_should_retry_alloc(struct super_block *sb, int *retries)
{
- if (!ext4_has_free_blocks(EXT4_SB(sb), 1) ||
+ if (!ext4_has_free_blocks(EXT4_SB(sb), 1, 0) ||
(*retries)++ > 3 ||
!EXT4_SB(sb)->s_journal)
return 0;
@@ -577,7 +451,8 @@
* error stores in errp pointer
*/
ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
- ext4_fsblk_t goal, unsigned long *count, int *errp)
+ ext4_fsblk_t goal, unsigned int flags,
+ unsigned long *count, int *errp)
{
struct ext4_allocation_request ar;
ext4_fsblk_t ret;
@@ -587,6 +462,7 @@
ar.inode = inode;
ar.goal = goal;
ar.len = count ? *count : 1;
+ ar.flags = flags;
ret = ext4_mb_new_blocks(handle, &ar, errp);
if (count)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 4daaf2b..a74b89c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -108,7 +108,8 @@
#define EXT4_MB_DELALLOC_RESERVED 0x0400
/* We are doing stream allocation */
#define EXT4_MB_STREAM_ALLOC 0x0800
-
+/* Use reserved root blocks if needed */
+#define EXT4_MB_USE_ROOT_BLOCKS 0x1000
struct ext4_allocation_request {
/* target inode for block we're allocating */
@@ -209,6 +210,8 @@
*/
#define EXT4_BAD_INO 1 /* Bad blocks inode */
#define EXT4_ROOT_INO 2 /* Root inode */
+#define EXT4_USR_QUOTA_INO 3 /* User quota inode */
+#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */
#define EXT4_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */
#define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */
@@ -512,6 +515,10 @@
/* Convert extent to initialized after IO complete */
#define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\
EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
+ /* Punch out blocks of an extent */
+#define EXT4_GET_BLOCKS_PUNCH_OUT_EXT 0x0020
+ /* Don't normalize allocation size (used for fallocate) */
+#define EXT4_GET_BLOCKS_NO_NORMALIZE 0x0040
/*
* Flags used by ext4_free_blocks
@@ -1028,7 +1035,7 @@
__le16 s_want_extra_isize; /* New inodes should reserve # bytes */
__le32 s_flags; /* Miscellaneous flags */
__le16 s_raid_stride; /* RAID stride */
- __le16 s_mmp_interval; /* # seconds to wait in MMP checking */
+ __le16 s_mmp_update_interval; /* # seconds to wait in MMP checking */
__le64 s_mmp_block; /* Block for multi-mount protection */
__le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
@@ -1144,6 +1151,9 @@
unsigned long s_ext_blocks;
unsigned long s_ext_extents;
#endif
+ /* ext4 extent cache stats */
+ unsigned long extent_cache_hits;
+ unsigned long extent_cache_misses;
/* for buddy allocator */
struct ext4_group_info ***s_group_info;
@@ -1201,6 +1211,9 @@
struct ext4_li_request *s_li_request;
/* Wait multiplier for lazy initialization thread */
unsigned int s_li_wait_mult;
+
+ /* Kernel thread for multiple mount protection */
+ struct task_struct *s_mmp_tsk;
};
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -1338,6 +1351,7 @@
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
+#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100
#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
@@ -1351,13 +1365,29 @@
#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 /* EA in inode */
#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */
+#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
+#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
+ EXT4_FEATURE_INCOMPAT_META_BG)
+#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT4_FEATURE_RO_COMPAT_BTREE_DIR)
+
+#define EXT3_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
+#define EXT3_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
+ EXT4_FEATURE_INCOMPAT_RECOVER| \
+ EXT4_FEATURE_INCOMPAT_META_BG)
+#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT4_FEATURE_RO_COMPAT_BTREE_DIR)
+
#define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
EXT4_FEATURE_INCOMPAT_RECOVER| \
EXT4_FEATURE_INCOMPAT_META_BG| \
EXT4_FEATURE_INCOMPAT_EXTENTS| \
EXT4_FEATURE_INCOMPAT_64BIT| \
- EXT4_FEATURE_INCOMPAT_FLEX_BG)
+ EXT4_FEATURE_INCOMPAT_FLEX_BG| \
+ EXT4_FEATURE_INCOMPAT_MMP)
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
@@ -1590,12 +1620,6 @@
*/
struct ext4_lazy_init {
unsigned long li_state;
-
- wait_queue_head_t li_wait_daemon;
- wait_queue_head_t li_wait_task;
- struct timer_list li_timer;
- struct task_struct *li_task;
-
struct list_head li_request_list;
struct mutex li_list_mtx;
};
@@ -1615,6 +1639,67 @@
};
/*
+ * This structure will be used for multiple mount protection. It will be
+ * written into the block number saved in the s_mmp_block field in the
+ * superblock. Programs that check MMP should assume that if
+ * SEQ_FSCK (or any unknown code above SEQ_MAX) is present then it is NOT safe
+ * to use the filesystem, regardless of how old the timestamp is.
+ */
+#define EXT4_MMP_MAGIC 0x004D4D50U /* ASCII for MMP */
+#define EXT4_MMP_SEQ_CLEAN 0xFF4D4D50U /* mmp_seq value for clean unmount */
+#define EXT4_MMP_SEQ_FSCK 0xE24D4D50U /* mmp_seq value when being fscked */
+#define EXT4_MMP_SEQ_MAX 0xE24D4D4FU /* maximum valid mmp_seq value */
+
+struct mmp_struct {
+ __le32 mmp_magic; /* Magic number for MMP */
+ __le32 mmp_seq; /* Sequence no. updated periodically */
+
+ /*
+ * mmp_time, mmp_nodename & mmp_bdevname are only used for information
+ * purposes and do not affect the correctness of the algorithm
+ */
+ __le64 mmp_time; /* Time last updated */
+ char mmp_nodename[64]; /* Node which last updated MMP block */
+ char mmp_bdevname[32]; /* Bdev which last updated MMP block */
+
+ /*
+ * mmp_check_interval is used to verify if the MMP block has been
+ * updated on the block device. The value is updated based on the
+ * maximum time to write the MMP block during an update cycle.
+ */
+ __le16 mmp_check_interval;
+
+ __le16 mmp_pad1;
+ __le32 mmp_pad2[227];
+};
+
+/* arguments passed to the mmp thread */
+struct mmpd_data {
+ struct buffer_head *bh; /* bh from initial read_mmp_block() */
+ struct super_block *sb; /* super block of the fs */
+};
+
+/*
+ * Check interval multiplier
+ * The MMP block is written every update interval and initially checked every
+ * update interval x the multiplier (the value is then adapted based on the
+ * write latency). The reason is that writes can be delayed under load and we
+ * don't want readers to incorrectly assume that the filesystem is no longer
+ * in use.
+ */
+#define EXT4_MMP_CHECK_MULT 2UL
+
+/*
+ * Minimum interval for MMP checking in seconds.
+ */
+#define EXT4_MMP_MIN_CHECK_INTERVAL 5UL
+
+/*
+ * Maximum interval for MMP checking in seconds.
+ */
+#define EXT4_MMP_MAX_CHECK_INTERVAL 300UL
+
+/*
* Function prototypes
*/
@@ -1638,10 +1723,12 @@
extern unsigned long ext4_bg_num_gdb(struct super_block *sb,
ext4_group_t group);
extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
- ext4_fsblk_t goal, unsigned long *count, int *errp);
-extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks);
-extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
- ext4_fsblk_t block, unsigned long count);
+ ext4_fsblk_t goal,
+ unsigned int flags,
+ unsigned long *count,
+ int *errp);
+extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi,
+ s64 nblocks, unsigned int flags);
extern ext4_fsblk_t ext4_count_free_blocks(struct super_block *);
extern void ext4_check_blocks_bitmap(struct super_block *);
extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb,
@@ -1706,6 +1793,8 @@
unsigned long count, int flags);
extern int ext4_mb_add_groupinfo(struct super_block *sb,
ext4_group_t i, struct ext4_group_desc *desc);
+extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
+ ext4_fsblk_t block, unsigned long count);
extern int ext4_trim_fs(struct super_block *, struct fstrim_range *);
/* inode.c */
@@ -1729,6 +1818,7 @@
extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *);
extern int ext4_can_truncate(struct inode *inode);
extern void ext4_truncate(struct inode *);
+extern int ext4_punch_hole(struct file *file, loff_t offset, loff_t length);
extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks);
extern void ext4_set_inode_flags(struct inode *);
extern void ext4_get_inode_flags(struct ext4_inode_info *);
@@ -1738,6 +1828,8 @@
extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks);
extern int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from);
+extern int ext4_block_zero_page_range(handle_t *handle,
+ struct address_space *mapping, loff_t from, loff_t length);
extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
extern qsize_t *ext4_get_reserved_space(struct inode *inode);
extern void ext4_da_update_reserve_space(struct inode *inode,
@@ -1788,6 +1880,10 @@
__LINE__, ## message)
extern void ext4_msg(struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
+extern void __dump_mmp_msg(struct super_block *, struct mmp_struct *mmp,
+ const char *, unsigned int, const char *);
+#define dump_mmp_msg(sb, mmp, msg) __dump_mmp_msg(sb, mmp, __func__, \
+ __LINE__, msg)
extern void __ext4_grp_locked_error(const char *, unsigned int, \
struct super_block *, ext4_group_t, \
unsigned long, ext4_fsblk_t, \
@@ -2064,6 +2160,8 @@
extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
struct ext4_map_blocks *map, int flags);
extern void ext4_ext_truncate(struct inode *);
+extern int ext4_ext_punch_hole(struct file *file, loff_t offset,
+ loff_t length);
extern void ext4_ext_init(struct super_block *);
extern void ext4_ext_release(struct super_block *);
extern long ext4_fallocate(struct file *file, int mode, loff_t offset,
@@ -2092,6 +2190,9 @@
int len,
struct writeback_control *wbc);
+/* mmp.c */
+extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t);
+
/* BH_Uninit flag: blocks are allocated but uninitialized on disk */
enum ext4_state_bits {
BH_Uninit /* blocks are allocated but uninitialized on disk */
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 6e272ef..f5240aa 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -6,20 +6,6 @@
#include <trace/events/ext4.h>
-int __ext4_journal_get_undo_access(const char *where, unsigned int line,
- handle_t *handle, struct buffer_head *bh)
-{
- int err = 0;
-
- if (ext4_handle_valid(handle)) {
- err = jbd2_journal_get_undo_access(handle, bh);
- if (err)
- ext4_journal_abort_handle(where, line, __func__, bh,
- handle, err);
- }
- return err;
-}
-
int __ext4_journal_get_write_access(const char *where, unsigned int line,
handle_t *handle, struct buffer_head *bh)
{
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index d0f5353..bb85757 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -126,9 +126,6 @@
const char *err_fn,
struct buffer_head *bh, handle_t *handle, int err);
-int __ext4_journal_get_undo_access(const char *where, unsigned int line,
- handle_t *handle, struct buffer_head *bh);
-
int __ext4_journal_get_write_access(const char *where, unsigned int line,
handle_t *handle, struct buffer_head *bh);
@@ -146,8 +143,6 @@
int __ext4_handle_dirty_super(const char *where, unsigned int line,
handle_t *handle, struct super_block *sb);
-#define ext4_journal_get_undo_access(handle, bh) \
- __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
#define ext4_journal_get_write_access(handle, bh) \
__ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
#define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 4890d6f..5199bac 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -46,6 +46,13 @@
#include <trace/events/ext4.h>
+static int ext4_split_extent(handle_t *handle,
+ struct inode *inode,
+ struct ext4_ext_path *path,
+ struct ext4_map_blocks *map,
+ int split_flag,
+ int flags);
+
static int ext4_ext_truncate_extend_restart(handle_t *handle,
struct inode *inode,
int needed)
@@ -192,12 +199,13 @@
static ext4_fsblk_t
ext4_ext_new_meta_block(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path,
- struct ext4_extent *ex, int *err)
+ struct ext4_extent *ex, int *err, unsigned int flags)
{
ext4_fsblk_t goal, newblock;
goal = ext4_ext_find_goal(inode, path, le32_to_cpu(ex->ee_block));
- newblock = ext4_new_meta_blocks(handle, inode, goal, NULL, err);
+ newblock = ext4_new_meta_blocks(handle, inode, goal, flags,
+ NULL, err);
return newblock;
}
@@ -474,9 +482,43 @@
}
ext_debug("\n");
}
+
+static void ext4_ext_show_move(struct inode *inode, struct ext4_ext_path *path,
+ ext4_fsblk_t newblock, int level)
+{
+ int depth = ext_depth(inode);
+ struct ext4_extent *ex;
+
+ if (depth != level) {
+ struct ext4_extent_idx *idx;
+ idx = path[level].p_idx;
+ while (idx <= EXT_MAX_INDEX(path[level].p_hdr)) {
+ ext_debug("%d: move %d:%llu in new index %llu\n", level,
+ le32_to_cpu(idx->ei_block),
+ ext4_idx_pblock(idx),
+ newblock);
+ idx++;
+ }
+
+ return;
+ }
+
+ ex = path[depth].p_ext;
+ while (ex <= EXT_MAX_EXTENT(path[depth].p_hdr)) {
+ ext_debug("move %d:%llu:[%d]%d in new leaf %llu\n",
+ le32_to_cpu(ex->ee_block),
+ ext4_ext_pblock(ex),
+ ext4_ext_is_uninitialized(ex),
+ ext4_ext_get_actual_len(ex),
+ newblock);
+ ex++;
+ }
+}
+
#else
#define ext4_ext_show_path(inode, path)
#define ext4_ext_show_leaf(inode, path)
+#define ext4_ext_show_move(inode, path, newblock, level)
#endif
void ext4_ext_drop_refs(struct ext4_ext_path *path)
@@ -792,14 +834,14 @@
* - initializes subtree
*/
static int ext4_ext_split(handle_t *handle, struct inode *inode,
- struct ext4_ext_path *path,
- struct ext4_extent *newext, int at)
+ unsigned int flags,
+ struct ext4_ext_path *path,
+ struct ext4_extent *newext, int at)
{
struct buffer_head *bh = NULL;
int depth = ext_depth(inode);
struct ext4_extent_header *neh;
struct ext4_extent_idx *fidx;
- struct ext4_extent *ex;
int i = at, k, m, a;
ext4_fsblk_t newblock, oldblock;
__le32 border;
@@ -847,7 +889,7 @@
ext_debug("allocate %d blocks for indexes/leaf\n", depth - at);
for (a = 0; a < depth - at; a++) {
newblock = ext4_ext_new_meta_block(handle, inode, path,
- newext, &err);
+ newext, &err, flags);
if (newblock == 0)
goto cleanup;
ablocks[a] = newblock;
@@ -876,7 +918,6 @@
neh->eh_max = cpu_to_le16(ext4_ext_space_block(inode, 0));
neh->eh_magic = EXT4_EXT_MAGIC;
neh->eh_depth = 0;
- ex = EXT_FIRST_EXTENT(neh);
/* move remainder of path[depth] to the new leaf */
if (unlikely(path[depth].p_hdr->eh_entries !=
@@ -888,25 +929,12 @@
goto cleanup;
}
/* start copy from next extent */
- /* TODO: we could do it by single memmove */
- m = 0;
- path[depth].p_ext++;
- while (path[depth].p_ext <=
- EXT_MAX_EXTENT(path[depth].p_hdr)) {
- ext_debug("move %d:%llu:[%d]%d in new leaf %llu\n",
- le32_to_cpu(path[depth].p_ext->ee_block),
- ext4_ext_pblock(path[depth].p_ext),
- ext4_ext_is_uninitialized(path[depth].p_ext),
- ext4_ext_get_actual_len(path[depth].p_ext),
- newblock);
- /*memmove(ex++, path[depth].p_ext++,
- sizeof(struct ext4_extent));
- neh->eh_entries++;*/
- path[depth].p_ext++;
- m++;
- }
+ m = EXT_MAX_EXTENT(path[depth].p_hdr) - path[depth].p_ext++;
+ ext4_ext_show_move(inode, path, newblock, depth);
if (m) {
- memmove(ex, path[depth].p_ext-m, sizeof(struct ext4_extent)*m);
+ struct ext4_extent *ex;
+ ex = EXT_FIRST_EXTENT(neh);
+ memmove(ex, path[depth].p_ext, sizeof(struct ext4_extent) * m);
le16_add_cpu(&neh->eh_entries, m);
}
@@ -968,12 +996,8 @@
ext_debug("int.index at %d (block %llu): %u -> %llu\n",
i, newblock, le32_to_cpu(border), oldblock);
- /* copy indexes */
- m = 0;
- path[i].p_idx++;
- ext_debug("cur 0x%p, last 0x%p\n", path[i].p_idx,
- EXT_MAX_INDEX(path[i].p_hdr));
+ /* move remainder of path[i] to the new index block */
if (unlikely(EXT_MAX_INDEX(path[i].p_hdr) !=
EXT_LAST_INDEX(path[i].p_hdr))) {
EXT4_ERROR_INODE(inode,
@@ -982,20 +1006,13 @@
err = -EIO;
goto cleanup;
}
- while (path[i].p_idx <= EXT_MAX_INDEX(path[i].p_hdr)) {
- ext_debug("%d: move %d:%llu in new index %llu\n", i,
- le32_to_cpu(path[i].p_idx->ei_block),
- ext4_idx_pblock(path[i].p_idx),
- newblock);
- /*memmove(++fidx, path[i].p_idx++,
- sizeof(struct ext4_extent_idx));
- neh->eh_entries++;
- BUG_ON(neh->eh_entries > neh->eh_max);*/
- path[i].p_idx++;
- m++;
- }
+ /* start copy indexes */
+ m = EXT_MAX_INDEX(path[i].p_hdr) - path[i].p_idx++;
+ ext_debug("cur 0x%p, last 0x%p\n", path[i].p_idx,
+ EXT_MAX_INDEX(path[i].p_hdr));
+ ext4_ext_show_move(inode, path, newblock, i);
if (m) {
- memmove(++fidx, path[i].p_idx - m,
+ memmove(++fidx, path[i].p_idx,
sizeof(struct ext4_extent_idx) * m);
le16_add_cpu(&neh->eh_entries, m);
}
@@ -1056,8 +1073,9 @@
* just created block
*/
static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
- struct ext4_ext_path *path,
- struct ext4_extent *newext)
+ unsigned int flags,
+ struct ext4_ext_path *path,
+ struct ext4_extent *newext)
{
struct ext4_ext_path *curp = path;
struct ext4_extent_header *neh;
@@ -1065,7 +1083,8 @@
ext4_fsblk_t newblock;
int err = 0;
- newblock = ext4_ext_new_meta_block(handle, inode, path, newext, &err);
+ newblock = ext4_ext_new_meta_block(handle, inode, path,
+ newext, &err, flags);
if (newblock == 0)
return err;
@@ -1140,8 +1159,9 @@
* if no free index is found, then it requests in-depth growing.
*/
static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode,
- struct ext4_ext_path *path,
- struct ext4_extent *newext)
+ unsigned int flags,
+ struct ext4_ext_path *path,
+ struct ext4_extent *newext)
{
struct ext4_ext_path *curp;
int depth, i, err = 0;
@@ -1161,7 +1181,7 @@
if (EXT_HAS_FREE_INDEX(curp)) {
/* if we found index with free entry, then use that
* entry: create all needed subtree and add new leaf */
- err = ext4_ext_split(handle, inode, path, newext, i);
+ err = ext4_ext_split(handle, inode, flags, path, newext, i);
if (err)
goto out;
@@ -1174,7 +1194,8 @@
err = PTR_ERR(path);
} else {
/* tree is full, time to grow in depth */
- err = ext4_ext_grow_indepth(handle, inode, path, newext);
+ err = ext4_ext_grow_indepth(handle, inode, flags,
+ path, newext);
if (err)
goto out;
@@ -1563,7 +1584,7 @@
* Returns 0 if the extents (ex and ex+1) were _not_ merged and returns
* 1 if they got merged.
*/
-static int ext4_ext_try_to_merge(struct inode *inode,
+static int ext4_ext_try_to_merge_right(struct inode *inode,
struct ext4_ext_path *path,
struct ext4_extent *ex)
{
@@ -1603,6 +1624,31 @@
}
/*
+ * This function tries to merge the @ex extent to neighbours in the tree.
+ * return 1 if merge left else 0.
+ */
+static int ext4_ext_try_to_merge(struct inode *inode,
+ struct ext4_ext_path *path,
+ struct ext4_extent *ex) {
+ struct ext4_extent_header *eh;
+ unsigned int depth;
+ int merge_done = 0;
+ int ret = 0;
+
+ depth = ext_depth(inode);
+ BUG_ON(path[depth].p_hdr == NULL);
+ eh = path[depth].p_hdr;
+
+ if (ex > EXT_FIRST_EXTENT(eh))
+ merge_done = ext4_ext_try_to_merge_right(inode, path, ex - 1);
+
+ if (!merge_done)
+ ret = ext4_ext_try_to_merge_right(inode, path, ex);
+
+ return ret;
+}
+
+/*
* check if a portion of the "newext" extent overlaps with an
* existing extent.
*
@@ -1668,6 +1714,7 @@
int depth, len, err;
ext4_lblk_t next;
unsigned uninitialized = 0;
+ int flags = 0;
if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0");
@@ -1742,7 +1789,9 @@
* There is no free space in the found leaf.
* We're gonna add a new leaf in the tree.
*/
- err = ext4_ext_create_new_leaf(handle, inode, path, newext);
+ if (flag & EXT4_GET_BLOCKS_PUNCH_OUT_EXT)
+ flags = EXT4_MB_USE_ROOT_BLOCKS;
+ err = ext4_ext_create_new_leaf(handle, inode, flags, path, newext);
if (err)
goto cleanup;
depth = ext_depth(inode);
@@ -2003,13 +2052,25 @@
}
/*
+ * ext4_ext_in_cache()
+ * Checks to see if the given block is in the cache.
+ * If it is, the cached extent is stored in the given
+ * cache extent pointer. If the cached extent is a hole,
+ * this routine should be used instead of
+ * ext4_ext_in_cache if the calling function needs to
+ * know the size of the hole.
+ *
+ * @inode: The files inode
+ * @block: The block to look for in the cache
+ * @ex: Pointer where the cached extent will be stored
+ * if it contains block
+ *
* Return 0 if cache is invalid; 1 if the cache is valid
*/
-static int
-ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
- struct ext4_extent *ex)
-{
+static int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block,
+ struct ext4_ext_cache *ex){
struct ext4_ext_cache *cex;
+ struct ext4_sb_info *sbi;
int ret = 0;
/*
@@ -2017,26 +2078,60 @@
*/
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
cex = &EXT4_I(inode)->i_cached_extent;
+ sbi = EXT4_SB(inode->i_sb);
/* has cache valid data? */
if (cex->ec_len == 0)
goto errout;
if (in_range(block, cex->ec_block, cex->ec_len)) {
- ex->ee_block = cpu_to_le32(cex->ec_block);
- ext4_ext_store_pblock(ex, cex->ec_start);
- ex->ee_len = cpu_to_le16(cex->ec_len);
+ memcpy(ex, cex, sizeof(struct ext4_ext_cache));
ext_debug("%u cached by %u:%u:%llu\n",
block,
cex->ec_block, cex->ec_len, cex->ec_start);
ret = 1;
}
errout:
+ if (!ret)
+ sbi->extent_cache_misses++;
+ else
+ sbi->extent_cache_hits++;
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
return ret;
}
/*
+ * ext4_ext_in_cache()
+ * Checks to see if the given block is in the cache.
+ * If it is, the cached extent is stored in the given
+ * extent pointer.
+ *
+ * @inode: The files inode
+ * @block: The block to look for in the cache
+ * @ex: Pointer where the cached extent will be stored
+ * if it contains block
+ *
+ * Return 0 if cache is invalid; 1 if the cache is valid
+ */
+static int
+ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
+ struct ext4_extent *ex)
+{
+ struct ext4_ext_cache cex;
+ int ret = 0;
+
+ if (ext4_ext_check_cache(inode, block, &cex)) {
+ ex->ee_block = cpu_to_le32(cex.ec_block);
+ ext4_ext_store_pblock(ex, cex.ec_start);
+ ex->ee_len = cpu_to_le16(cex.ec_len);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+
+/*
* ext4_ext_rm_idx:
* removes index from the index block.
* It's used in truncate case only, thus all requests are for
@@ -2163,8 +2258,16 @@
ext4_free_blocks(handle, inode, NULL, start, num, flags);
} else if (from == le32_to_cpu(ex->ee_block)
&& to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
- printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
- from, to, le32_to_cpu(ex->ee_block), ee_len);
+ /* head removal */
+ ext4_lblk_t num;
+ ext4_fsblk_t start;
+
+ num = to - from;
+ start = ext4_ext_pblock(ex);
+
+ ext_debug("free first %u blocks starting %llu\n", num, start);
+ ext4_free_blocks(handle, inode, 0, start, num, flags);
+
} else {
printk(KERN_INFO "strange request: removal(2) "
"%u-%u from %u:%u\n",
@@ -2173,9 +2276,22 @@
return 0;
}
+
+/*
+ * ext4_ext_rm_leaf() Removes the extents associated with the
+ * blocks appearing between "start" and "end", and splits the extents
+ * if "start" and "end" appear in the same extent
+ *
+ * @handle: The journal handle
+ * @inode: The files inode
+ * @path: The path to the leaf
+ * @start: The first block to remove
+ * @end: The last block to remove
+ */
static int
ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
- struct ext4_ext_path *path, ext4_lblk_t start)
+ struct ext4_ext_path *path, ext4_lblk_t start,
+ ext4_lblk_t end)
{
int err = 0, correct_index = 0;
int depth = ext_depth(inode), credits;
@@ -2186,6 +2302,7 @@
unsigned short ex_ee_len;
unsigned uninitialized = 0;
struct ext4_extent *ex;
+ struct ext4_map_blocks map;
/* the header must be checked already in ext4_ext_remove_space() */
ext_debug("truncate since %u in leaf\n", start);
@@ -2215,31 +2332,95 @@
path[depth].p_ext = ex;
a = ex_ee_block > start ? ex_ee_block : start;
- b = ex_ee_block + ex_ee_len - 1 < EXT_MAX_BLOCK ?
- ex_ee_block + ex_ee_len - 1 : EXT_MAX_BLOCK;
+ b = ex_ee_block+ex_ee_len - 1 < end ?
+ ex_ee_block+ex_ee_len - 1 : end;
ext_debug(" border %u:%u\n", a, b);
- if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
- block = 0;
- num = 0;
- BUG();
+ /* If this extent is beyond the end of the hole, skip it */
+ if (end <= ex_ee_block) {
+ ex--;
+ ex_ee_block = le32_to_cpu(ex->ee_block);
+ ex_ee_len = ext4_ext_get_actual_len(ex);
+ continue;
+ } else if (a != ex_ee_block &&
+ b != ex_ee_block + ex_ee_len - 1) {
+ /*
+ * If this is a truncate, then this condition should
+ * never happen because at least one of the end points
+ * needs to be on the edge of the extent.
+ */
+ if (end == EXT_MAX_BLOCK) {
+ ext_debug(" bad truncate %u:%u\n",
+ start, end);
+ block = 0;
+ num = 0;
+ err = -EIO;
+ goto out;
+ }
+ /*
+ * else this is a hole punch, so the extent needs to
+ * be split since neither edge of the hole is on the
+ * extent edge
+ */
+ else{
+ map.m_pblk = ext4_ext_pblock(ex);
+ map.m_lblk = ex_ee_block;
+ map.m_len = b - ex_ee_block;
+
+ err = ext4_split_extent(handle,
+ inode, path, &map, 0,
+ EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
+ EXT4_GET_BLOCKS_PRE_IO);
+
+ if (err < 0)
+ goto out;
+
+ ex_ee_len = ext4_ext_get_actual_len(ex);
+
+ b = ex_ee_block+ex_ee_len - 1 < end ?
+ ex_ee_block+ex_ee_len - 1 : end;
+
+ /* Then remove tail of this extent */
+ block = ex_ee_block;
+ num = a - block;
+ }
} else if (a != ex_ee_block) {
/* remove tail of the extent */
block = ex_ee_block;
num = a - block;
} else if (b != ex_ee_block + ex_ee_len - 1) {
/* remove head of the extent */
- block = a;
- num = b - a;
- /* there is no "make a hole" API yet */
- BUG();
+ block = b;
+ num = ex_ee_block + ex_ee_len - b;
+
+ /*
+ * If this is a truncate, this condition
+ * should never happen
+ */
+ if (end == EXT_MAX_BLOCK) {
+ ext_debug(" bad truncate %u:%u\n",
+ start, end);
+ err = -EIO;
+ goto out;
+ }
} else {
/* remove whole extent: excellent! */
block = ex_ee_block;
num = 0;
- BUG_ON(a != ex_ee_block);
- BUG_ON(b != ex_ee_block + ex_ee_len - 1);
+ if (a != ex_ee_block) {
+ ext_debug(" bad truncate %u:%u\n",
+ start, end);
+ err = -EIO;
+ goto out;
+ }
+
+ if (b != ex_ee_block + ex_ee_len - 1) {
+ ext_debug(" bad truncate %u:%u\n",
+ start, end);
+ err = -EIO;
+ goto out;
+ }
}
/*
@@ -2270,7 +2451,13 @@
if (num == 0) {
/* this extent is removed; mark slot entirely unused */
ext4_ext_store_pblock(ex, 0);
- le16_add_cpu(&eh->eh_entries, -1);
+ } else if (block != ex_ee_block) {
+ /*
+ * If this was a head removal, then we need to update
+ * the physical block since it is now at a different
+ * location
+ */
+ ext4_ext_store_pblock(ex, ext4_ext_pblock(ex) + (b-a));
}
ex->ee_block = cpu_to_le32(block);
@@ -2286,6 +2473,27 @@
if (err)
goto out;
+ /*
+ * If the extent was completely released,
+ * we need to remove it from the leaf
+ */
+ if (num == 0) {
+ if (end != EXT_MAX_BLOCK) {
+ /*
+ * For hole punching, we need to scoot all the
+ * extents up when an extent is removed so that
+ * we dont have blank extents in the middle
+ */
+ memmove(ex, ex+1, (EXT_LAST_EXTENT(eh) - ex) *
+ sizeof(struct ext4_extent));
+
+ /* Now get rid of the one at the end */
+ memset(EXT_LAST_EXTENT(eh), 0,
+ sizeof(struct ext4_extent));
+ }
+ le16_add_cpu(&eh->eh_entries, -1);
+ }
+
ext_debug("new extent: %u:%u:%llu\n", block, num,
ext4_ext_pblock(ex));
ex--;
@@ -2326,7 +2534,8 @@
return 1;
}
-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
+static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
+ ext4_lblk_t end)
{
struct super_block *sb = inode->i_sb;
int depth = ext_depth(inode);
@@ -2365,7 +2574,8 @@
while (i >= 0 && err == 0) {
if (i == depth) {
/* this is leaf block */
- err = ext4_ext_rm_leaf(handle, inode, path, start);
+ err = ext4_ext_rm_leaf(handle, inode, path,
+ start, end);
/* root level has p_bh == NULL, brelse() eats this */
brelse(path[i].p_bh);
path[i].p_bh = NULL;
@@ -2529,6 +2739,195 @@
return ret;
}
+/*
+ * used by extent splitting.
+ */
+#define EXT4_EXT_MAY_ZEROOUT 0x1 /* safe to zeroout if split fails \
+ due to ENOSPC */
+#define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */
+#define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */
+
+/*
+ * ext4_split_extent_at() splits an extent at given block.
+ *
+ * @handle: the journal handle
+ * @inode: the file inode
+ * @path: the path to the extent
+ * @split: the logical block where the extent is splitted.
+ * @split_flags: indicates if the extent could be zeroout if split fails, and
+ * the states(init or uninit) of new extents.
+ * @flags: flags used to insert new extent to extent tree.
+ *
+ *
+ * Splits extent [a, b] into two extents [a, @split) and [@split, b], states
+ * of which are deterimined by split_flag.
+ *
+ * There are two cases:
+ * a> the extent are splitted into two extent.
+ * b> split is not needed, and just mark the extent.
+ *
+ * return 0 on success.
+ */
+static int ext4_split_extent_at(handle_t *handle,
+ struct inode *inode,
+ struct ext4_ext_path *path,
+ ext4_lblk_t split,
+ int split_flag,
+ int flags)
+{
+ ext4_fsblk_t newblock;
+ ext4_lblk_t ee_block;
+ struct ext4_extent *ex, newex, orig_ex;
+ struct ext4_extent *ex2 = NULL;
+ unsigned int ee_len, depth;
+ int err = 0;
+
+ ext_debug("ext4_split_extents_at: inode %lu, logical"
+ "block %llu\n", inode->i_ino, (unsigned long long)split);
+
+ ext4_ext_show_leaf(inode, path);
+
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ ee_block = le32_to_cpu(ex->ee_block);
+ ee_len = ext4_ext_get_actual_len(ex);
+ newblock = split - ee_block + ext4_ext_pblock(ex);
+
+ BUG_ON(split < ee_block || split >= (ee_block + ee_len));
+
+ err = ext4_ext_get_access(handle, inode, path + depth);
+ if (err)
+ goto out;
+
+ if (split == ee_block) {
+ /*
+ * case b: block @split is the block that the extent begins with
+ * then we just change the state of the extent, and splitting
+ * is not needed.
+ */
+ if (split_flag & EXT4_EXT_MARK_UNINIT2)
+ ext4_ext_mark_uninitialized(ex);
+ else
+ ext4_ext_mark_initialized(ex);
+
+ if (!(flags & EXT4_GET_BLOCKS_PRE_IO))
+ ext4_ext_try_to_merge(inode, path, ex);
+
+ err = ext4_ext_dirty(handle, inode, path + depth);
+ goto out;
+ }
+
+ /* case a */
+ memcpy(&orig_ex, ex, sizeof(orig_ex));
+ ex->ee_len = cpu_to_le16(split - ee_block);
+ if (split_flag & EXT4_EXT_MARK_UNINIT1)
+ ext4_ext_mark_uninitialized(ex);
+
+ /*
+ * path may lead to new leaf, not to original leaf any more
+ * after ext4_ext_insert_extent() returns,
+ */
+ err = ext4_ext_dirty(handle, inode, path + depth);
+ if (err)
+ goto fix_extent_len;
+
+ ex2 = &newex;
+ ex2->ee_block = cpu_to_le32(split);
+ ex2->ee_len = cpu_to_le16(ee_len - (split - ee_block));
+ ext4_ext_store_pblock(ex2, newblock);
+ if (split_flag & EXT4_EXT_MARK_UNINIT2)
+ ext4_ext_mark_uninitialized(ex2);
+
+ err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
+ if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
+ err = ext4_ext_zeroout(inode, &orig_ex);
+ if (err)
+ goto fix_extent_len;
+ /* update the extent length and mark as initialized */
+ ex->ee_len = cpu_to_le32(ee_len);
+ ext4_ext_try_to_merge(inode, path, ex);
+ err = ext4_ext_dirty(handle, inode, path + depth);
+ goto out;
+ } else if (err)
+ goto fix_extent_len;
+
+out:
+ ext4_ext_show_leaf(inode, path);
+ return err;
+
+fix_extent_len:
+ ex->ee_len = orig_ex.ee_len;
+ ext4_ext_dirty(handle, inode, path + depth);
+ return err;
+}
+
+/*
+ * ext4_split_extents() splits an extent and mark extent which is covered
+ * by @map as split_flags indicates
+ *
+ * It may result in splitting the extent into multiple extents (upto three)
+ * There are three possibilities:
+ * a> There is no split required
+ * b> Splits in two extents: Split is happening at either end of the extent
+ * c> Splits in three extents: Somone is splitting in middle of the extent
+ *
+ */
+static int ext4_split_extent(handle_t *handle,
+ struct inode *inode,
+ struct ext4_ext_path *path,
+ struct ext4_map_blocks *map,
+ int split_flag,
+ int flags)
+{
+ ext4_lblk_t ee_block;
+ struct ext4_extent *ex;
+ unsigned int ee_len, depth;
+ int err = 0;
+ int uninitialized;
+ int split_flag1, flags1;
+
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ ee_block = le32_to_cpu(ex->ee_block);
+ ee_len = ext4_ext_get_actual_len(ex);
+ uninitialized = ext4_ext_is_uninitialized(ex);
+
+ if (map->m_lblk + map->m_len < ee_block + ee_len) {
+ split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ?
+ EXT4_EXT_MAY_ZEROOUT : 0;
+ flags1 = flags | EXT4_GET_BLOCKS_PRE_IO;
+ if (uninitialized)
+ split_flag1 |= EXT4_EXT_MARK_UNINIT1 |
+ EXT4_EXT_MARK_UNINIT2;
+ err = ext4_split_extent_at(handle, inode, path,
+ map->m_lblk + map->m_len, split_flag1, flags1);
+ if (err)
+ goto out;
+ }
+
+ ext4_ext_drop_refs(path);
+ path = ext4_ext_find_extent(inode, map->m_lblk, path);
+ if (IS_ERR(path))
+ return PTR_ERR(path);
+
+ if (map->m_lblk >= ee_block) {
+ split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT ?
+ EXT4_EXT_MAY_ZEROOUT : 0;
+ if (uninitialized)
+ split_flag1 |= EXT4_EXT_MARK_UNINIT1;
+ if (split_flag & EXT4_EXT_MARK_UNINIT2)
+ split_flag1 |= EXT4_EXT_MARK_UNINIT2;
+ err = ext4_split_extent_at(handle, inode, path,
+ map->m_lblk, split_flag1, flags);
+ if (err)
+ goto out;
+ }
+
+ ext4_ext_show_leaf(inode, path);
+out:
+ return err ? err : map->m_len;
+}
+
#define EXT4_EXT_ZERO_LEN 7
/*
* This function is called by ext4_ext_map_blocks() if someone tries to write
@@ -2545,17 +2944,13 @@
struct ext4_map_blocks *map,
struct ext4_ext_path *path)
{
- struct ext4_extent *ex, newex, orig_ex;
- struct ext4_extent *ex1 = NULL;
- struct ext4_extent *ex2 = NULL;
- struct ext4_extent *ex3 = NULL;
- struct ext4_extent_header *eh;
+ struct ext4_map_blocks split_map;
+ struct ext4_extent zero_ex;
+ struct ext4_extent *ex;
ext4_lblk_t ee_block, eof_block;
unsigned int allocated, ee_len, depth;
- ext4_fsblk_t newblock;
int err = 0;
- int ret = 0;
- int may_zeroout;
+ int split_flag = 0;
ext_debug("ext4_ext_convert_to_initialized: inode %lu, logical"
"block %llu, max_blocks %u\n", inode->i_ino,
@@ -2567,280 +2962,86 @@
eof_block = map->m_lblk + map->m_len;
depth = ext_depth(inode);
- eh = path[depth].p_hdr;
ex = path[depth].p_ext;
ee_block = le32_to_cpu(ex->ee_block);
ee_len = ext4_ext_get_actual_len(ex);
allocated = ee_len - (map->m_lblk - ee_block);
- newblock = map->m_lblk - ee_block + ext4_ext_pblock(ex);
- ex2 = ex;
- orig_ex.ee_block = ex->ee_block;
- orig_ex.ee_len = cpu_to_le16(ee_len);
- ext4_ext_store_pblock(&orig_ex, ext4_ext_pblock(ex));
-
+ WARN_ON(map->m_lblk < ee_block);
/*
* It is safe to convert extent to initialized via explicit
* zeroout only if extent is fully insde i_size or new_size.
*/
- may_zeroout = ee_block + ee_len <= eof_block;
+ split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0;
- err = ext4_ext_get_access(handle, inode, path + depth);
- if (err)
- goto out;
/* If extent has less than 2*EXT4_EXT_ZERO_LEN zerout directly */
- if (ee_len <= 2*EXT4_EXT_ZERO_LEN && may_zeroout) {
- err = ext4_ext_zeroout(inode, &orig_ex);
+ if (ee_len <= 2*EXT4_EXT_ZERO_LEN &&
+ (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
+ err = ext4_ext_zeroout(inode, ex);
if (err)
- goto fix_extent_len;
- /* update the extent length and mark as initialized */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* zeroed the full extent */
- return allocated;
- }
-
- /* ex1: ee_block to map->m_lblk - 1 : uninitialized */
- if (map->m_lblk > ee_block) {
- ex1 = ex;
- ex1->ee_len = cpu_to_le16(map->m_lblk - ee_block);
- ext4_ext_mark_uninitialized(ex1);
- ex2 = &newex;
- }
- /*
- * for sanity, update the length of the ex2 extent before
- * we insert ex3, if ex1 is NULL. This is to avoid temporary
- * overlap of blocks.
- */
- if (!ex1 && allocated > map->m_len)
- ex2->ee_len = cpu_to_le16(map->m_len);
- /* ex3: to ee_block + ee_len : uninitialised */
- if (allocated > map->m_len) {
- unsigned int newdepth;
- /* If extent has less than EXT4_EXT_ZERO_LEN zerout directly */
- if (allocated <= EXT4_EXT_ZERO_LEN && may_zeroout) {
- /*
- * map->m_lblk == ee_block is handled by the zerouout
- * at the beginning.
- * Mark first half uninitialized.
- * Mark second half initialized and zero out the
- * initialized extent
- */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = cpu_to_le16(ee_len - allocated);
- ext4_ext_mark_uninitialized(ex);
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
-
- ex3 = &newex;
- ex3->ee_block = cpu_to_le32(map->m_lblk);
- ext4_ext_store_pblock(ex3, newblock);
- ex3->ee_len = cpu_to_le16(allocated);
- err = ext4_ext_insert_extent(handle, inode, path,
- ex3, 0);
- if (err == -ENOSPC) {
- err = ext4_ext_zeroout(inode, &orig_ex);
- if (err)
- goto fix_extent_len;
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex,
- ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* blocks available from map->m_lblk */
- return allocated;
-
- } else if (err)
- goto fix_extent_len;
-
- /*
- * We need to zero out the second half because
- * an fallocate request can update file size and
- * converting the second half to initialized extent
- * implies that we can leak some junk data to user
- * space.
- */
- err = ext4_ext_zeroout(inode, ex3);
- if (err) {
- /*
- * We should actually mark the
- * second half as uninit and return error
- * Insert would have changed the extent
- */
- depth = ext_depth(inode);
- ext4_ext_drop_refs(path);
- path = ext4_ext_find_extent(inode, map->m_lblk,
- path);
- if (IS_ERR(path)) {
- err = PTR_ERR(path);
- return err;
- }
- /* get the second half extent details */
- ex = path[depth].p_ext;
- err = ext4_ext_get_access(handle, inode,
- path + depth);
- if (err)
- return err;
- ext4_ext_mark_uninitialized(ex);
- ext4_ext_dirty(handle, inode, path + depth);
- return err;
- }
-
- /* zeroed the second half */
- return allocated;
- }
- ex3 = &newex;
- ex3->ee_block = cpu_to_le32(map->m_lblk + map->m_len);
- ext4_ext_store_pblock(ex3, newblock + map->m_len);
- ex3->ee_len = cpu_to_le16(allocated - map->m_len);
- ext4_ext_mark_uninitialized(ex3);
- err = ext4_ext_insert_extent(handle, inode, path, ex3, 0);
- if (err == -ENOSPC && may_zeroout) {
- err = ext4_ext_zeroout(inode, &orig_ex);
- if (err)
- goto fix_extent_len;
- /* update the extent length and mark as initialized */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* zeroed the full extent */
- /* blocks available from map->m_lblk */
- return allocated;
-
- } else if (err)
- goto fix_extent_len;
- /*
- * The depth, and hence eh & ex might change
- * as part of the insert above.
- */
- newdepth = ext_depth(inode);
- /*
- * update the extent length after successful insert of the
- * split extent
- */
- ee_len -= ext4_ext_get_actual_len(ex3);
- orig_ex.ee_len = cpu_to_le16(ee_len);
- may_zeroout = ee_block + ee_len <= eof_block;
-
- depth = newdepth;
- ext4_ext_drop_refs(path);
- path = ext4_ext_find_extent(inode, map->m_lblk, path);
- if (IS_ERR(path)) {
- err = PTR_ERR(path);
goto out;
- }
- eh = path[depth].p_hdr;
- ex = path[depth].p_ext;
- if (ex2 != &newex)
- ex2 = ex;
err = ext4_ext_get_access(handle, inode, path + depth);
if (err)
goto out;
-
- allocated = map->m_len;
-
- /* If extent has less than EXT4_EXT_ZERO_LEN and we are trying
- * to insert a extent in the middle zerout directly
- * otherwise give the extent a chance to merge to left
- */
- if (le16_to_cpu(orig_ex.ee_len) <= EXT4_EXT_ZERO_LEN &&
- map->m_lblk != ee_block && may_zeroout) {
- err = ext4_ext_zeroout(inode, &orig_ex);
- if (err)
- goto fix_extent_len;
- /* update the extent length and mark as initialized */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* zero out the first half */
- /* blocks available from map->m_lblk */
- return allocated;
- }
+ ext4_ext_mark_initialized(ex);
+ ext4_ext_try_to_merge(inode, path, ex);
+ err = ext4_ext_dirty(handle, inode, path + depth);
+ goto out;
}
+
/*
- * If there was a change of depth as part of the
- * insertion of ex3 above, we need to update the length
- * of the ex1 extent again here
+ * four cases:
+ * 1. split the extent into three extents.
+ * 2. split the extent into two extents, zeroout the first half.
+ * 3. split the extent into two extents, zeroout the second half.
+ * 4. split the extent into two extents with out zeroout.
*/
- if (ex1 && ex1 != ex) {
- ex1 = ex;
- ex1->ee_len = cpu_to_le16(map->m_lblk - ee_block);
- ext4_ext_mark_uninitialized(ex1);
- ex2 = &newex;
- }
- /* ex2: map->m_lblk to map->m_lblk + maxblocks-1 : initialised */
- ex2->ee_block = cpu_to_le32(map->m_lblk);
- ext4_ext_store_pblock(ex2, newblock);
- ex2->ee_len = cpu_to_le16(allocated);
- if (ex2 != ex)
- goto insert;
- /*
- * New (initialized) extent starts from the first block
- * in the current extent. i.e., ex2 == ex
- * We have to see if it can be merged with the extent
- * on the left.
- */
- if (ex2 > EXT_FIRST_EXTENT(eh)) {
- /*
- * To merge left, pass "ex2 - 1" to try_to_merge(),
- * since it merges towards right _only_.
- */
- ret = ext4_ext_try_to_merge(inode, path, ex2 - 1);
- if (ret) {
- err = ext4_ext_correct_indexes(handle, inode, path);
+ split_map.m_lblk = map->m_lblk;
+ split_map.m_len = map->m_len;
+
+ if (allocated > map->m_len) {
+ if (allocated <= EXT4_EXT_ZERO_LEN &&
+ (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
+ /* case 3 */
+ zero_ex.ee_block =
+ cpu_to_le32(map->m_lblk);
+ zero_ex.ee_len = cpu_to_le16(allocated);
+ ext4_ext_store_pblock(&zero_ex,
+ ext4_ext_pblock(ex) + map->m_lblk - ee_block);
+ err = ext4_ext_zeroout(inode, &zero_ex);
if (err)
goto out;
- depth = ext_depth(inode);
- ex2--;
+ split_map.m_lblk = map->m_lblk;
+ split_map.m_len = allocated;
+ } else if ((map->m_lblk - ee_block + map->m_len <
+ EXT4_EXT_ZERO_LEN) &&
+ (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
+ /* case 2 */
+ if (map->m_lblk != ee_block) {
+ zero_ex.ee_block = ex->ee_block;
+ zero_ex.ee_len = cpu_to_le16(map->m_lblk -
+ ee_block);
+ ext4_ext_store_pblock(&zero_ex,
+ ext4_ext_pblock(ex));
+ err = ext4_ext_zeroout(inode, &zero_ex);
+ if (err)
+ goto out;
+ }
+
+ split_map.m_lblk = ee_block;
+ split_map.m_len = map->m_lblk - ee_block + map->m_len;
+ allocated = map->m_len;
}
}
- /*
- * Try to Merge towards right. This might be required
- * only when the whole extent is being written to.
- * i.e. ex2 == ex and ex3 == NULL.
- */
- if (!ex3) {
- ret = ext4_ext_try_to_merge(inode, path, ex2);
- if (ret) {
- err = ext4_ext_correct_indexes(handle, inode, path);
- if (err)
- goto out;
- }
- }
- /* Mark modified extent as dirty */
- err = ext4_ext_dirty(handle, inode, path + depth);
- goto out;
-insert:
- err = ext4_ext_insert_extent(handle, inode, path, &newex, 0);
- if (err == -ENOSPC && may_zeroout) {
- err = ext4_ext_zeroout(inode, &orig_ex);
- if (err)
- goto fix_extent_len;
- /* update the extent length and mark as initialized */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* zero out the first half */
- return allocated;
- } else if (err)
- goto fix_extent_len;
+
+ allocated = ext4_split_extent(handle, inode, path,
+ &split_map, split_flag, 0);
+ if (allocated < 0)
+ err = allocated;
+
out:
- ext4_ext_show_leaf(inode, path);
return err ? err : allocated;
-
-fix_extent_len:
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_mark_uninitialized(ex);
- ext4_ext_dirty(handle, inode, path + depth);
- return err;
}
/*
@@ -2871,15 +3072,11 @@
struct ext4_ext_path *path,
int flags)
{
- struct ext4_extent *ex, newex, orig_ex;
- struct ext4_extent *ex1 = NULL;
- struct ext4_extent *ex2 = NULL;
- struct ext4_extent *ex3 = NULL;
- ext4_lblk_t ee_block, eof_block;
- unsigned int allocated, ee_len, depth;
- ext4_fsblk_t newblock;
- int err = 0;
- int may_zeroout;
+ ext4_lblk_t eof_block;
+ ext4_lblk_t ee_block;
+ struct ext4_extent *ex;
+ unsigned int ee_len;
+ int split_flag = 0, depth;
ext_debug("ext4_split_unwritten_extents: inode %lu, logical"
"block %llu, max_blocks %u\n", inode->i_ino,
@@ -2889,156 +3086,22 @@
inode->i_sb->s_blocksize_bits;
if (eof_block < map->m_lblk + map->m_len)
eof_block = map->m_lblk + map->m_len;
-
- depth = ext_depth(inode);
- ex = path[depth].p_ext;
- ee_block = le32_to_cpu(ex->ee_block);
- ee_len = ext4_ext_get_actual_len(ex);
- allocated = ee_len - (map->m_lblk - ee_block);
- newblock = map->m_lblk - ee_block + ext4_ext_pblock(ex);
-
- ex2 = ex;
- orig_ex.ee_block = ex->ee_block;
- orig_ex.ee_len = cpu_to_le16(ee_len);
- ext4_ext_store_pblock(&orig_ex, ext4_ext_pblock(ex));
-
/*
* It is safe to convert extent to initialized via explicit
* zeroout only if extent is fully insde i_size or new_size.
*/
- may_zeroout = ee_block + ee_len <= eof_block;
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ ee_block = le32_to_cpu(ex->ee_block);
+ ee_len = ext4_ext_get_actual_len(ex);
- /*
- * If the uninitialized extent begins at the same logical
- * block where the write begins, and the write completely
- * covers the extent, then we don't need to split it.
- */
- if ((map->m_lblk == ee_block) && (allocated <= map->m_len))
- return allocated;
+ split_flag |= ee_block + ee_len <= eof_block ? EXT4_EXT_MAY_ZEROOUT : 0;
+ split_flag |= EXT4_EXT_MARK_UNINIT2;
- err = ext4_ext_get_access(handle, inode, path + depth);
- if (err)
- goto out;
- /* ex1: ee_block to map->m_lblk - 1 : uninitialized */
- if (map->m_lblk > ee_block) {
- ex1 = ex;
- ex1->ee_len = cpu_to_le16(map->m_lblk - ee_block);
- ext4_ext_mark_uninitialized(ex1);
- ex2 = &newex;
- }
- /*
- * for sanity, update the length of the ex2 extent before
- * we insert ex3, if ex1 is NULL. This is to avoid temporary
- * overlap of blocks.
- */
- if (!ex1 && allocated > map->m_len)
- ex2->ee_len = cpu_to_le16(map->m_len);
- /* ex3: to ee_block + ee_len : uninitialised */
- if (allocated > map->m_len) {
- unsigned int newdepth;
- ex3 = &newex;
- ex3->ee_block = cpu_to_le32(map->m_lblk + map->m_len);
- ext4_ext_store_pblock(ex3, newblock + map->m_len);
- ex3->ee_len = cpu_to_le16(allocated - map->m_len);
- ext4_ext_mark_uninitialized(ex3);
- err = ext4_ext_insert_extent(handle, inode, path, ex3, flags);
- if (err == -ENOSPC && may_zeroout) {
- err = ext4_ext_zeroout(inode, &orig_ex);
- if (err)
- goto fix_extent_len;
- /* update the extent length and mark as initialized */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* zeroed the full extent */
- /* blocks available from map->m_lblk */
- return allocated;
-
- } else if (err)
- goto fix_extent_len;
- /*
- * The depth, and hence eh & ex might change
- * as part of the insert above.
- */
- newdepth = ext_depth(inode);
- /*
- * update the extent length after successful insert of the
- * split extent
- */
- ee_len -= ext4_ext_get_actual_len(ex3);
- orig_ex.ee_len = cpu_to_le16(ee_len);
- may_zeroout = ee_block + ee_len <= eof_block;
-
- depth = newdepth;
- ext4_ext_drop_refs(path);
- path = ext4_ext_find_extent(inode, map->m_lblk, path);
- if (IS_ERR(path)) {
- err = PTR_ERR(path);
- goto out;
- }
- ex = path[depth].p_ext;
- if (ex2 != &newex)
- ex2 = ex;
-
- err = ext4_ext_get_access(handle, inode, path + depth);
- if (err)
- goto out;
-
- allocated = map->m_len;
- }
- /*
- * If there was a change of depth as part of the
- * insertion of ex3 above, we need to update the length
- * of the ex1 extent again here
- */
- if (ex1 && ex1 != ex) {
- ex1 = ex;
- ex1->ee_len = cpu_to_le16(map->m_lblk - ee_block);
- ext4_ext_mark_uninitialized(ex1);
- ex2 = &newex;
- }
- /*
- * ex2: map->m_lblk to map->m_lblk + map->m_len-1 : to be written
- * using direct I/O, uninitialised still.
- */
- ex2->ee_block = cpu_to_le32(map->m_lblk);
- ext4_ext_store_pblock(ex2, newblock);
- ex2->ee_len = cpu_to_le16(allocated);
- ext4_ext_mark_uninitialized(ex2);
- if (ex2 != ex)
- goto insert;
- /* Mark modified extent as dirty */
- err = ext4_ext_dirty(handle, inode, path + depth);
- ext_debug("out here\n");
- goto out;
-insert:
- err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
- if (err == -ENOSPC && may_zeroout) {
- err = ext4_ext_zeroout(inode, &orig_ex);
- if (err)
- goto fix_extent_len;
- /* update the extent length and mark as initialized */
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_dirty(handle, inode, path + depth);
- /* zero out the first half */
- return allocated;
- } else if (err)
- goto fix_extent_len;
-out:
- ext4_ext_show_leaf(inode, path);
- return err ? err : allocated;
-
-fix_extent_len:
- ex->ee_block = orig_ex.ee_block;
- ex->ee_len = orig_ex.ee_len;
- ext4_ext_store_pblock(ex, ext4_ext_pblock(&orig_ex));
- ext4_ext_mark_uninitialized(ex);
- ext4_ext_dirty(handle, inode, path + depth);
- return err;
+ flags |= EXT4_GET_BLOCKS_PRE_IO;
+ return ext4_split_extent(handle, inode, path, map, split_flag, flags);
}
+
static int ext4_convert_unwritten_extents_endio(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path)
@@ -3047,46 +3110,27 @@
struct ext4_extent_header *eh;
int depth;
int err = 0;
- int ret = 0;
depth = ext_depth(inode);
eh = path[depth].p_hdr;
ex = path[depth].p_ext;
+ ext_debug("ext4_convert_unwritten_extents_endio: inode %lu, logical"
+ "block %llu, max_blocks %u\n", inode->i_ino,
+ (unsigned long long)le32_to_cpu(ex->ee_block),
+ ext4_ext_get_actual_len(ex));
+
err = ext4_ext_get_access(handle, inode, path + depth);
if (err)
goto out;
/* first mark the extent as initialized */
ext4_ext_mark_initialized(ex);
- /*
- * We have to see if it can be merged with the extent
- * on the left.
+ /* note: ext4_ext_correct_indexes() isn't needed here because
+ * borders are not changed
*/
- if (ex > EXT_FIRST_EXTENT(eh)) {
- /*
- * To merge left, pass "ex - 1" to try_to_merge(),
- * since it merges towards right _only_.
- */
- ret = ext4_ext_try_to_merge(inode, path, ex - 1);
- if (ret) {
- err = ext4_ext_correct_indexes(handle, inode, path);
- if (err)
- goto out;
- depth = ext_depth(inode);
- ex--;
- }
- }
- /*
- * Try to Merge towards right.
- */
- ret = ext4_ext_try_to_merge(inode, path, ex);
- if (ret) {
- err = ext4_ext_correct_indexes(handle, inode, path);
- if (err)
- goto out;
- depth = ext_depth(inode);
- }
+ ext4_ext_try_to_merge(inode, path, ex);
+
/* Mark modified extent as dirty */
err = ext4_ext_dirty(handle, inode, path + depth);
out:
@@ -3302,15 +3346,19 @@
ext4_fsblk_t newblock = 0;
int err = 0, depth, ret;
unsigned int allocated = 0;
+ unsigned int punched_out = 0;
+ unsigned int result = 0;
struct ext4_allocation_request ar;
ext4_io_end_t *io = EXT4_I(inode)->cur_aio_dio;
+ struct ext4_map_blocks punch_map;
ext_debug("blocks %u/%u requested for inode %lu\n",
map->m_lblk, map->m_len, inode->i_ino);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
/* check in cache */
- if (ext4_ext_in_cache(inode, map->m_lblk, &newex)) {
+ if (ext4_ext_in_cache(inode, map->m_lblk, &newex) &&
+ ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0)) {
if (!newex.ee_start_lo && !newex.ee_start_hi) {
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
/*
@@ -3375,16 +3423,84 @@
ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk,
ee_block, ee_len, newblock);
- /* Do not put uninitialized extent in the cache */
- if (!ext4_ext_is_uninitialized(ex)) {
- ext4_ext_put_in_cache(inode, ee_block,
- ee_len, ee_start);
- goto out;
+ if ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0) {
+ /*
+ * Do not put uninitialized extent
+ * in the cache
+ */
+ if (!ext4_ext_is_uninitialized(ex)) {
+ ext4_ext_put_in_cache(inode, ee_block,
+ ee_len, ee_start);
+ goto out;
+ }
+ ret = ext4_ext_handle_uninitialized_extents(
+ handle, inode, map, path, flags,
+ allocated, newblock);
+ return ret;
}
- ret = ext4_ext_handle_uninitialized_extents(handle,
- inode, map, path, flags, allocated,
- newblock);
- return ret;
+
+ /*
+ * Punch out the map length, but only to the
+ * end of the extent
+ */
+ punched_out = allocated < map->m_len ?
+ allocated : map->m_len;
+
+ /*
+ * Sense extents need to be converted to
+ * uninitialized, they must fit in an
+ * uninitialized extent
+ */
+ if (punched_out > EXT_UNINIT_MAX_LEN)
+ punched_out = EXT_UNINIT_MAX_LEN;
+
+ punch_map.m_lblk = map->m_lblk;
+ punch_map.m_pblk = newblock;
+ punch_map.m_len = punched_out;
+ punch_map.m_flags = 0;
+
+ /* Check to see if the extent needs to be split */
+ if (punch_map.m_len != ee_len ||
+ punch_map.m_lblk != ee_block) {
+
+ ret = ext4_split_extent(handle, inode,
+ path, &punch_map, 0,
+ EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
+ EXT4_GET_BLOCKS_PRE_IO);
+
+ if (ret < 0) {
+ err = ret;
+ goto out2;
+ }
+ /*
+ * find extent for the block at
+ * the start of the hole
+ */
+ ext4_ext_drop_refs(path);
+ kfree(path);
+
+ path = ext4_ext_find_extent(inode,
+ map->m_lblk, NULL);
+ if (IS_ERR(path)) {
+ err = PTR_ERR(path);
+ path = NULL;
+ goto out2;
+ }
+
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ ee_len = ext4_ext_get_actual_len(ex);
+ ee_block = le32_to_cpu(ex->ee_block);
+ ee_start = ext4_ext_pblock(ex);
+
+ }
+
+ ext4_ext_mark_uninitialized(ex);
+
+ err = ext4_ext_remove_space(inode, map->m_lblk,
+ map->m_lblk + punched_out);
+
+ goto out2;
}
}
@@ -3446,6 +3562,8 @@
else
/* disable in-core preallocation for non-regular files */
ar.flags = 0;
+ if (flags & EXT4_GET_BLOCKS_NO_NORMALIZE)
+ ar.flags |= EXT4_MB_HINT_NOPREALLOC;
newblock = ext4_mb_new_blocks(handle, &ar, &err);
if (!newblock)
goto out2;
@@ -3529,7 +3647,11 @@
}
trace_ext4_ext_map_blocks_exit(inode, map->m_lblk,
newblock, map->m_len, err ? err : allocated);
- return err ? err : allocated;
+
+ result = (flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) ?
+ punched_out : allocated;
+
+ return err ? err : result;
}
void ext4_ext_truncate(struct inode *inode)
@@ -3577,7 +3699,7 @@
last_block = (inode->i_size + sb->s_blocksize - 1)
>> EXT4_BLOCK_SIZE_BITS(sb);
- err = ext4_ext_remove_space(inode, last_block);
+ err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCK);
/* In a multi-transaction truncate, we only make the final
* transaction synchronous.
@@ -3585,8 +3707,9 @@
if (IS_SYNC(inode))
ext4_handle_sync(handle);
-out_stop:
up_write(&EXT4_I(inode)->i_data_sem);
+
+out_stop:
/*
* If this was a simple ftruncate() and the file will remain alive,
* then we need to clear up the orphan record which we created above.
@@ -3651,10 +3774,6 @@
struct ext4_map_blocks map;
unsigned int credits, blkbits = inode->i_blkbits;
- /* We only support the FALLOC_FL_KEEP_SIZE mode */
- if (mode & ~FALLOC_FL_KEEP_SIZE)
- return -EOPNOTSUPP;
-
/*
* currently supporting (pre)allocate mode for extent-based
* files _only_
@@ -3662,6 +3781,13 @@
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
return -EOPNOTSUPP;
+ /* Return error if mode is not supported */
+ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
+ return -EOPNOTSUPP;
+
+ if (mode & FALLOC_FL_PUNCH_HOLE)
+ return ext4_punch_hole(file, offset, len);
+
trace_ext4_fallocate_enter(inode, offset, len, mode);
map.m_lblk = offset >> blkbits;
/*
@@ -3691,7 +3817,8 @@
break;
}
ret = ext4_map_blocks(handle, inode, &map,
- EXT4_GET_BLOCKS_CREATE_UNINIT_EXT);
+ EXT4_GET_BLOCKS_CREATE_UNINIT_EXT |
+ EXT4_GET_BLOCKS_NO_NORMALIZE);
if (ret <= 0) {
#ifdef EXT4FS_DEBUG
WARN_ON(ret <= 0);
@@ -3822,6 +3949,7 @@
pgoff_t last_offset;
pgoff_t offset;
pgoff_t index;
+ pgoff_t start_index = 0;
struct page **pages = NULL;
struct buffer_head *bh = NULL;
struct buffer_head *head = NULL;
@@ -3848,39 +3976,57 @@
kfree(pages);
return EXT_CONTINUE;
}
+ index = 0;
+next_page:
/* Try to find the 1st mapped buffer. */
- end = ((__u64)pages[0]->index << PAGE_SHIFT) >>
+ end = ((__u64)pages[index]->index << PAGE_SHIFT) >>
blksize_bits;
- if (!page_has_buffers(pages[0]))
+ if (!page_has_buffers(pages[index]))
goto out;
- head = page_buffers(pages[0]);
+ head = page_buffers(pages[index]);
if (!head)
goto out;
+ index++;
bh = head;
do {
- if (buffer_mapped(bh)) {
+ if (end >= newex->ec_block +
+ newex->ec_len)
+ /* The buffer is out of
+ * the request range.
+ */
+ goto out;
+
+ if (buffer_mapped(bh) &&
+ end >= newex->ec_block) {
+ start_index = index - 1;
/* get the 1st mapped buffer. */
- if (end > newex->ec_block +
- newex->ec_len)
- /* The buffer is out of
- * the request range.
- */
- goto out;
goto found_mapped_buffer;
}
+
bh = bh->b_this_page;
end++;
} while (bh != head);
- /* No mapped buffer found. */
- goto out;
+ /* No mapped buffer in the range found in this page,
+ * We need to look up next page.
+ */
+ if (index >= ret) {
+ /* There is no page left, but we need to limit
+ * newex->ec_len.
+ */
+ newex->ec_len = end - newex->ec_block;
+ goto out;
+ }
+ goto next_page;
} else {
/*Find contiguous delayed buffers. */
if (ret > 0 && pages[0]->index == last_offset)
head = page_buffers(pages[0]);
bh = head;
+ index = 1;
+ start_index = 0;
}
found_mapped_buffer:
@@ -3903,7 +4049,7 @@
end++;
} while (bh != head);
- for (index = 1; index < ret; index++) {
+ for (; index < ret; index++) {
if (!page_has_buffers(pages[index])) {
bh = NULL;
break;
@@ -3913,8 +4059,10 @@
bh = NULL;
break;
}
+
if (pages[index]->index !=
- pages[0]->index + index) {
+ pages[start_index]->index + index
+ - start_index) {
/* Blocks are not contiguous. */
bh = NULL;
break;
@@ -4006,6 +4154,177 @@
return (error < 0 ? error : 0);
}
+/*
+ * ext4_ext_punch_hole
+ *
+ * Punches a hole of "length" bytes in a file starting
+ * at byte "offset"
+ *
+ * @inode: The inode of the file to punch a hole in
+ * @offset: The starting byte offset of the hole
+ * @length: The length of the hole
+ *
+ * Returns the number of blocks removed or negative on err
+ */
+int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct ext4_ext_cache cache_ex;
+ ext4_lblk_t first_block, last_block, num_blocks, iblock, max_blocks;
+ struct address_space *mapping = inode->i_mapping;
+ struct ext4_map_blocks map;
+ handle_t *handle;
+ loff_t first_block_offset, last_block_offset, block_len;
+ loff_t first_page, last_page, first_page_offset, last_page_offset;
+ int ret, credits, blocks_released, err = 0;
+
+ first_block = (offset + sb->s_blocksize - 1) >>
+ EXT4_BLOCK_SIZE_BITS(sb);
+ last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);
+
+ first_block_offset = first_block << EXT4_BLOCK_SIZE_BITS(sb);
+ last_block_offset = last_block << EXT4_BLOCK_SIZE_BITS(sb);
+
+ first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ last_page = (offset + length) >> PAGE_CACHE_SHIFT;
+
+ first_page_offset = first_page << PAGE_CACHE_SHIFT;
+ last_page_offset = last_page << PAGE_CACHE_SHIFT;
+
+ /*
+ * Write out all dirty pages to avoid race conditions
+ * Then release them.
+ */
+ if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
+ err = filemap_write_and_wait_range(mapping,
+ first_page_offset == 0 ? 0 : first_page_offset-1,
+ last_page_offset);
+
+ if (err)
+ return err;
+ }
+
+ /* Now release the pages */
+ if (last_page_offset > first_page_offset) {
+ truncate_inode_pages_range(mapping, first_page_offset,
+ last_page_offset-1);
+ }
+
+ /* finish any pending end_io work */
+ ext4_flush_completed_IO(inode);
+
+ credits = ext4_writepage_trans_blocks(inode);
+ handle = ext4_journal_start(inode, credits);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ err = ext4_orphan_add(handle, inode);
+ if (err)
+ goto out;
+
+ /*
+ * Now we need to zero out the un block aligned data.
+ * If the file is smaller than a block, just
+ * zero out the middle
+ */
+ if (first_block > last_block)
+ ext4_block_zero_page_range(handle, mapping, offset, length);
+ else {
+ /* zero out the head of the hole before the first block */
+ block_len = first_block_offset - offset;
+ if (block_len > 0)
+ ext4_block_zero_page_range(handle, mapping,
+ offset, block_len);
+
+ /* zero out the tail of the hole after the last block */
+ block_len = offset + length - last_block_offset;
+ if (block_len > 0) {
+ ext4_block_zero_page_range(handle, mapping,
+ last_block_offset, block_len);
+ }
+ }
+
+ /* If there are no blocks to remove, return now */
+ if (first_block >= last_block)
+ goto out;
+
+ down_write(&EXT4_I(inode)->i_data_sem);
+ ext4_ext_invalidate_cache(inode);
+ ext4_discard_preallocations(inode);
+
+ /*
+ * Loop over all the blocks and identify blocks
+ * that need to be punched out
+ */
+ iblock = first_block;
+ blocks_released = 0;
+ while (iblock < last_block) {
+ max_blocks = last_block - iblock;
+ num_blocks = 1;
+ memset(&map, 0, sizeof(map));
+ map.m_lblk = iblock;
+ map.m_len = max_blocks;
+ ret = ext4_ext_map_blocks(handle, inode, &map,
+ EXT4_GET_BLOCKS_PUNCH_OUT_EXT);
+
+ if (ret > 0) {
+ blocks_released += ret;
+ num_blocks = ret;
+ } else if (ret == 0) {
+ /*
+ * If map blocks could not find the block,
+ * then it is in a hole. If the hole was
+ * not already cached, then map blocks should
+ * put it in the cache. So we can get the hole
+ * out of the cache
+ */
+ memset(&cache_ex, 0, sizeof(cache_ex));
+ if ((ext4_ext_check_cache(inode, iblock, &cache_ex)) &&
+ !cache_ex.ec_start) {
+
+ /* The hole is cached */
+ num_blocks = cache_ex.ec_block +
+ cache_ex.ec_len - iblock;
+
+ } else {
+ /* The block could not be identified */
+ err = -EIO;
+ break;
+ }
+ } else {
+ /* Map blocks error */
+ err = ret;
+ break;
+ }
+
+ if (num_blocks == 0) {
+ /* This condition should never happen */
+ ext_debug("Block lookup failed");
+ err = -EIO;
+ break;
+ }
+
+ iblock += num_blocks;
+ }
+
+ if (blocks_released > 0) {
+ ext4_ext_invalidate_cache(inode);
+ ext4_discard_preallocations(inode);
+ }
+
+ if (IS_SYNC(inode))
+ ext4_handle_sync(handle);
+
+ up_write(&EXT4_I(inode)->i_data_sem);
+
+out:
+ ext4_orphan_del(handle, inode);
+ inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+ ext4_mark_inode_dirty(handle, inode);
+ ext4_journal_stop(handle);
+ return err;
+}
int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len)
{
@@ -4042,4 +4361,3 @@
return error;
}
-
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 7b80d54..2c09723 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -272,7 +272,6 @@
};
const struct inode_operations ext4_file_inode_operations = {
- .truncate = ext4_truncate,
.setattr = ext4_setattr,
.getattr = ext4_getattr,
#ifdef CONFIG_EXT4_FS_XATTR
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index e9473cb..ce66d2f 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c
@@ -36,7 +36,7 @@
static void dump_completed_IO(struct inode * inode)
{
-#ifdef EXT4_DEBUG
+#ifdef EXT4FS_DEBUG
struct list_head *cur, *before, *after;
ext4_io_end_t *io, *io0, *io1;
unsigned long flags;
@@ -172,6 +172,7 @@
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
int ret;
tid_t commit_tid;
+ bool needs_barrier = false;
J_ASSERT(ext4_journal_current_handle() == NULL);
@@ -211,22 +212,12 @@
}
commit_tid = datasync ? ei->i_datasync_tid : ei->i_sync_tid;
- if (jbd2_log_start_commit(journal, commit_tid)) {
- /*
- * When the journal is on a different device than the
- * fs data disk, we need to issue the barrier in
- * writeback mode. (In ordered mode, the jbd2 layer
- * will take care of issuing the barrier. In
- * data=journal, all of the data blocks are written to
- * the journal device.)
- */
- if (ext4_should_writeback_data(inode) &&
- (journal->j_fs_dev != journal->j_dev) &&
- (journal->j_flags & JBD2_BARRIER))
- blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL,
- NULL);
- ret = jbd2_log_wait_commit(journal, commit_tid);
- } else if (journal->j_flags & JBD2_BARRIER)
+ if (journal->j_flags & JBD2_BARRIER &&
+ !jbd2_trans_will_send_data_barrier(journal, commit_tid))
+ needs_barrier = true;
+ jbd2_log_start_commit(journal, commit_tid);
+ ret = jbd2_log_wait_commit(journal, commit_tid);
+ if (needs_barrier)
blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
out:
trace_ext4_sync_file_exit(inode, ret);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f2fa5e8..50d0e9c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -639,8 +639,8 @@
while (target > 0) {
count = target;
/* allocating blocks for indirect blocks and direct blocks */
- current_block = ext4_new_meta_blocks(handle, inode,
- goal, &count, err);
+ current_block = ext4_new_meta_blocks(handle, inode, goal,
+ 0, &count, err);
if (*err)
goto failed_out;
@@ -1930,7 +1930,7 @@
* We do still charge estimated metadata to the sb though;
* we cannot afford to run out of free blocks.
*/
- if (ext4_claim_free_blocks(sbi, md_needed + 1)) {
+ if (ext4_claim_free_blocks(sbi, md_needed + 1, 0)) {
dquot_release_reservation_block(inode, 1);
if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
yield();
@@ -2796,9 +2796,7 @@
continue;
}
- if (PageWriteback(page))
- wait_on_page_writeback(page);
-
+ wait_on_page_writeback(page);
BUG_ON(PageWriteback(page));
if (mpd->next_page != page->index)
@@ -3513,7 +3511,7 @@
loff_t end = offset + iov_length(iov, nr_segs);
if (end > isize)
- vmtruncate(inode, isize);
+ ext4_truncate_failed_write(inode);
}
}
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
@@ -3916,9 +3914,30 @@
int ext4_block_truncate_page(handle_t *handle,
struct address_space *mapping, loff_t from)
{
+ unsigned offset = from & (PAGE_CACHE_SIZE-1);
+ unsigned length;
+ unsigned blocksize;
+ struct inode *inode = mapping->host;
+
+ blocksize = inode->i_sb->s_blocksize;
+ length = blocksize - (offset & (blocksize - 1));
+
+ return ext4_block_zero_page_range(handle, mapping, from, length);
+}
+
+/*
+ * ext4_block_zero_page_range() zeros out a mapping of length 'length'
+ * starting from file offset 'from'. The range to be zero'd must
+ * be contained with in one block. If the specified range exceeds
+ * the end of the block it will be shortened to end of the block
+ * that cooresponds to 'from'
+ */
+int ext4_block_zero_page_range(handle_t *handle,
+ struct address_space *mapping, loff_t from, loff_t length)
+{
ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT;
unsigned offset = from & (PAGE_CACHE_SIZE-1);
- unsigned blocksize, length, pos;
+ unsigned blocksize, max, pos;
ext4_lblk_t iblock;
struct inode *inode = mapping->host;
struct buffer_head *bh;
@@ -3931,7 +3950,15 @@
return -EINVAL;
blocksize = inode->i_sb->s_blocksize;
- length = blocksize - (offset & (blocksize - 1));
+ max = blocksize - (offset & (blocksize - 1));
+
+ /*
+ * correct length if it does not fall between
+ * 'from' and the end of the block
+ */
+ if (length > max || length < 0)
+ length = max;
+
iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
if (!page_has_buffers(page))
@@ -4380,8 +4407,6 @@
int ext4_can_truncate(struct inode *inode)
{
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- return 0;
if (S_ISREG(inode->i_mode))
return 1;
if (S_ISDIR(inode->i_mode))
@@ -4392,6 +4417,31 @@
}
/*
+ * ext4_punch_hole: punches a hole in a file by releaseing the blocks
+ * associated with the given offset and length
+ *
+ * @inode: File inode
+ * @offset: The offset where the hole will begin
+ * @len: The length of the hole
+ *
+ * Returns: 0 on sucess or negative on failure
+ */
+
+int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ if (!S_ISREG(inode->i_mode))
+ return -ENOTSUPP;
+
+ if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+ /* TODO: Add support for non extent hole punching */
+ return -ENOTSUPP;
+ }
+
+ return ext4_ext_punch_hole(file, offset, length);
+}
+
+/*
* ext4_truncate()
*
* We block out ext4_get_block() block instantiations across the entire
@@ -4617,7 +4667,7 @@
/*
* Figure out the offset within the block group inode table
*/
- inodes_per_block = (EXT4_BLOCK_SIZE(sb) / EXT4_INODE_SIZE(sb));
+ inodes_per_block = EXT4_SB(sb)->s_inodes_per_block;
inode_offset = ((inode->i_ino - 1) %
EXT4_INODES_PER_GROUP(sb));
block = ext4_inode_table(sb, gdp) + (inode_offset / inodes_per_block);
@@ -5311,8 +5361,7 @@
if (S_ISREG(inode->i_mode) &&
attr->ia_valid & ATTR_SIZE &&
- (attr->ia_size < inode->i_size ||
- (ext4_test_inode_flag(inode, EXT4_INODE_EOFBLOCKS)))) {
+ (attr->ia_size < inode->i_size)) {
handle_t *handle;
handle = ext4_journal_start(inode, 3);
@@ -5346,14 +5395,15 @@
goto err_out;
}
}
- /* ext4_truncate will clear the flag */
- if ((ext4_test_inode_flag(inode, EXT4_INODE_EOFBLOCKS)))
- ext4_truncate(inode);
}
- if ((attr->ia_valid & ATTR_SIZE) &&
- attr->ia_size != i_size_read(inode))
- rc = vmtruncate(inode, attr->ia_size);
+ if (attr->ia_valid & ATTR_SIZE) {
+ if (attr->ia_size != i_size_read(inode)) {
+ truncate_setsize(inode, attr->ia_size);
+ ext4_truncate(inode);
+ } else if (ext4_test_inode_flag(inode, EXT4_INODE_EOFBLOCKS))
+ ext4_truncate(inode);
+ }
if (!rc) {
setattr_copy(inode, attr);
@@ -5811,15 +5861,19 @@
goto out_unlock;
}
ret = 0;
- if (PageMappedToDisk(page))
- goto out_unlock;
+
+ lock_page(page);
+ wait_on_page_writeback(page);
+ if (PageMappedToDisk(page)) {
+ up_read(&inode->i_alloc_sem);
+ return VM_FAULT_LOCKED;
+ }
if (page->index == size >> PAGE_CACHE_SHIFT)
len = size & ~PAGE_CACHE_MASK;
else
len = PAGE_CACHE_SIZE;
- lock_page(page);
/*
* return if we have all the buffers mapped. This avoid
* the need to call write_begin/write_end which does a
@@ -5829,8 +5883,8 @@
if (page_has_buffers(page)) {
if (!walk_page_buffers(NULL, page_buffers(page), 0, len, NULL,
ext4_bh_unmapped)) {
- unlock_page(page);
- goto out_unlock;
+ up_read(&inode->i_alloc_sem);
+ return VM_FAULT_LOCKED;
}
}
unlock_page(page);
@@ -5850,6 +5904,16 @@
if (ret < 0)
goto out_unlock;
ret = 0;
+
+ /*
+ * write_begin/end might have created a dirty page and someone
+ * could wander in and start the IO. Make sure that hasn't
+ * happened.
+ */
+ lock_page(page);
+ wait_on_page_writeback(page);
+ up_read(&inode->i_alloc_sem);
+ return VM_FAULT_LOCKED;
out_unlock:
if (ret)
ret = VM_FAULT_SIGBUS;
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index d8a16ee..859f2ae 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -787,6 +787,7 @@
struct inode *inode;
char *data;
char *bitmap;
+ struct ext4_group_info *grinfo;
mb_debug(1, "init page %lu\n", page->index);
@@ -819,6 +820,18 @@
if (first_group + i >= ngroups)
break;
+ grinfo = ext4_get_group_info(sb, first_group + i);
+ /*
+ * If page is uptodate then we came here after online resize
+ * which added some new uninitialized group info structs, so
+ * we must skip all initialized uptodate buddies on the page,
+ * which may be currently in use by an allocating task.
+ */
+ if (PageUptodate(page) && !EXT4_MB_GRP_NEED_INIT(grinfo)) {
+ bh[i] = NULL;
+ continue;
+ }
+
err = -EIO;
desc = ext4_get_group_desc(sb, first_group + i, NULL);
if (desc == NULL)
@@ -871,26 +884,28 @@
}
/* wait for I/O completion */
- for (i = 0; i < groups_per_page && bh[i]; i++)
- wait_on_buffer(bh[i]);
+ for (i = 0; i < groups_per_page; i++)
+ if (bh[i])
+ wait_on_buffer(bh[i]);
err = -EIO;
- for (i = 0; i < groups_per_page && bh[i]; i++)
- if (!buffer_uptodate(bh[i]))
+ for (i = 0; i < groups_per_page; i++)
+ if (bh[i] && !buffer_uptodate(bh[i]))
goto out;
err = 0;
first_block = page->index * blocks_per_page;
- /* init the page */
- memset(page_address(page), 0xff, PAGE_CACHE_SIZE);
for (i = 0; i < blocks_per_page; i++) {
int group;
- struct ext4_group_info *grinfo;
group = (first_block + i) >> 1;
if (group >= ngroups)
break;
+ if (!bh[group - first_group])
+ /* skip initialized uptodate buddy */
+ continue;
+
/*
* data carry information regarding this
* particular group in the format specified
@@ -919,6 +934,8 @@
* incore got set to the group block bitmap below
*/
ext4_lock_group(sb, group);
+ /* init the buddy */
+ memset(data, 0xff, blocksize);
ext4_mb_generate_buddy(sb, data, incore, group);
ext4_unlock_group(sb, group);
incore = NULL;
@@ -948,7 +965,7 @@
out:
if (bh) {
- for (i = 0; i < groups_per_page && bh[i]; i++)
+ for (i = 0; i < groups_per_page; i++)
brelse(bh[i]);
if (bh != &bhs)
kfree(bh);
@@ -957,22 +974,21 @@
}
/*
- * lock the group_info alloc_sem of all the groups
- * belonging to the same buddy cache page. This
- * make sure other parallel operation on the buddy
- * cache doesn't happen whild holding the buddy cache
- * lock
+ * Lock the buddy and bitmap pages. This make sure other parallel init_group
+ * on the same buddy page doesn't happen whild holding the buddy page lock.
+ * Return locked buddy and bitmap pages on e4b struct. If buddy and bitmap
+ * are on the same page e4b->bd_buddy_page is NULL and return value is 0.
*/
-static int ext4_mb_get_buddy_cache_lock(struct super_block *sb,
- ext4_group_t group)
+static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
+ ext4_group_t group, struct ext4_buddy *e4b)
{
- int i;
- int block, pnum;
+ struct inode *inode = EXT4_SB(sb)->s_buddy_cache;
+ int block, pnum, poff;
int blocks_per_page;
- int groups_per_page;
- ext4_group_t ngroups = ext4_get_groups_count(sb);
- ext4_group_t first_group;
- struct ext4_group_info *grp;
+ struct page *page;
+
+ e4b->bd_buddy_page = NULL;
+ e4b->bd_bitmap_page = NULL;
blocks_per_page = PAGE_CACHE_SIZE / sb->s_blocksize;
/*
@@ -982,57 +998,40 @@
*/
block = group * 2;
pnum = block / blocks_per_page;
- first_group = pnum * blocks_per_page / 2;
+ poff = block % blocks_per_page;
+ page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
+ if (!page)
+ return -EIO;
+ BUG_ON(page->mapping != inode->i_mapping);
+ e4b->bd_bitmap_page = page;
+ e4b->bd_bitmap = page_address(page) + (poff * sb->s_blocksize);
- groups_per_page = blocks_per_page >> 1;
- if (groups_per_page == 0)
- groups_per_page = 1;
- /* read all groups the page covers into the cache */
- for (i = 0; i < groups_per_page; i++) {
-
- if ((first_group + i) >= ngroups)
- break;
- grp = ext4_get_group_info(sb, first_group + i);
- /* take all groups write allocation
- * semaphore. This make sure there is
- * no block allocation going on in any
- * of that groups
- */
- down_write_nested(&grp->alloc_sem, i);
+ if (blocks_per_page >= 2) {
+ /* buddy and bitmap are on the same page */
+ return 0;
}
- return i;
+
+ block++;
+ pnum = block / blocks_per_page;
+ poff = block % blocks_per_page;
+ page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
+ if (!page)
+ return -EIO;
+ BUG_ON(page->mapping != inode->i_mapping);
+ e4b->bd_buddy_page = page;
+ return 0;
}
-static void ext4_mb_put_buddy_cache_lock(struct super_block *sb,
- ext4_group_t group, int locked_group)
+static void ext4_mb_put_buddy_page_lock(struct ext4_buddy *e4b)
{
- int i;
- int block, pnum;
- int blocks_per_page;
- ext4_group_t first_group;
- struct ext4_group_info *grp;
-
- blocks_per_page = PAGE_CACHE_SIZE / sb->s_blocksize;
- /*
- * the buddy cache inode stores the block bitmap
- * and buddy information in consecutive blocks.
- * So for each group we need two blocks.
- */
- block = group * 2;
- pnum = block / blocks_per_page;
- first_group = pnum * blocks_per_page / 2;
- /* release locks on all the groups */
- for (i = 0; i < locked_group; i++) {
-
- grp = ext4_get_group_info(sb, first_group + i);
- /* take all groups write allocation
- * semaphore. This make sure there is
- * no block allocation going on in any
- * of that groups
- */
- up_write(&grp->alloc_sem);
+ if (e4b->bd_bitmap_page) {
+ unlock_page(e4b->bd_bitmap_page);
+ page_cache_release(e4b->bd_bitmap_page);
}
-
+ if (e4b->bd_buddy_page) {
+ unlock_page(e4b->bd_buddy_page);
+ page_cache_release(e4b->bd_buddy_page);
+ }
}
/*
@@ -1044,93 +1043,60 @@
int ext4_mb_init_group(struct super_block *sb, ext4_group_t group)
{
- int ret = 0;
- void *bitmap;
- int blocks_per_page;
- int block, pnum, poff;
- int num_grp_locked = 0;
struct ext4_group_info *this_grp;
- struct ext4_sb_info *sbi = EXT4_SB(sb);
- struct inode *inode = sbi->s_buddy_cache;
- struct page *page = NULL, *bitmap_page = NULL;
+ struct ext4_buddy e4b;
+ struct page *page;
+ int ret = 0;
mb_debug(1, "init group %u\n", group);
- blocks_per_page = PAGE_CACHE_SIZE / sb->s_blocksize;
this_grp = ext4_get_group_info(sb, group);
/*
* This ensures that we don't reinit the buddy cache
* page which map to the group from which we are already
* allocating. If we are looking at the buddy cache we would
* have taken a reference using ext4_mb_load_buddy and that
- * would have taken the alloc_sem lock.
+ * would have pinned buddy page to page cache.
*/
- num_grp_locked = ext4_mb_get_buddy_cache_lock(sb, group);
- if (!EXT4_MB_GRP_NEED_INIT(this_grp)) {
+ ret = ext4_mb_get_buddy_page_lock(sb, group, &e4b);
+ if (ret || !EXT4_MB_GRP_NEED_INIT(this_grp)) {
/*
* somebody initialized the group
* return without doing anything
*/
- ret = 0;
goto err;
}
- /*
- * the buddy cache inode stores the block bitmap
- * and buddy information in consecutive blocks.
- * So for each group we need two blocks.
- */
- block = group * 2;
- pnum = block / blocks_per_page;
- poff = block % blocks_per_page;
- page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
- if (page) {
- BUG_ON(page->mapping != inode->i_mapping);
- ret = ext4_mb_init_cache(page, NULL);
- if (ret) {
- unlock_page(page);
- goto err;
- }
- unlock_page(page);
- }
- if (page == NULL || !PageUptodate(page)) {
+
+ page = e4b.bd_bitmap_page;
+ ret = ext4_mb_init_cache(page, NULL);
+ if (ret)
+ goto err;
+ if (!PageUptodate(page)) {
ret = -EIO;
goto err;
}
mark_page_accessed(page);
- bitmap_page = page;
- bitmap = page_address(page) + (poff * sb->s_blocksize);
- /* init buddy cache */
- block++;
- pnum = block / blocks_per_page;
- poff = block % blocks_per_page;
- page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
- if (page == bitmap_page) {
+ if (e4b.bd_buddy_page == NULL) {
/*
* If both the bitmap and buddy are in
* the same page we don't need to force
* init the buddy
*/
- unlock_page(page);
- } else if (page) {
- BUG_ON(page->mapping != inode->i_mapping);
- ret = ext4_mb_init_cache(page, bitmap);
- if (ret) {
- unlock_page(page);
- goto err;
- }
- unlock_page(page);
+ ret = 0;
+ goto err;
}
- if (page == NULL || !PageUptodate(page)) {
+ /* init buddy cache */
+ page = e4b.bd_buddy_page;
+ ret = ext4_mb_init_cache(page, e4b.bd_bitmap);
+ if (ret)
+ goto err;
+ if (!PageUptodate(page)) {
ret = -EIO;
goto err;
}
mark_page_accessed(page);
err:
- ext4_mb_put_buddy_cache_lock(sb, group, num_grp_locked);
- if (bitmap_page)
- page_cache_release(bitmap_page);
- if (page)
- page_cache_release(page);
+ ext4_mb_put_buddy_page_lock(&e4b);
return ret;
}
@@ -1164,24 +1130,8 @@
e4b->bd_group = group;
e4b->bd_buddy_page = NULL;
e4b->bd_bitmap_page = NULL;
- e4b->alloc_semp = &grp->alloc_sem;
-
- /* Take the read lock on the group alloc
- * sem. This would make sure a parallel
- * ext4_mb_init_group happening on other
- * groups mapped by the page is blocked
- * till we are done with allocation
- */
-repeat_load_buddy:
- down_read(e4b->alloc_semp);
if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) {
- /* we need to check for group need init flag
- * with alloc_semp held so that we can be sure
- * that new blocks didn't get added to the group
- * when we are loading the buddy cache
- */
- up_read(e4b->alloc_semp);
/*
* we need full data about the group
* to make a good selection
@@ -1189,7 +1139,6 @@
ret = ext4_mb_init_group(sb, group);
if (ret)
return ret;
- goto repeat_load_buddy;
}
/*
@@ -1273,15 +1222,14 @@
return 0;
err:
+ if (page)
+ page_cache_release(page);
if (e4b->bd_bitmap_page)
page_cache_release(e4b->bd_bitmap_page);
if (e4b->bd_buddy_page)
page_cache_release(e4b->bd_buddy_page);
e4b->bd_buddy = NULL;
e4b->bd_bitmap = NULL;
-
- /* Done with the buddy cache */
- up_read(e4b->alloc_semp);
return ret;
}
@@ -1291,9 +1239,6 @@
page_cache_release(e4b->bd_bitmap_page);
if (e4b->bd_buddy_page)
page_cache_release(e4b->bd_buddy_page);
- /* Done with the buddy cache */
- if (e4b->alloc_semp)
- up_read(e4b->alloc_semp);
}
@@ -1606,9 +1551,6 @@
get_page(ac->ac_bitmap_page);
ac->ac_buddy_page = e4b->bd_buddy_page;
get_page(ac->ac_buddy_page);
- /* on allocation we use ac to track the held semaphore */
- ac->alloc_semp = e4b->alloc_semp;
- e4b->alloc_semp = NULL;
/* store last allocated for subsequent stream allocation */
if (ac->ac_flags & EXT4_MB_STREAM_ALLOC) {
spin_lock(&sbi->s_md_lock);
@@ -2659,7 +2601,7 @@
struct super_block *sb = journal->j_private;
struct ext4_buddy e4b;
struct ext4_group_info *db;
- int err, ret, count = 0, count2 = 0;
+ int err, count = 0, count2 = 0;
struct ext4_free_data *entry;
struct list_head *l, *ltmp;
@@ -2669,15 +2611,9 @@
mb_debug(1, "gonna free %u blocks in group %u (0x%p):",
entry->count, entry->group, entry);
- if (test_opt(sb, DISCARD)) {
- ret = ext4_issue_discard(sb, entry->group,
- entry->start_blk, entry->count);
- if (unlikely(ret == -EOPNOTSUPP)) {
- ext4_warning(sb, "discard not supported, "
- "disabling");
- clear_opt(sb, DISCARD);
- }
- }
+ if (test_opt(sb, DISCARD))
+ ext4_issue_discard(sb, entry->group,
+ entry->start_blk, entry->count);
err = ext4_mb_load_buddy(sb, entry->group, &e4b);
/* we expect to find existing buddy because it's pinned */
@@ -4226,15 +4162,12 @@
spin_unlock(&pa->pa_lock);
}
}
- if (ac->alloc_semp)
- up_read(ac->alloc_semp);
if (pa) {
/*
* We want to add the pa to the right bucket.
* Remove it from the list and while adding
* make sure the list to which we are adding
- * doesn't grow big. We need to release
- * alloc_semp before calling ext4_mb_add_n_trim()
+ * doesn't grow big.
*/
if ((pa->pa_type == MB_GROUP_PA) && likely(pa->pa_free)) {
spin_lock(pa->pa_obj_lock);
@@ -4303,7 +4236,9 @@
* there is enough free blocks to do block allocation
* and verify allocation doesn't exceed the quota limits.
*/
- while (ar->len && ext4_claim_free_blocks(sbi, ar->len)) {
+ while (ar->len &&
+ ext4_claim_free_blocks(sbi, ar->len, ar->flags)) {
+
/* let others to free the space */
yield();
ar->len = ar->len >> 1;
@@ -4313,9 +4248,15 @@
return 0;
}
reserv_blks = ar->len;
- while (ar->len && dquot_alloc_block(ar->inode, ar->len)) {
- ar->flags |= EXT4_MB_HINT_NOPREALLOC;
- ar->len--;
+ if (ar->flags & EXT4_MB_USE_ROOT_BLOCKS) {
+ dquot_alloc_block_nofail(ar->inode, ar->len);
+ } else {
+ while (ar->len &&
+ dquot_alloc_block(ar->inode, ar->len)) {
+
+ ar->flags |= EXT4_MB_HINT_NOPREALLOC;
+ ar->len--;
+ }
}
inquota = ar->len;
if (ar->len == 0) {
@@ -4704,6 +4645,127 @@
}
/**
+ * ext4_add_groupblocks() -- Add given blocks to an existing group
+ * @handle: handle to this transaction
+ * @sb: super block
+ * @block: start physcial block to add to the block group
+ * @count: number of blocks to free
+ *
+ * This marks the blocks as free in the bitmap and buddy.
+ */
+void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
+ ext4_fsblk_t block, unsigned long count)
+{
+ struct buffer_head *bitmap_bh = NULL;
+ struct buffer_head *gd_bh;
+ ext4_group_t block_group;
+ ext4_grpblk_t bit;
+ unsigned int i;
+ struct ext4_group_desc *desc;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ struct ext4_buddy e4b;
+ int err = 0, ret, blk_free_count;
+ ext4_grpblk_t blocks_freed;
+ struct ext4_group_info *grp;
+
+ ext4_debug("Adding block(s) %llu-%llu\n", block, block + count - 1);
+
+ ext4_get_group_no_and_offset(sb, block, &block_group, &bit);
+ grp = ext4_get_group_info(sb, block_group);
+ /*
+ * Check to see if we are freeing blocks across a group
+ * boundary.
+ */
+ if (bit + count > EXT4_BLOCKS_PER_GROUP(sb))
+ goto error_return;
+
+ bitmap_bh = ext4_read_block_bitmap(sb, block_group);
+ if (!bitmap_bh)
+ goto error_return;
+ desc = ext4_get_group_desc(sb, block_group, &gd_bh);
+ if (!desc)
+ goto error_return;
+
+ if (in_range(ext4_block_bitmap(sb, desc), block, count) ||
+ in_range(ext4_inode_bitmap(sb, desc), block, count) ||
+ in_range(block, ext4_inode_table(sb, desc), sbi->s_itb_per_group) ||
+ in_range(block + count - 1, ext4_inode_table(sb, desc),
+ sbi->s_itb_per_group)) {
+ ext4_error(sb, "Adding blocks in system zones - "
+ "Block = %llu, count = %lu",
+ block, count);
+ goto error_return;
+ }
+
+ BUFFER_TRACE(bitmap_bh, "getting write access");
+ err = ext4_journal_get_write_access(handle, bitmap_bh);
+ if (err)
+ goto error_return;
+
+ /*
+ * We are about to modify some metadata. Call the journal APIs
+ * to unshare ->b_data if a currently-committing transaction is
+ * using it
+ */
+ BUFFER_TRACE(gd_bh, "get_write_access");
+ err = ext4_journal_get_write_access(handle, gd_bh);
+ if (err)
+ goto error_return;
+
+ for (i = 0, blocks_freed = 0; i < count; i++) {
+ BUFFER_TRACE(bitmap_bh, "clear bit");
+ if (!mb_test_bit(bit + i, bitmap_bh->b_data)) {
+ ext4_error(sb, "bit already cleared for block %llu",
+ (ext4_fsblk_t)(block + i));
+ BUFFER_TRACE(bitmap_bh, "bit already cleared");
+ } else {
+ blocks_freed++;
+ }
+ }
+
+ err = ext4_mb_load_buddy(sb, block_group, &e4b);
+ if (err)
+ goto error_return;
+
+ /*
+ * need to update group_info->bb_free and bitmap
+ * with group lock held. generate_buddy look at
+ * them with group lock_held
+ */
+ ext4_lock_group(sb, block_group);
+ mb_clear_bits(bitmap_bh->b_data, bit, count);
+ mb_free_blocks(NULL, &e4b, bit, count);
+ blk_free_count = blocks_freed + ext4_free_blks_count(sb, desc);
+ ext4_free_blks_set(sb, desc, blk_free_count);
+ desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc);
+ ext4_unlock_group(sb, block_group);
+ percpu_counter_add(&sbi->s_freeblocks_counter, blocks_freed);
+
+ if (sbi->s_log_groups_per_flex) {
+ ext4_group_t flex_group = ext4_flex_group(sbi, block_group);
+ atomic_add(blocks_freed,
+ &sbi->s_flex_groups[flex_group].free_blocks);
+ }
+
+ ext4_mb_unload_buddy(&e4b);
+
+ /* We dirtied the bitmap block */
+ BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
+ err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
+
+ /* And the group descriptor block */
+ BUFFER_TRACE(gd_bh, "dirtied group descriptor block");
+ ret = ext4_handle_dirty_metadata(handle, NULL, gd_bh);
+ if (!err)
+ err = ret;
+
+error_return:
+ brelse(bitmap_bh);
+ ext4_std_error(sb, err);
+ return;
+}
+
+/**
* ext4_trim_extent -- function to TRIM one single free extent in the group
* @sb: super block for the file system
* @start: starting block of the free extent in the alloc. group
@@ -4715,11 +4777,10 @@
* one will allocate those blocks, mark it as used in buddy bitmap. This must
* be called with under the group lock.
*/
-static int ext4_trim_extent(struct super_block *sb, int start, int count,
- ext4_group_t group, struct ext4_buddy *e4b)
+static void ext4_trim_extent(struct super_block *sb, int start, int count,
+ ext4_group_t group, struct ext4_buddy *e4b)
{
struct ext4_free_extent ex;
- int ret = 0;
assert_spin_locked(ext4_group_lock_ptr(sb, group));
@@ -4733,12 +4794,9 @@
*/
mb_mark_used(e4b, &ex);
ext4_unlock_group(sb, group);
-
- ret = ext4_issue_discard(sb, group, start, count);
-
+ ext4_issue_discard(sb, group, start, count);
ext4_lock_group(sb, group);
mb_free_blocks(NULL, e4b, start, ex.fe_len);
- return ret;
}
/**
@@ -4760,21 +4818,26 @@
* the group buddy bitmap. This is done until whole group is scanned.
*/
static ext4_grpblk_t
-ext4_trim_all_free(struct super_block *sb, struct ext4_buddy *e4b,
- ext4_grpblk_t start, ext4_grpblk_t max, ext4_grpblk_t minblocks)
+ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ext4_grpblk_t start, ext4_grpblk_t max,
+ ext4_grpblk_t minblocks)
{
void *bitmap;
ext4_grpblk_t next, count = 0;
- ext4_group_t group;
- int ret = 0;
+ struct ext4_buddy e4b;
+ int ret;
- BUG_ON(e4b == NULL);
+ ret = ext4_mb_load_buddy(sb, group, &e4b);
+ if (ret) {
+ ext4_error(sb, "Error in loading buddy "
+ "information for %u", group);
+ return ret;
+ }
+ bitmap = e4b.bd_bitmap;
- bitmap = e4b->bd_bitmap;
- group = e4b->bd_group;
- start = (e4b->bd_info->bb_first_free > start) ?
- e4b->bd_info->bb_first_free : start;
ext4_lock_group(sb, group);
+ start = (e4b.bd_info->bb_first_free > start) ?
+ e4b.bd_info->bb_first_free : start;
while (start < max) {
start = mb_find_next_zero_bit(bitmap, max, start);
@@ -4783,10 +4846,8 @@
next = mb_find_next_bit(bitmap, max, start);
if ((next - start) >= minblocks) {
- ret = ext4_trim_extent(sb, start,
- next - start, group, e4b);
- if (ret < 0)
- break;
+ ext4_trim_extent(sb, start,
+ next - start, group, &e4b);
count += next - start;
}
start = next + 1;
@@ -4802,17 +4863,15 @@
ext4_lock_group(sb, group);
}
- if ((e4b->bd_info->bb_free - count) < minblocks)
+ if ((e4b.bd_info->bb_free - count) < minblocks)
break;
}
ext4_unlock_group(sb, group);
+ ext4_mb_unload_buddy(&e4b);
ext4_debug("trimmed %d blocks in the group %d\n",
count, group);
- if (ret < 0)
- count = ret;
-
return count;
}
@@ -4830,11 +4889,11 @@
*/
int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
{
- struct ext4_buddy e4b;
+ struct ext4_group_info *grp;
ext4_group_t first_group, last_group;
ext4_group_t group, ngroups = ext4_get_groups_count(sb);
ext4_grpblk_t cnt = 0, first_block, last_block;
- uint64_t start, len, minlen, trimmed;
+ uint64_t start, len, minlen, trimmed = 0;
ext4_fsblk_t first_data_blk =
le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
int ret = 0;
@@ -4842,7 +4901,6 @@
start = range->start >> sb->s_blocksize_bits;
len = range->len >> sb->s_blocksize_bits;
minlen = range->minlen >> sb->s_blocksize_bits;
- trimmed = 0;
if (unlikely(minlen > EXT4_BLOCKS_PER_GROUP(sb)))
return -EINVAL;
@@ -4863,11 +4921,12 @@
return -EINVAL;
for (group = first_group; group <= last_group; group++) {
- ret = ext4_mb_load_buddy(sb, group, &e4b);
- if (ret) {
- ext4_error(sb, "Error in loading buddy "
- "information for %u", group);
- break;
+ grp = ext4_get_group_info(sb, group);
+ /* We only do this if the grp has never been initialized */
+ if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) {
+ ret = ext4_mb_init_group(sb, group);
+ if (ret)
+ break;
}
/*
@@ -4880,16 +4939,14 @@
last_block = first_block + len;
len -= last_block - first_block;
- if (e4b.bd_info->bb_free >= minlen) {
- cnt = ext4_trim_all_free(sb, &e4b, first_block,
+ if (grp->bb_free >= minlen) {
+ cnt = ext4_trim_all_free(sb, group, first_block,
last_block, minlen);
if (cnt < 0) {
ret = cnt;
- ext4_mb_unload_buddy(&e4b);
break;
}
}
- ext4_mb_unload_buddy(&e4b);
trimmed += cnt;
first_block = 0;
}
diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
index 22bd4d7..20b5e7b 100644
--- a/fs/ext4/mballoc.h
+++ b/fs/ext4/mballoc.h
@@ -193,11 +193,6 @@
__u8 ac_op; /* operation, for history only */
struct page *ac_bitmap_page;
struct page *ac_buddy_page;
- /*
- * pointer to the held semaphore upon successful
- * block allocation
- */
- struct rw_semaphore *alloc_semp;
struct ext4_prealloc_space *ac_pa;
struct ext4_locality_group *ac_lg;
};
@@ -215,7 +210,6 @@
struct super_block *bd_sb;
__u16 bd_blkbits;
ext4_group_t bd_group;
- struct rw_semaphore *alloc_semp;
};
#define EXT4_MB_BITMAP(e4b) ((e4b)->bd_bitmap)
#define EXT4_MB_BUDDY(e4b) ((e4b)->bd_buddy)
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index 92816b4..b57b98f 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -376,7 +376,7 @@
* We have the extent map build with the tmp inode.
* Now copy the i_data across
*/
- ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS);
+ ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
memcpy(ei->i_data, tmp_ei->i_data, sizeof(ei->i_data));
/*
diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c
new file mode 100644
index 0000000..9bdef3f
--- /dev/null
+++ b/fs/ext4/mmp.c
@@ -0,0 +1,351 @@
+#include <linux/fs.h>
+#include <linux/random.h>
+#include <linux/buffer_head.h>
+#include <linux/utsname.h>
+#include <linux/kthread.h>
+
+#include "ext4.h"
+
+/*
+ * Write the MMP block using WRITE_SYNC to try to get the block on-disk
+ * faster.
+ */
+static int write_mmp_block(struct buffer_head *bh)
+{
+ mark_buffer_dirty(bh);
+ lock_buffer(bh);
+ bh->b_end_io = end_buffer_write_sync;
+ get_bh(bh);
+ submit_bh(WRITE_SYNC, bh);
+ wait_on_buffer(bh);
+ if (unlikely(!buffer_uptodate(bh)))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Read the MMP block. It _must_ be read from disk and hence we clear the
+ * uptodate flag on the buffer.
+ */
+static int read_mmp_block(struct super_block *sb, struct buffer_head **bh,
+ ext4_fsblk_t mmp_block)
+{
+ struct mmp_struct *mmp;
+
+ if (*bh)
+ clear_buffer_uptodate(*bh);
+
+ /* This would be sb_bread(sb, mmp_block), except we need to be sure
+ * that the MD RAID device cache has been bypassed, and that the read
+ * is not blocked in the elevator. */
+ if (!*bh)
+ *bh = sb_getblk(sb, mmp_block);
+ if (*bh) {
+ get_bh(*bh);
+ lock_buffer(*bh);
+ (*bh)->b_end_io = end_buffer_read_sync;
+ submit_bh(READ_SYNC, *bh);
+ wait_on_buffer(*bh);
+ if (!buffer_uptodate(*bh)) {
+ brelse(*bh);
+ *bh = NULL;
+ }
+ }
+ if (!*bh) {
+ ext4_warning(sb, "Error while reading MMP block %llu",
+ mmp_block);
+ return -EIO;
+ }
+
+ mmp = (struct mmp_struct *)((*bh)->b_data);
+ if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Dump as much information as possible to help the admin.
+ */
+void __dump_mmp_msg(struct super_block *sb, struct mmp_struct *mmp,
+ const char *function, unsigned int line, const char *msg)
+{
+ __ext4_warning(sb, function, line, msg);
+ __ext4_warning(sb, function, line,
+ "MMP failure info: last update time: %llu, last update "
+ "node: %s, last update device: %s\n",
+ (long long unsigned int) le64_to_cpu(mmp->mmp_time),
+ mmp->mmp_nodename, mmp->mmp_bdevname);
+}
+
+/*
+ * kmmpd will update the MMP sequence every s_mmp_update_interval seconds
+ */
+static int kmmpd(void *data)
+{
+ struct super_block *sb = ((struct mmpd_data *) data)->sb;
+ struct buffer_head *bh = ((struct mmpd_data *) data)->bh;
+ struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+ struct mmp_struct *mmp;
+ ext4_fsblk_t mmp_block;
+ u32 seq = 0;
+ unsigned long failed_writes = 0;
+ int mmp_update_interval = le16_to_cpu(es->s_mmp_update_interval);
+ unsigned mmp_check_interval;
+ unsigned long last_update_time;
+ unsigned long diff;
+ int retval;
+
+ mmp_block = le64_to_cpu(es->s_mmp_block);
+ mmp = (struct mmp_struct *)(bh->b_data);
+ mmp->mmp_time = cpu_to_le64(get_seconds());
+ /*
+ * Start with the higher mmp_check_interval and reduce it if
+ * the MMP block is being updated on time.
+ */
+ mmp_check_interval = max(EXT4_MMP_CHECK_MULT * mmp_update_interval,
+ EXT4_MMP_MIN_CHECK_INTERVAL);
+ mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval);
+ bdevname(bh->b_bdev, mmp->mmp_bdevname);
+
+ memcpy(mmp->mmp_nodename, init_utsname()->sysname,
+ sizeof(mmp->mmp_nodename));
+
+ while (!kthread_should_stop()) {
+ if (++seq > EXT4_MMP_SEQ_MAX)
+ seq = 1;
+
+ mmp->mmp_seq = cpu_to_le32(seq);
+ mmp->mmp_time = cpu_to_le64(get_seconds());
+ last_update_time = jiffies;
+
+ retval = write_mmp_block(bh);
+ /*
+ * Don't spew too many error messages. Print one every
+ * (s_mmp_update_interval * 60) seconds.
+ */
+ if (retval && (failed_writes % 60) == 0) {
+ ext4_error(sb, "Error writing to MMP block");
+ failed_writes++;
+ }
+
+ if (!(le32_to_cpu(es->s_feature_incompat) &
+ EXT4_FEATURE_INCOMPAT_MMP)) {
+ ext4_warning(sb, "kmmpd being stopped since MMP feature"
+ " has been disabled.");
+ EXT4_SB(sb)->s_mmp_tsk = NULL;
+ goto failed;
+ }
+
+ if (sb->s_flags & MS_RDONLY) {
+ ext4_warning(sb, "kmmpd being stopped since filesystem "
+ "has been remounted as readonly.");
+ EXT4_SB(sb)->s_mmp_tsk = NULL;
+ goto failed;
+ }
+
+ diff = jiffies - last_update_time;
+ if (diff < mmp_update_interval * HZ)
+ schedule_timeout_interruptible(mmp_update_interval *
+ HZ - diff);
+
+ /*
+ * We need to make sure that more than mmp_check_interval
+ * seconds have not passed since writing. If that has happened
+ * we need to check if the MMP block is as we left it.
+ */
+ diff = jiffies - last_update_time;
+ if (diff > mmp_check_interval * HZ) {
+ struct buffer_head *bh_check = NULL;
+ struct mmp_struct *mmp_check;
+
+ retval = read_mmp_block(sb, &bh_check, mmp_block);
+ if (retval) {
+ ext4_error(sb, "error reading MMP data: %d",
+ retval);
+
+ EXT4_SB(sb)->s_mmp_tsk = NULL;
+ goto failed;
+ }
+
+ mmp_check = (struct mmp_struct *)(bh_check->b_data);
+ if (mmp->mmp_seq != mmp_check->mmp_seq ||
+ memcmp(mmp->mmp_nodename, mmp_check->mmp_nodename,
+ sizeof(mmp->mmp_nodename))) {
+ dump_mmp_msg(sb, mmp_check,
+ "Error while updating MMP info. "
+ "The filesystem seems to have been"
+ " multiply mounted.");
+ ext4_error(sb, "abort");
+ goto failed;
+ }
+ put_bh(bh_check);
+ }
+
+ /*
+ * Adjust the mmp_check_interval depending on how much time
+ * it took for the MMP block to be written.
+ */
+ mmp_check_interval = max(min(EXT4_MMP_CHECK_MULT * diff / HZ,
+ EXT4_MMP_MAX_CHECK_INTERVAL),
+ EXT4_MMP_MIN_CHECK_INTERVAL);
+ mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval);
+ }
+
+ /*
+ * Unmount seems to be clean.
+ */
+ mmp->mmp_seq = cpu_to_le32(EXT4_MMP_SEQ_CLEAN);
+ mmp->mmp_time = cpu_to_le64(get_seconds());
+
+ retval = write_mmp_block(bh);
+
+failed:
+ kfree(data);
+ brelse(bh);
+ return retval;
+}
+
+/*
+ * Get a random new sequence number but make sure it is not greater than
+ * EXT4_MMP_SEQ_MAX.
+ */
+static unsigned int mmp_new_seq(void)
+{
+ u32 new_seq;
+
+ do {
+ get_random_bytes(&new_seq, sizeof(u32));
+ } while (new_seq > EXT4_MMP_SEQ_MAX);
+
+ return new_seq;
+}
+
+/*
+ * Protect the filesystem from being mounted more than once.
+ */
+int ext4_multi_mount_protect(struct super_block *sb,
+ ext4_fsblk_t mmp_block)
+{
+ struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+ struct buffer_head *bh = NULL;
+ struct mmp_struct *mmp = NULL;
+ struct mmpd_data *mmpd_data;
+ u32 seq;
+ unsigned int mmp_check_interval = le16_to_cpu(es->s_mmp_update_interval);
+ unsigned int wait_time = 0;
+ int retval;
+
+ if (mmp_block < le32_to_cpu(es->s_first_data_block) ||
+ mmp_block >= ext4_blocks_count(es)) {
+ ext4_warning(sb, "Invalid MMP block in superblock");
+ goto failed;
+ }
+
+ retval = read_mmp_block(sb, &bh, mmp_block);
+ if (retval)
+ goto failed;
+
+ mmp = (struct mmp_struct *)(bh->b_data);
+
+ if (mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL)
+ mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL;
+
+ /*
+ * If check_interval in MMP block is larger, use that instead of
+ * update_interval from the superblock.
+ */
+ if (mmp->mmp_check_interval > mmp_check_interval)
+ mmp_check_interval = mmp->mmp_check_interval;
+
+ seq = le32_to_cpu(mmp->mmp_seq);
+ if (seq == EXT4_MMP_SEQ_CLEAN)
+ goto skip;
+
+ if (seq == EXT4_MMP_SEQ_FSCK) {
+ dump_mmp_msg(sb, mmp, "fsck is running on the filesystem");
+ goto failed;
+ }
+
+ wait_time = min(mmp_check_interval * 2 + 1,
+ mmp_check_interval + 60);
+
+ /* Print MMP interval if more than 20 secs. */
+ if (wait_time > EXT4_MMP_MIN_CHECK_INTERVAL * 4)
+ ext4_warning(sb, "MMP interval %u higher than expected, please"
+ " wait.\n", wait_time * 2);
+
+ if (schedule_timeout_interruptible(HZ * wait_time) != 0) {
+ ext4_warning(sb, "MMP startup interrupted, failing mount\n");
+ goto failed;
+ }
+
+ retval = read_mmp_block(sb, &bh, mmp_block);
+ if (retval)
+ goto failed;
+ mmp = (struct mmp_struct *)(bh->b_data);
+ if (seq != le32_to_cpu(mmp->mmp_seq)) {
+ dump_mmp_msg(sb, mmp,
+ "Device is already active on another node.");
+ goto failed;
+ }
+
+skip:
+ /*
+ * write a new random sequence number.
+ */
+ mmp->mmp_seq = seq = cpu_to_le32(mmp_new_seq());
+
+ retval = write_mmp_block(bh);
+ if (retval)
+ goto failed;
+
+ /*
+ * wait for MMP interval and check mmp_seq.
+ */
+ if (schedule_timeout_interruptible(HZ * wait_time) != 0) {
+ ext4_warning(sb, "MMP startup interrupted, failing mount\n");
+ goto failed;
+ }
+
+ retval = read_mmp_block(sb, &bh, mmp_block);
+ if (retval)
+ goto failed;
+ mmp = (struct mmp_struct *)(bh->b_data);
+ if (seq != le32_to_cpu(mmp->mmp_seq)) {
+ dump_mmp_msg(sb, mmp,
+ "Device is already active on another node.");
+ goto failed;
+ }
+
+ mmpd_data = kmalloc(sizeof(struct mmpd_data), GFP_KERNEL);
+ if (!mmpd_data) {
+ ext4_warning(sb, "not enough memory for mmpd_data");
+ goto failed;
+ }
+ mmpd_data->sb = sb;
+ mmpd_data->bh = bh;
+
+ /*
+ * Start a kernel thread to update the MMP block periodically.
+ */
+ EXT4_SB(sb)->s_mmp_tsk = kthread_run(kmmpd, mmpd_data, "kmmpd-%s",
+ bdevname(bh->b_bdev,
+ mmp->mmp_bdevname));
+ if (IS_ERR(EXT4_SB(sb)->s_mmp_tsk)) {
+ EXT4_SB(sb)->s_mmp_tsk = NULL;
+ kfree(mmpd_data);
+ ext4_warning(sb, "Unable to create kmmpd thread for %s.",
+ sb->s_id);
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ brelse(bh);
+ return 1;
+}
+
+
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index b9f3e78..2b8304b 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -876,8 +876,7 @@
* It needs to call wait_on_page_writeback() to wait for the
* writeback of the page.
*/
- if (PageWriteback(page))
- wait_on_page_writeback(page);
+ wait_on_page_writeback(page);
/* Release old bh and drop refs */
try_to_release_page(page, 0);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 67fd0b0..b754b77 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1413,10 +1413,22 @@
frame->at = entries;
frame->bh = bh;
bh = bh2;
+
+ ext4_handle_dirty_metadata(handle, dir, frame->bh);
+ ext4_handle_dirty_metadata(handle, dir, bh);
+
de = do_split(handle,dir, &bh, frame, &hinfo, &retval);
- dx_release (frames);
- if (!(de))
+ if (!de) {
+ /*
+ * Even if the block split failed, we have to properly write
+ * out all the changes we did so far. Otherwise we can end up
+ * with corrupted filesystem.
+ */
+ ext4_mark_inode_dirty(handle, dir);
+ dx_release(frames);
return retval;
+ }
+ dx_release(frames);
retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
brelse(bh);
@@ -2240,6 +2252,7 @@
handle_t *handle;
struct inode *inode;
int l, err, retries = 0;
+ int credits;
l = strlen(symname)+1;
if (l > dir->i_sb->s_blocksize)
@@ -2247,10 +2260,26 @@
dquot_initialize(dir);
+ if (l > EXT4_N_BLOCKS * 4) {
+ /*
+ * For non-fast symlinks, we just allocate inode and put it on
+ * orphan list in the first transaction => we need bitmap,
+ * group descriptor, sb, inode block, quota blocks.
+ */
+ credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
+ } else {
+ /*
+ * Fast symlink. We have to add entry to directory
+ * (EXT4_DATA_TRANS_BLOCKS + EXT4_INDEX_EXTRA_TRANS_BLOCKS),
+ * allocate new inode (bitmap, group descriptor, inode block,
+ * quota blocks, sb is already counted in previous macros).
+ */
+ credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
+ EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
+ EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
+ }
retry:
- handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
- EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 +
- EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
+ handle = ext4_journal_start(dir, credits);
if (IS_ERR(handle))
return PTR_ERR(handle);
@@ -2263,21 +2292,44 @@
if (IS_ERR(inode))
goto out_stop;
- if (l > sizeof(EXT4_I(inode)->i_data)) {
+ if (l > EXT4_N_BLOCKS * 4) {
inode->i_op = &ext4_symlink_inode_operations;
ext4_set_aops(inode);
/*
- * page_symlink() calls into ext4_prepare/commit_write.
- * We have a transaction open. All is sweetness. It also sets
- * i_size in generic_commit_write().
+ * We cannot call page_symlink() with transaction started
+ * because it calls into ext4_write_begin() which can wait
+ * for transaction commit if we are running out of space
+ * and thus we deadlock. So we have to stop transaction now
+ * and restart it when symlink contents is written.
+ *
+ * To keep fs consistent in case of crash, we have to put inode
+ * to orphan list in the mean time.
*/
+ drop_nlink(inode);
+ err = ext4_orphan_add(handle, inode);
+ ext4_journal_stop(handle);
+ if (err)
+ goto err_drop_inode;
err = __page_symlink(inode, symname, l, 1);
+ if (err)
+ goto err_drop_inode;
+ /*
+ * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS
+ * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
+ */
+ handle = ext4_journal_start(dir,
+ EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
+ EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1);
+ if (IS_ERR(handle)) {
+ err = PTR_ERR(handle);
+ goto err_drop_inode;
+ }
+ inc_nlink(inode);
+ err = ext4_orphan_del(handle, inode);
if (err) {
+ ext4_journal_stop(handle);
clear_nlink(inode);
- unlock_new_inode(inode);
- ext4_mark_inode_dirty(handle, inode);
- iput(inode);
- goto out_stop;
+ goto err_drop_inode;
}
} else {
/* clear the extent format for fast symlink */
@@ -2293,6 +2345,10 @@
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
goto retry;
return err;
+err_drop_inode:
+ unlock_new_inode(inode);
+ iput(inode);
+ return err;
}
static int ext4_link(struct dentry *old_dentry,
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index b6dbd05..7bb8f76 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -203,46 +203,29 @@
for (i = 0; i < io_end->num_io_pages; i++) {
struct page *page = io_end->pages[i]->p_page;
struct buffer_head *bh, *head;
- int partial_write = 0;
+ loff_t offset;
+ loff_t io_end_offset;
- head = page_buffers(page);
- if (error)
+ if (error) {
SetPageError(page);
- BUG_ON(!head);
- if (head->b_size != PAGE_CACHE_SIZE) {
- loff_t offset;
- loff_t io_end_offset = io_end->offset + io_end->size;
+ set_bit(AS_EIO, &page->mapping->flags);
+ head = page_buffers(page);
+ BUG_ON(!head);
+
+ io_end_offset = io_end->offset + io_end->size;
offset = (sector_t) page->index << PAGE_CACHE_SHIFT;
bh = head;
do {
if ((offset >= io_end->offset) &&
- (offset+bh->b_size <= io_end_offset)) {
- if (error)
- buffer_io_error(bh);
+ (offset+bh->b_size <= io_end_offset))
+ buffer_io_error(bh);
- }
- if (buffer_delay(bh))
- partial_write = 1;
- else if (!buffer_mapped(bh))
- clear_buffer_dirty(bh);
- else if (buffer_dirty(bh))
- partial_write = 1;
offset += bh->b_size;
bh = bh->b_this_page;
} while (bh != head);
}
- /*
- * If this is a partial write which happened to make
- * all buffers uptodate then we can optimize away a
- * bogus readpage() for the next read(). Here we
- * 'discover' whether the page went uptodate as a
- * result of this (potentially partial) write.
- */
- if (!partial_write)
- SetPageUptodate(page);
-
put_io_page(io_end->pages[i]);
}
io_end->num_io_pages = 0;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 8553dfb..cc5c157 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -38,6 +38,7 @@
#include <linux/ctype.h>
#include <linux/log2.h>
#include <linux/crc16.h>
+#include <linux/cleancache.h>
#include <asm/uaccess.h>
#include <linux/kthread.h>
@@ -75,11 +76,27 @@
static int ext4_freeze(struct super_block *sb);
static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data);
+static inline int ext2_feature_set_ok(struct super_block *sb);
+static inline int ext3_feature_set_ok(struct super_block *sb);
static int ext4_feature_set_ok(struct super_block *sb, int readonly);
static void ext4_destroy_lazyinit_thread(void);
static void ext4_unregister_li_request(struct super_block *sb);
static void ext4_clear_request_list(void);
+#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
+static struct file_system_type ext2_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "ext2",
+ .mount = ext4_mount,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+#define IS_EXT2_SB(sb) ((sb)->s_bdev->bd_holder == &ext2_fs_type)
+#else
+#define IS_EXT2_SB(sb) (0)
+#endif
+
+
#if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
static struct file_system_type ext3_fs_type = {
.owner = THIS_MODULE,
@@ -806,6 +823,8 @@
invalidate_bdev(sbi->journal_bdev);
ext4_blkdev_remove(sbi);
}
+ if (sbi->s_mmp_tsk)
+ kthread_stop(sbi->s_mmp_tsk);
sb->s_fs_info = NULL;
/*
* Now that we are completely done shutting down the
@@ -1096,7 +1115,7 @@
if (!test_opt(sb, INIT_INODE_TABLE))
seq_puts(seq, ",noinit_inode_table");
- else if (sbi->s_li_wait_mult)
+ else if (sbi->s_li_wait_mult != EXT4_DEF_LI_WAIT_MULT)
seq_printf(seq, ",init_inode_table=%u",
(unsigned) sbi->s_li_wait_mult);
@@ -1187,9 +1206,7 @@
const char *data, size_t len, loff_t off);
static const struct dquot_operations ext4_quota_operations = {
-#ifdef CONFIG_QUOTA
.get_reserved_space = ext4_get_reserved_space,
-#endif
.write_dquot = ext4_write_dquot,
.acquire_dquot = ext4_acquire_dquot,
.release_dquot = ext4_release_dquot,
@@ -1900,7 +1917,7 @@
ext4_msg(sb, KERN_WARNING,
"warning: mounting fs with errors, "
"running e2fsck is recommended");
- else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &&
+ else if ((__s16) le16_to_cpu(es->s_max_mnt_count) > 0 &&
le16_to_cpu(es->s_mnt_count) >=
(unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))
ext4_msg(sb, KERN_WARNING,
@@ -1932,6 +1949,7 @@
EXT4_INODES_PER_GROUP(sb),
sbi->s_mount_opt, sbi->s_mount_opt2);
+ cleancache_init_fs(sb);
return res;
}
@@ -2425,6 +2443,18 @@
EXT4_SB(sb)->s_sectors_written_start) >> 1)));
}
+static ssize_t extent_cache_hits_show(struct ext4_attr *a,
+ struct ext4_sb_info *sbi, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%lu\n", sbi->extent_cache_hits);
+}
+
+static ssize_t extent_cache_misses_show(struct ext4_attr *a,
+ struct ext4_sb_info *sbi, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%lu\n", sbi->extent_cache_misses);
+}
+
static ssize_t inode_readahead_blks_store(struct ext4_attr *a,
struct ext4_sb_info *sbi,
const char *buf, size_t count)
@@ -2482,6 +2512,8 @@
EXT4_RO_ATTR(delayed_allocation_blocks);
EXT4_RO_ATTR(session_write_kbytes);
EXT4_RO_ATTR(lifetime_write_kbytes);
+EXT4_RO_ATTR(extent_cache_hits);
+EXT4_RO_ATTR(extent_cache_misses);
EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, sbi_ui_show,
inode_readahead_blks_store, s_inode_readahead_blks);
EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal);
@@ -2497,6 +2529,8 @@
ATTR_LIST(delayed_allocation_blocks),
ATTR_LIST(session_write_kbytes),
ATTR_LIST(lifetime_write_kbytes),
+ ATTR_LIST(extent_cache_hits),
+ ATTR_LIST(extent_cache_misses),
ATTR_LIST(inode_readahead_blks),
ATTR_LIST(inode_goal),
ATTR_LIST(mb_stats),
@@ -2659,12 +2693,6 @@
mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); /* Once a day */
}
-static void ext4_lazyinode_timeout(unsigned long data)
-{
- struct task_struct *p = (struct task_struct *)data;
- wake_up_process(p);
-}
-
/* Find next suitable group and run ext4_init_inode_table */
static int ext4_run_li_request(struct ext4_li_request *elr)
{
@@ -2696,11 +2724,8 @@
ret = ext4_init_inode_table(sb, group,
elr->lr_timeout ? 0 : 1);
if (elr->lr_timeout == 0) {
- timeout = jiffies - timeout;
- if (elr->lr_sbi->s_li_wait_mult)
- timeout *= elr->lr_sbi->s_li_wait_mult;
- else
- timeout *= 20;
+ timeout = (jiffies - timeout) *
+ elr->lr_sbi->s_li_wait_mult;
elr->lr_timeout = timeout;
}
elr->lr_next_sched = jiffies + elr->lr_timeout;
@@ -2712,7 +2737,7 @@
/*
* Remove lr_request from the list_request and free the
- * request tructure. Should be called with li_list_mtx held
+ * request structure. Should be called with li_list_mtx held
*/
static void ext4_remove_li_request(struct ext4_li_request *elr)
{
@@ -2730,14 +2755,16 @@
static void ext4_unregister_li_request(struct super_block *sb)
{
- struct ext4_li_request *elr = EXT4_SB(sb)->s_li_request;
-
- if (!ext4_li_info)
+ mutex_lock(&ext4_li_mtx);
+ if (!ext4_li_info) {
+ mutex_unlock(&ext4_li_mtx);
return;
+ }
mutex_lock(&ext4_li_info->li_list_mtx);
- ext4_remove_li_request(elr);
+ ext4_remove_li_request(EXT4_SB(sb)->s_li_request);
mutex_unlock(&ext4_li_info->li_list_mtx);
+ mutex_unlock(&ext4_li_mtx);
}
static struct task_struct *ext4_lazyinit_task;
@@ -2756,17 +2783,10 @@
struct ext4_lazy_init *eli = (struct ext4_lazy_init *)arg;
struct list_head *pos, *n;
struct ext4_li_request *elr;
- unsigned long next_wakeup;
- DEFINE_WAIT(wait);
+ unsigned long next_wakeup, cur;
BUG_ON(NULL == eli);
- eli->li_timer.data = (unsigned long)current;
- eli->li_timer.function = ext4_lazyinode_timeout;
-
- eli->li_task = current;
- wake_up(&eli->li_wait_task);
-
cont_thread:
while (true) {
next_wakeup = MAX_JIFFY_OFFSET;
@@ -2797,19 +2817,15 @@
if (freezing(current))
refrigerator();
- if ((time_after_eq(jiffies, next_wakeup)) ||
+ cur = jiffies;
+ if ((time_after_eq(cur, next_wakeup)) ||
(MAX_JIFFY_OFFSET == next_wakeup)) {
cond_resched();
continue;
}
- eli->li_timer.expires = next_wakeup;
- add_timer(&eli->li_timer);
- prepare_to_wait(&eli->li_wait_daemon, &wait,
- TASK_INTERRUPTIBLE);
- if (time_before(jiffies, next_wakeup))
- schedule();
- finish_wait(&eli->li_wait_daemon, &wait);
+ schedule_timeout_interruptible(next_wakeup - cur);
+
if (kthread_should_stop()) {
ext4_clear_request_list();
goto exit_thread;
@@ -2833,12 +2849,7 @@
goto cont_thread;
}
mutex_unlock(&eli->li_list_mtx);
- del_timer_sync(&ext4_li_info->li_timer);
- eli->li_task = NULL;
- wake_up(&eli->li_wait_task);
-
kfree(ext4_li_info);
- ext4_lazyinit_task = NULL;
ext4_li_info = NULL;
mutex_unlock(&ext4_li_mtx);
@@ -2866,7 +2877,6 @@
if (IS_ERR(ext4_lazyinit_task)) {
int err = PTR_ERR(ext4_lazyinit_task);
ext4_clear_request_list();
- del_timer_sync(&ext4_li_info->li_timer);
kfree(ext4_li_info);
ext4_li_info = NULL;
printk(KERN_CRIT "EXT4: error %d creating inode table "
@@ -2875,8 +2885,6 @@
return err;
}
ext4_li_info->li_state |= EXT4_LAZYINIT_RUNNING;
-
- wait_event(ext4_li_info->li_wait_task, ext4_li_info->li_task != NULL);
return 0;
}
@@ -2911,13 +2919,9 @@
if (!eli)
return -ENOMEM;
- eli->li_task = NULL;
INIT_LIST_HEAD(&eli->li_request_list);
mutex_init(&eli->li_list_mtx);
- init_waitqueue_head(&eli->li_wait_daemon);
- init_waitqueue_head(&eli->li_wait_task);
- init_timer(&eli->li_timer);
eli->li_state |= EXT4_LAZYINIT_QUIT;
ext4_li_info = eli;
@@ -2960,20 +2964,19 @@
ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count;
int ret = 0;
- if (sbi->s_li_request != NULL)
+ if (sbi->s_li_request != NULL) {
+ /*
+ * Reset timeout so it can be computed again, because
+ * s_li_wait_mult might have changed.
+ */
+ sbi->s_li_request->lr_timeout = 0;
return 0;
+ }
if (first_not_zeroed == ngroups ||
(sb->s_flags & MS_RDONLY) ||
- !test_opt(sb, INIT_INODE_TABLE)) {
- sbi->s_li_request = NULL;
+ !test_opt(sb, INIT_INODE_TABLE))
return 0;
- }
-
- if (first_not_zeroed == ngroups) {
- sbi->s_li_request = NULL;
- return 0;
- }
elr = ext4_li_request_new(sb, first_not_zeroed);
if (!elr)
@@ -3166,6 +3169,12 @@
((def_mount_opts & EXT4_DEFM_NODELALLOC) == 0))
set_opt(sb, DELALLOC);
+ /*
+ * set default s_li_wait_mult for lazyinit, for the case there is
+ * no mount option specified.
+ */
+ sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
+
if (!parse_options((char *) sbi->s_es->s_mount_opts, sb,
&journal_devnum, &journal_ioprio, NULL, 0)) {
ext4_msg(sb, KERN_WARNING,
@@ -3187,6 +3196,28 @@
"feature flags set on rev 0 fs, "
"running e2fsck is recommended");
+ if (IS_EXT2_SB(sb)) {
+ if (ext2_feature_set_ok(sb))
+ ext4_msg(sb, KERN_INFO, "mounting ext2 file system "
+ "using the ext4 subsystem");
+ else {
+ ext4_msg(sb, KERN_ERR, "couldn't mount as ext2 due "
+ "to feature incompatibilities");
+ goto failed_mount;
+ }
+ }
+
+ if (IS_EXT3_SB(sb)) {
+ if (ext3_feature_set_ok(sb))
+ ext4_msg(sb, KERN_INFO, "mounting ext3 file system "
+ "using the ext4 subsystem");
+ else {
+ ext4_msg(sb, KERN_ERR, "couldn't mount as ext3 due "
+ "to feature incompatibilities");
+ goto failed_mount;
+ }
+ }
+
/*
* Check feature flags regardless of the revision level, since we
* previously didn't change the revision level when setting the flags,
@@ -3459,6 +3490,11 @@
EXT4_HAS_INCOMPAT_FEATURE(sb,
EXT4_FEATURE_INCOMPAT_RECOVER));
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_MMP) &&
+ !(sb->s_flags & MS_RDONLY))
+ if (ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)))
+ goto failed_mount3;
+
/*
* The first inode we look at is the journal inode. Don't try
* root first: it may be modified in the journal!
@@ -3474,7 +3510,6 @@
goto failed_mount_wq;
} else {
clear_opt(sb, DATA_FLAGS);
- set_opt(sb, WRITEBACK_DATA);
sbi->s_journal = NULL;
needs_recovery = 0;
goto no_journal;
@@ -3707,6 +3742,8 @@
percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter);
percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
+ if (sbi->s_mmp_tsk)
+ kthread_stop(sbi->s_mmp_tsk);
failed_mount2:
for (i = 0; i < db_count; i++)
brelse(sbi->s_group_desc[i]);
@@ -4242,7 +4279,7 @@
int enable_quota = 0;
ext4_group_t g;
unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
- int err;
+ int err = 0;
#ifdef CONFIG_QUOTA
int i;
#endif
@@ -4368,6 +4405,13 @@
goto restore_opts;
if (!ext4_setup_super(sb, es, 0))
sb->s_flags &= ~MS_RDONLY;
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_MMP))
+ if (ext4_multi_mount_protect(sb,
+ le64_to_cpu(es->s_mmp_block))) {
+ err = -EROFS;
+ goto restore_opts;
+ }
enable_quota = 1;
}
}
@@ -4432,6 +4476,7 @@
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_super_block *es = sbi->s_es;
u64 fsid;
+ s64 bfree;
if (test_opt(sb, MINIX_DF)) {
sbi->s_overhead_last = 0;
@@ -4475,8 +4520,10 @@
buf->f_type = EXT4_SUPER_MAGIC;
buf->f_bsize = sb->s_blocksize;
buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last;
- buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
+ bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter);
+ /* prevent underflow in case that few free space is available */
+ buf->f_bfree = max_t(s64, bfree, 0);
buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);
if (buf->f_bfree < ext4_r_blocks_count(es))
buf->f_bavail = 0;
@@ -4652,6 +4699,9 @@
if (test_opt(sb, DELALLOC))
sync_filesystem(sb);
+ if (!inode)
+ goto out;
+
/* Update modification times of quota files when userspace can
* start looking at them */
handle = ext4_journal_start(inode, 1);
@@ -4772,14 +4822,6 @@
}
#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
-static struct file_system_type ext2_fs_type = {
- .owner = THIS_MODULE,
- .name = "ext2",
- .mount = ext4_mount,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
-};
-
static inline void register_as_ext2(void)
{
int err = register_filesystem(&ext2_fs_type);
@@ -4792,10 +4834,22 @@
{
unregister_filesystem(&ext2_fs_type);
}
+
+static inline int ext2_feature_set_ok(struct super_block *sb)
+{
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))
+ return 0;
+ if (sb->s_flags & MS_RDONLY)
+ return 1;
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))
+ return 0;
+ return 1;
+}
MODULE_ALIAS("ext2");
#else
static inline void register_as_ext2(void) { }
static inline void unregister_as_ext2(void) { }
+static inline int ext2_feature_set_ok(struct super_block *sb) { return 0; }
#endif
#if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
@@ -4811,10 +4865,24 @@
{
unregister_filesystem(&ext3_fs_type);
}
+
+static inline int ext3_feature_set_ok(struct super_block *sb)
+{
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb, ~EXT3_FEATURE_INCOMPAT_SUPP))
+ return 0;
+ if (!EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL))
+ return 0;
+ if (sb->s_flags & MS_RDONLY)
+ return 1;
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP))
+ return 0;
+ return 1;
+}
MODULE_ALIAS("ext3");
#else
static inline void register_as_ext3(void) { }
static inline void unregister_as_ext3(void) { }
+static inline int ext3_feature_set_ok(struct super_block *sb) { return 0; }
#endif
static struct file_system_type ext4_fs_type = {
@@ -4898,8 +4966,8 @@
err = init_inodecache();
if (err)
goto out1;
- register_as_ext2();
register_as_ext3();
+ register_as_ext2();
err = register_filesystem(&ext4_fs_type);
if (err)
goto out;
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index b545ca1..c757adc 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -820,8 +820,8 @@
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
goal = goal & EXT4_MAX_BLOCK_FILE_PHYS;
- block = ext4_new_meta_blocks(handle, inode,
- goal, NULL, &error);
+ block = ext4_new_meta_blocks(handle, inode, goal, 0,
+ NULL, &error);
if (error)
goto cleanup;
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
index 3b222da..be15437 100644
--- a/fs/fat/namei_msdos.c
+++ b/fs/fat/namei_msdos.c
@@ -326,6 +326,8 @@
struct fat_slot_info sinfo;
int err;
+ dentry_unhash(dentry);
+
lock_super(sb);
/*
* Check whether the directory is not in use, then check
@@ -457,6 +459,9 @@
old_inode = old_dentry->d_inode;
new_inode = new_dentry->d_inode;
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
err = fat_scan(old_dir, old_name, &old_sinfo);
if (err) {
err = -EIO;
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
index 20b4ea5..c61a678 100644
--- a/fs/fat/namei_vfat.c
+++ b/fs/fat/namei_vfat.c
@@ -824,6 +824,8 @@
struct fat_slot_info sinfo;
int err;
+ dentry_unhash(dentry);
+
lock_super(sb);
err = fat_dir_empty(inode);
@@ -931,6 +933,9 @@
int err, is_dir, update_dotdot, corrupt = 0;
struct super_block *sb = old_dir->i_sb;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
old_inode = old_dentry->d_inode;
new_inode = new_dentry->d_inode;
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index b32eb29..0d0e3fa 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -667,6 +667,8 @@
if (IS_ERR(req))
return PTR_ERR(req);
+ dentry_unhash(entry);
+
req->in.h.opcode = FUSE_RMDIR;
req->in.h.nodeid = get_node_id(dir);
req->in.numargs = 1;
@@ -691,6 +693,10 @@
struct fuse_rename_in inarg;
struct fuse_conn *fc = get_fuse_conn(olddir);
struct fuse_req *req = fuse_get_req(fc);
+
+ if (newent->d_inode && S_ISDIR(newent->d_inode->i_mode))
+ dentry_unhash(newent);
+
if (IS_ERR(req))
return PTR_ERR(req);
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index cfa327d..c2b34cd 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -146,7 +146,7 @@
gfs2_register_debugfs();
- printk("GFS2 (built %s %s) installed\n", __DATE__, __TIME__);
+ printk("GFS2 installed\n");
return 0;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index b4d70b1..1cb70cd 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -253,6 +253,9 @@
struct inode *inode = dentry->d_inode;
int res;
+ if (S_ISDIR(inode->i_mode))
+ dentry_unhash(dentry);
+
if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
return -ENOTEMPTY;
res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
@@ -283,6 +286,9 @@
/* Unlink destination if it already exists */
if (new_dentry->d_inode) {
+ if (S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
res = hfs_remove(new_dir, new_dentry);
if (res)
return res;
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 4df5059..b288350 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -370,6 +370,8 @@
struct inode *inode = dentry->d_inode;
int res;
+ dentry_unhash(dentry);
+
if (inode->i_size != 2)
return -ENOTEMPTY;
@@ -467,10 +469,12 @@
/* Unlink destination if it already exists */
if (new_dentry->d_inode) {
- if (S_ISDIR(new_dentry->d_inode->i_mode))
+ if (S_ISDIR(new_dentry->d_inode->i_mode)) {
+ dentry_unhash(new_dentry);
res = hfsplus_rmdir(new_dir, new_dentry);
- else
+ } else {
res = hfsplus_unlink(new_dir, new_dentry);
+ }
if (res)
return res;
}
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 2638c834e..e6816b9 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -683,6 +683,8 @@
char *file;
int err;
+ dentry_unhash(dentry);
+
if ((file = dentry_name(dentry)) == NULL)
return -ENOMEM;
err = do_rmdir(file);
@@ -736,6 +738,9 @@
char *from_name, *to_name;
int err;
+ if (to->d_inode && S_ISDIR(to->d_inode->i_mode))
+ dentry_unhash(to);
+
if ((from_name = dentry_name(from)) == NULL)
return -ENOMEM;
if ((to_name = dentry_name(to)) == NULL) {
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index 1f05839..ff0ce21 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -395,7 +395,6 @@
dentry_unhash(dentry);
if (!d_unhashed(dentry)) {
- dput(dentry);
hpfs_unlock(dir->i_sb);
return -ENOSPC;
}
@@ -403,7 +402,6 @@
!S_ISREG(inode->i_mode) ||
get_write_access(inode)) {
d_rehash(dentry);
- dput(dentry);
} else {
struct iattr newattrs;
/*printk("HPFS: truncating file before delete.\n");*/
@@ -411,7 +409,6 @@
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
put_write_access(inode);
- dput(dentry);
if (!err)
goto again;
}
@@ -442,6 +439,8 @@
int err;
int r;
+ dentry_unhash(dentry);
+
hpfs_adjust_length(name, &len);
hpfs_lock(dir->i_sb);
err = -ENOENT;
@@ -535,6 +534,10 @@
struct buffer_head *bh;
struct fnode *fnode;
int err;
+
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
if ((err = hpfs_chk_name(new_name, &new_len))) return err;
err = 0;
hpfs_adjust_length(old_name, &old_len);
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index e7a0357..7aafeb8 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -921,7 +921,8 @@
return capable(CAP_IPC_LOCK) || in_group_p(sysctl_hugetlb_shm_group);
}
-struct file *hugetlb_file_setup(const char *name, size_t size, int acctflag,
+struct file *hugetlb_file_setup(const char *name, size_t size,
+ vm_flags_t acctflag,
struct user_struct **user, int creat_flags)
{
int error = -ENOMEM;
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 29148a8..7f21cf3 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -219,7 +219,6 @@
ret = err;
spin_lock(&journal->j_list_lock);
J_ASSERT(jinode->i_transaction == commit_transaction);
- commit_transaction->t_flushed_data_blocks = 1;
clear_bit(__JI_COMMIT_RUNNING, &jinode->i_flags);
smp_mb__after_clear_bit();
wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING);
@@ -672,12 +671,16 @@
err = 0;
}
+ write_lock(&journal->j_state_lock);
+ J_ASSERT(commit_transaction->t_state == T_COMMIT);
+ commit_transaction->t_state = T_COMMIT_DFLUSH;
+ write_unlock(&journal->j_state_lock);
/*
* If the journal is not located on the file system device,
* then we must flush the file system device before we issue
* the commit record
*/
- if (commit_transaction->t_flushed_data_blocks &&
+ if (commit_transaction->t_need_data_flush &&
(journal->j_fs_dev != journal->j_dev) &&
(journal->j_flags & JBD2_BARRIER))
blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL);
@@ -754,8 +757,13 @@
required. */
JBUFFER_TRACE(jh, "file as BJ_Forget");
jbd2_journal_file_buffer(jh, commit_transaction, BJ_Forget);
- /* Wake up any transactions which were waiting for this
- IO to complete */
+ /*
+ * Wake up any transactions which were waiting for this IO to
+ * complete. The barrier must be here so that changes by
+ * jbd2_journal_file_buffer() take effect before wake_up_bit()
+ * does the waitqueue check.
+ */
+ smp_mb();
wake_up_bit(&bh->b_state, BH_Unshadow);
JBUFFER_TRACE(jh, "brelse shadowed buffer");
__brelse(bh);
@@ -794,6 +802,10 @@
jbd2_journal_abort(journal, err);
jbd_debug(3, "JBD: commit phase 5\n");
+ write_lock(&journal->j_state_lock);
+ J_ASSERT(commit_transaction->t_state == T_COMMIT_DFLUSH);
+ commit_transaction->t_state = T_COMMIT_JFLUSH;
+ write_unlock(&journal->j_state_lock);
if (!JBD2_HAS_INCOMPAT_FEATURE(journal,
JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) {
@@ -949,7 +961,7 @@
jbd_debug(3, "JBD: commit phase 7\n");
- J_ASSERT(commit_transaction->t_state == T_COMMIT);
+ J_ASSERT(commit_transaction->t_state == T_COMMIT_JFLUSH);
commit_transaction->t_start = jiffies;
stats.run.rs_logging = jbd2_time_diff(stats.run.rs_logging,
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index e0ec3db..9a78269 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -479,9 +479,12 @@
int __jbd2_log_start_commit(journal_t *journal, tid_t target)
{
/*
- * Are we already doing a recent enough commit?
+ * The only transaction we can possibly wait upon is the
+ * currently running transaction (if it exists). Otherwise,
+ * the target tid must be an old one.
*/
- if (!tid_geq(journal->j_commit_request, target)) {
+ if (journal->j_running_transaction &&
+ journal->j_running_transaction->t_tid == target) {
/*
* We want a new commit: OK, mark the request and wakeup the
* commit thread. We do _not_ do the commit ourselves.
@@ -493,7 +496,15 @@
journal->j_commit_sequence);
wake_up(&journal->j_wait_commit);
return 1;
- }
+ } else if (!tid_geq(journal->j_commit_request, target))
+ /* This should never happen, but if it does, preserve
+ the evidence before kjournald goes into a loop and
+ increments j_commit_sequence beyond all recognition. */
+ WARN_ONCE(1, "jbd: bad log_start_commit: %u %u %u %u\n",
+ journal->j_commit_request,
+ journal->j_commit_sequence,
+ target, journal->j_running_transaction ?
+ journal->j_running_transaction->t_tid : 0);
return 0;
}
@@ -577,6 +588,47 @@
}
/*
+ * Return 1 if a given transaction has not yet sent barrier request
+ * connected with a transaction commit. If 0 is returned, transaction
+ * may or may not have sent the barrier. Used to avoid sending barrier
+ * twice in common cases.
+ */
+int jbd2_trans_will_send_data_barrier(journal_t *journal, tid_t tid)
+{
+ int ret = 0;
+ transaction_t *commit_trans;
+
+ if (!(journal->j_flags & JBD2_BARRIER))
+ return 0;
+ read_lock(&journal->j_state_lock);
+ /* Transaction already committed? */
+ if (tid_geq(journal->j_commit_sequence, tid))
+ goto out;
+ commit_trans = journal->j_committing_transaction;
+ if (!commit_trans || commit_trans->t_tid != tid) {
+ ret = 1;
+ goto out;
+ }
+ /*
+ * Transaction is being committed and we already proceeded to
+ * submitting a flush to fs partition?
+ */
+ if (journal->j_fs_dev != journal->j_dev) {
+ if (!commit_trans->t_need_data_flush ||
+ commit_trans->t_state >= T_COMMIT_DFLUSH)
+ goto out;
+ } else {
+ if (commit_trans->t_state >= T_COMMIT_JFLUSH)
+ goto out;
+ }
+ ret = 1;
+out:
+ read_unlock(&journal->j_state_lock);
+ return ret;
+}
+EXPORT_SYMBOL(jbd2_trans_will_send_data_barrier);
+
+/*
* Wait for a specified commit to complete.
* The caller may not hold the journal lock.
*/
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 05fa77a..3eec82d 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -82,7 +82,7 @@
*/
/*
- * Update transiaction's maximum wait time, if debugging is enabled.
+ * Update transaction's maximum wait time, if debugging is enabled.
*
* In order for t_max_wait to be reliable, it must be protected by a
* lock. But doing so will mean that start_this_handle() can not be
@@ -91,11 +91,10 @@
* means that maximum wait time reported by the jbd2_run_stats
* tracepoint will always be zero.
*/
-static inline void update_t_max_wait(transaction_t *transaction)
+static inline void update_t_max_wait(transaction_t *transaction,
+ unsigned long ts)
{
#ifdef CONFIG_JBD2_DEBUG
- unsigned long ts = jiffies;
-
if (jbd2_journal_enable_debug &&
time_after(transaction->t_start, ts)) {
ts = jbd2_time_diff(ts, transaction->t_start);
@@ -121,6 +120,7 @@
tid_t tid;
int needed, need_to_start;
int nblocks = handle->h_buffer_credits;
+ unsigned long ts = jiffies;
if (nblocks > journal->j_max_transaction_buffers) {
printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n",
@@ -271,7 +271,7 @@
/* OK, account for the buffers that this operation expects to
* use and add the handle to the running transaction.
*/
- update_t_max_wait(transaction);
+ update_t_max_wait(transaction, ts);
handle->h_transaction = transaction;
atomic_inc(&transaction->t_updates);
atomic_inc(&transaction->t_handle_count);
@@ -316,7 +316,8 @@
* This function is visible to journal users (like ext3fs), so is not
* called with the journal already locked.
*
- * Return a pointer to a newly allocated handle, or NULL on failure
+ * Return a pointer to a newly allocated handle, or an ERR_PTR() value
+ * on failure.
*/
handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int gfp_mask)
{
@@ -921,8 +922,8 @@
*/
JBUFFER_TRACE(jh, "cancelling revoke");
jbd2_journal_cancel_revoke(handle, jh);
- jbd2_journal_put_journal_head(jh);
out:
+ jbd2_journal_put_journal_head(jh);
return err;
}
@@ -2147,6 +2148,13 @@
jinode->i_next_transaction == transaction)
goto done;
+ /*
+ * We only ever set this variable to 1 so the test is safe. Since
+ * t_need_data_flush is likely to be set, we do the test to save some
+ * cacheline bouncing
+ */
+ if (!transaction->t_need_data_flush)
+ transaction->t_need_data_flush = 1;
/* On some different transaction's list - should be
* the committing one */
if (jinode->i_transaction) {
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 82faddd..05f7332 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -609,6 +609,8 @@
int ret;
uint32_t now = get_seconds();
+ dentry_unhash(dentry);
+
for (fd = f->dents ; fd; fd = fd->next) {
if (fd->ino)
return -ENOTEMPTY;
@@ -784,6 +786,9 @@
uint8_t type;
uint32_t now;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* The VFS will check for us and prevent trying to rename a
* file over a directory and vice versa, but if it's a directory,
* the VFS can't check whether the victim is empty. The filesystem
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index eaaf2b5..865df16 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -360,6 +360,8 @@
jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name);
+ dentry_unhash(dentry);
+
/* Init inode for quota operations. */
dquot_initialize(dip);
dquot_initialize(ip);
@@ -1095,6 +1097,9 @@
jfs_info("jfs_rename: %s %s", old_dentry->d_name.name,
new_dentry->d_name.name);
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
dquot_initialize(old_dir);
dquot_initialize(new_dir);
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index 9ed89d1..f34c9cd 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -273,6 +273,8 @@
{
struct inode *inode = dentry->d_inode;
+ dentry_unhash(dentry);
+
if (!logfs_empty_dir(inode))
return -ENOTEMPTY;
@@ -622,6 +624,9 @@
loff_t pos;
int err;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* 1. locate source dd */
err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
if (err)
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 6e6777f..f60aed8 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -168,6 +168,8 @@
struct inode * inode = dentry->d_inode;
int err = -ENOTEMPTY;
+ dentry_unhash(dentry);
+
if (minix_empty_dir(inode)) {
err = minix_unlink(dir, dentry);
if (!err) {
@@ -190,6 +192,9 @@
struct minix_dir_entry * old_de;
int err = -ENOENT;
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = minix_find_entry(old_dentry, &old_page);
if (!old_de)
goto out;
diff --git a/fs/mpage.c b/fs/mpage.c
index 0afc809..fdfae9f 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -27,6 +27,7 @@
#include <linux/writeback.h>
#include <linux/backing-dev.h>
#include <linux/pagevec.h>
+#include <linux/cleancache.h>
/*
* I/O completion handler for multipage BIOs.
@@ -271,6 +272,12 @@
SetPageMappedToDisk(page);
}
+ if (fully_mapped && blocks_per_page == 1 && !PageUptodate(page) &&
+ cleancache_get_page(page) == 0) {
+ SetPageUptodate(page);
+ goto confused;
+ }
+
/*
* This page will go to BIO. Do we need to send this BIO off first?
*/
diff --git a/fs/namei.c b/fs/namei.c
index 6ff858c..2358b32 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -391,79 +391,28 @@
}
EXPORT_SYMBOL(path_put);
-/**
- * nameidata_drop_rcu - drop this nameidata out of rcu-walk
- * @nd: nameidata pathwalk data to drop
- * Returns: 0 on success, -ECHILD on failure
- *
+/*
* Path walking has 2 modes, rcu-walk and ref-walk (see
- * Documentation/filesystems/path-lookup.txt). __drop_rcu* functions attempt
- * to drop out of rcu-walk mode and take normal reference counts on dentries
- * and vfsmounts to transition to rcu-walk mode. __drop_rcu* functions take
- * refcounts at the last known good point before rcu-walk got stuck, so
- * ref-walk may continue from there. If this is not successful (eg. a seqcount
- * has changed), then failure is returned and path walk restarts from the
- * beginning in ref-walk mode.
- *
- * nameidata_drop_rcu attempts to drop the current nd->path and nd->root into
- * ref-walk. Must be called from rcu-walk context.
+ * Documentation/filesystems/path-lookup.txt). In situations when we can't
+ * continue in RCU mode, we attempt to drop out of rcu-walk mode and grab
+ * normal reference counts on dentries and vfsmounts to transition to rcu-walk
+ * mode. Refcounts are grabbed at the last known good point before rcu-walk
+ * got stuck, so ref-walk may continue from there. If this is not successful
+ * (eg. a seqcount has changed), then failure is returned and it's up to caller
+ * to restart the path walk from the beginning in ref-walk mode.
*/
-static int nameidata_drop_rcu(struct nameidata *nd)
-{
- struct fs_struct *fs = current->fs;
- struct dentry *dentry = nd->path.dentry;
- int want_root = 0;
-
- BUG_ON(!(nd->flags & LOOKUP_RCU));
- if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
- want_root = 1;
- spin_lock(&fs->lock);
- if (nd->root.mnt != fs->root.mnt ||
- nd->root.dentry != fs->root.dentry)
- goto err_root;
- }
- spin_lock(&dentry->d_lock);
- if (!__d_rcu_to_refcount(dentry, nd->seq))
- goto err;
- BUG_ON(nd->inode != dentry->d_inode);
- spin_unlock(&dentry->d_lock);
- if (want_root) {
- path_get(&nd->root);
- spin_unlock(&fs->lock);
- }
- mntget(nd->path.mnt);
-
- rcu_read_unlock();
- br_read_unlock(vfsmount_lock);
- nd->flags &= ~LOOKUP_RCU;
- return 0;
-err:
- spin_unlock(&dentry->d_lock);
-err_root:
- if (want_root)
- spin_unlock(&fs->lock);
- return -ECHILD;
-}
-
-/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing. */
-static inline int nameidata_drop_rcu_maybe(struct nameidata *nd)
-{
- if (nd->flags & LOOKUP_RCU)
- return nameidata_drop_rcu(nd);
- return 0;
-}
/**
- * nameidata_dentry_drop_rcu - drop nameidata and dentry out of rcu-walk
- * @nd: nameidata pathwalk data to drop
- * @dentry: dentry to drop
+ * unlazy_walk - try to switch to ref-walk mode.
+ * @nd: nameidata pathwalk data
+ * @dentry: child of nd->path.dentry or NULL
* Returns: 0 on success, -ECHILD on failure
*
- * nameidata_dentry_drop_rcu attempts to drop the current nd->path and nd->root,
- * and dentry into ref-walk. @dentry must be a path found by a do_lookup call on
- * @nd. Must be called from rcu-walk context.
+ * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry
+ * for ref-walk mode. @dentry must be a path found by a do_lookup call on
+ * @nd or NULL. Must be called from rcu-walk context.
*/
-static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry)
+static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
{
struct fs_struct *fs = current->fs;
struct dentry *parent = nd->path.dentry;
@@ -478,18 +427,25 @@
goto err_root;
}
spin_lock(&parent->d_lock);
- spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
- if (!__d_rcu_to_refcount(dentry, nd->seq))
- goto err;
- /*
- * If the sequence check on the child dentry passed, then the child has
- * not been removed from its parent. This means the parent dentry must
- * be valid and able to take a reference at this point.
- */
- BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
- BUG_ON(!parent->d_count);
- parent->d_count++;
- spin_unlock(&dentry->d_lock);
+ if (!dentry) {
+ if (!__d_rcu_to_refcount(parent, nd->seq))
+ goto err_parent;
+ BUG_ON(nd->inode != parent->d_inode);
+ } else {
+ spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
+ if (!__d_rcu_to_refcount(dentry, nd->seq))
+ goto err_child;
+ /*
+ * If the sequence check on the child dentry passed, then
+ * the child has not been removed from its parent. This
+ * means the parent dentry must be valid and able to take
+ * a reference at this point.
+ */
+ BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
+ BUG_ON(!parent->d_count);
+ parent->d_count++;
+ spin_unlock(&dentry->d_lock);
+ }
spin_unlock(&parent->d_lock);
if (want_root) {
path_get(&nd->root);
@@ -501,8 +457,10 @@
br_read_unlock(vfsmount_lock);
nd->flags &= ~LOOKUP_RCU;
return 0;
-err:
+
+err_child:
spin_unlock(&dentry->d_lock);
+err_parent:
spin_unlock(&parent->d_lock);
err_root:
if (want_root)
@@ -510,59 +468,6 @@
return -ECHILD;
}
-/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing. */
-static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct dentry *dentry)
-{
- if (nd->flags & LOOKUP_RCU) {
- if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) {
- nd->flags &= ~LOOKUP_RCU;
- if (!(nd->flags & LOOKUP_ROOT))
- nd->root.mnt = NULL;
- rcu_read_unlock();
- br_read_unlock(vfsmount_lock);
- return -ECHILD;
- }
- }
- return 0;
-}
-
-/**
- * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk
- * @nd: nameidata pathwalk data to drop
- * Returns: 0 on success, -ECHILD on failure
- *
- * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk.
- * nd->path should be the final element of the lookup, so nd->root is discarded.
- * Must be called from rcu-walk context.
- */
-static int nameidata_drop_rcu_last(struct nameidata *nd)
-{
- struct dentry *dentry = nd->path.dentry;
-
- BUG_ON(!(nd->flags & LOOKUP_RCU));
- nd->flags &= ~LOOKUP_RCU;
- if (!(nd->flags & LOOKUP_ROOT))
- nd->root.mnt = NULL;
- spin_lock(&dentry->d_lock);
- if (!__d_rcu_to_refcount(dentry, nd->seq))
- goto err_unlock;
- BUG_ON(nd->inode != dentry->d_inode);
- spin_unlock(&dentry->d_lock);
-
- mntget(nd->path.mnt);
-
- rcu_read_unlock();
- br_read_unlock(vfsmount_lock);
-
- return 0;
-
-err_unlock:
- spin_unlock(&dentry->d_lock);
- rcu_read_unlock();
- br_read_unlock(vfsmount_lock);
- return -ECHILD;
-}
-
/**
* release_open_intent - free up open intent resources
* @nd: pointer to nameidata
@@ -606,26 +511,39 @@
return dentry;
}
-/*
- * handle_reval_path - force revalidation of a dentry
+/**
+ * complete_walk - successful completion of path walk
+ * @nd: pointer nameidata
*
- * In some situations the path walking code will trust dentries without
- * revalidating them. This causes problems for filesystems that depend on
- * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set
- * (which indicates that it's possible for the dentry to go stale), force
- * a d_revalidate call before proceeding.
- *
- * Returns 0 if the revalidation was successful. If the revalidation fails,
- * either return the error returned by d_revalidate or -ESTALE if the
- * revalidation it just returned 0. If d_revalidate returns 0, we attempt to
- * invalidate the dentry. It's up to the caller to handle putting references
- * to the path if necessary.
+ * If we had been in RCU mode, drop out of it and legitimize nd->path.
+ * Revalidate the final result, unless we'd already done that during
+ * the path walk or the filesystem doesn't ask for it. Return 0 on
+ * success, -error on failure. In case of failure caller does not
+ * need to drop nd->path.
*/
-static inline int handle_reval_path(struct nameidata *nd)
+static int complete_walk(struct nameidata *nd)
{
struct dentry *dentry = nd->path.dentry;
int status;
+ if (nd->flags & LOOKUP_RCU) {
+ nd->flags &= ~LOOKUP_RCU;
+ if (!(nd->flags & LOOKUP_ROOT))
+ nd->root.mnt = NULL;
+ spin_lock(&dentry->d_lock);
+ if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
+ spin_unlock(&dentry->d_lock);
+ rcu_read_unlock();
+ br_read_unlock(vfsmount_lock);
+ return -ECHILD;
+ }
+ BUG_ON(nd->inode != dentry->d_inode);
+ spin_unlock(&dentry->d_lock);
+ mntget(nd->path.mnt);
+ rcu_read_unlock();
+ br_read_unlock(vfsmount_lock);
+ }
+
if (likely(!(nd->flags & LOOKUP_JUMPED)))
return 0;
@@ -643,6 +561,7 @@
if (!status)
status = -ESTALE;
+ path_put(&nd->path);
return status;
}
@@ -1241,13 +1160,8 @@
if (likely(__follow_mount_rcu(nd, path, inode, false)))
return 0;
unlazy:
- if (dentry) {
- if (nameidata_dentry_drop_rcu(nd, dentry))
- return -ECHILD;
- } else {
- if (nameidata_drop_rcu(nd))
- return -ECHILD;
- }
+ if (unlazy_walk(nd, dentry))
+ return -ECHILD;
} else {
dentry = __d_lookup(parent, name);
}
@@ -1303,7 +1217,7 @@
int err = exec_permission(nd->inode, IPERM_FLAG_RCU);
if (err != -ECHILD)
return err;
- if (nameidata_drop_rcu(nd))
+ if (unlazy_walk(nd, NULL))
return -ECHILD;
}
return exec_permission(nd->inode, 0);
@@ -1357,8 +1271,12 @@
return -ENOENT;
}
if (unlikely(inode->i_op->follow_link) && follow) {
- if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
- return -ECHILD;
+ if (nd->flags & LOOKUP_RCU) {
+ if (unlikely(unlazy_walk(nd, path->dentry))) {
+ terminate_walk(nd);
+ return -ECHILD;
+ }
+ }
BUG_ON(inode != path->dentry->d_inode);
return 1;
}
@@ -1657,18 +1575,8 @@
}
}
- if (nd->flags & LOOKUP_RCU) {
- /* went all way through without dropping RCU */
- BUG_ON(err);
- if (nameidata_drop_rcu_last(nd))
- err = -ECHILD;
- }
-
- if (!err) {
- err = handle_reval_path(nd);
- if (err)
- path_put(&nd->path);
- }
+ if (!err)
+ err = complete_walk(nd);
if (!err && nd->flags & LOOKUP_DIRECTORY) {
if (!nd->inode->i_op->lookup) {
@@ -2134,13 +2042,9 @@
return ERR_PTR(error);
/* fallthrough */
case LAST_ROOT:
- if (nd->flags & LOOKUP_RCU) {
- if (nameidata_drop_rcu_last(nd))
- return ERR_PTR(-ECHILD);
- }
- error = handle_reval_path(nd);
+ error = complete_walk(nd);
if (error)
- goto exit;
+ return ERR_PTR(error);
audit_inode(pathname, nd->path.dentry);
if (open_flag & O_CREAT) {
error = -EISDIR;
@@ -2148,10 +2052,9 @@
}
goto ok;
case LAST_BIND:
- /* can't be RCU mode here */
- error = handle_reval_path(nd);
+ error = complete_walk(nd);
if (error)
- goto exit;
+ return ERR_PTR(error);
audit_inode(pathname, dir);
goto ok;
}
@@ -2170,10 +2073,9 @@
if (error) /* symlink */
return NULL;
/* sayonara */
- if (nd->flags & LOOKUP_RCU) {
- if (nameidata_drop_rcu_last(nd))
- return ERR_PTR(-ECHILD);
- }
+ error = complete_walk(nd);
+ if (error)
+ return ERR_PTR(-ECHILD);
error = -ENOTDIR;
if (nd->flags & LOOKUP_DIRECTORY) {
@@ -2185,11 +2087,9 @@
}
/* create side of things */
-
- if (nd->flags & LOOKUP_RCU) {
- if (nameidata_drop_rcu_last(nd))
- return ERR_PTR(-ECHILD);
- }
+ error = complete_walk(nd);
+ if (error)
+ return ERR_PTR(error);
audit_inode(pathname, dir);
error = -EISDIR;
@@ -2629,10 +2529,10 @@
}
/*
- * We try to drop the dentry early: we should have
- * a usage count of 2 if we're the only user of this
- * dentry, and if that is true (possibly after pruning
- * the dcache), then we drop the dentry now.
+ * The dentry_unhash() helper will try to drop the dentry early: we
+ * should have a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning the dcache),
+ * then we drop the dentry now.
*
* A low-level filesystem can, if it choses, legally
* do a
@@ -2645,10 +2545,9 @@
*/
void dentry_unhash(struct dentry *dentry)
{
- dget(dentry);
shrink_dcache_parent(dentry);
spin_lock(&dentry->d_lock);
- if (dentry->d_count == 2)
+ if (dentry->d_count == 1)
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
}
@@ -2664,25 +2563,26 @@
return -EPERM;
mutex_lock(&dentry->d_inode->i_mutex);
- dentry_unhash(dentry);
- if (d_mountpoint(dentry))
- error = -EBUSY;
- else {
- error = security_inode_rmdir(dir, dentry);
- if (!error) {
- error = dir->i_op->rmdir(dir, dentry);
- if (!error) {
- dentry->d_inode->i_flags |= S_DEAD;
- dont_mount(dentry);
- }
- }
- }
- mutex_unlock(&dentry->d_inode->i_mutex);
- if (!error) {
- d_delete(dentry);
- }
- dput(dentry);
+ error = -EBUSY;
+ if (d_mountpoint(dentry))
+ goto out;
+
+ error = security_inode_rmdir(dir, dentry);
+ if (error)
+ goto out;
+
+ error = dir->i_op->rmdir(dir, dentry);
+ if (error)
+ goto out;
+
+ dentry->d_inode->i_flags |= S_DEAD;
+ dont_mount(dentry);
+
+out:
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ if (!error)
+ d_delete(dentry);
return error;
}
@@ -3053,12 +2953,7 @@
* HOWEVER, it relies on the assumption that any object with ->lookup()
* has no more than 1 dentry. If "hybrid" objects will ever appear,
* we'd better make sure that there's no link(2) for them.
- * d) some filesystems don't support opened-but-unlinked directories,
- * either because of layout or because they are not ready to deal with
- * all cases correctly. The latter will be fixed (taking this sort of
- * stuff into VFS), but the former is not going away. Solution: the same
- * trick as in rmdir().
- * e) conversion from fhandle to dentry may come in the wrong moment - when
+ * d) conversion from fhandle to dentry may come in the wrong moment - when
* we are removing the target. Solution: we will have to grab ->i_mutex
* in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
* ->i_mutex on parents, which works but leads to some truly excessive
@@ -3068,7 +2963,7 @@
struct inode *new_dir, struct dentry *new_dentry)
{
int error = 0;
- struct inode *target;
+ struct inode *target = new_dentry->d_inode;
/*
* If we are going to change the parent - check write permissions,
@@ -3084,26 +2979,24 @@
if (error)
return error;
- target = new_dentry->d_inode;
if (target)
mutex_lock(&target->i_mutex);
- if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
- error = -EBUSY;
- else {
- if (target)
- dentry_unhash(new_dentry);
- error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
- }
+
+ error = -EBUSY;
+ if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
+ goto out;
+
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ if (error)
+ goto out;
+
if (target) {
- if (!error) {
- target->i_flags |= S_DEAD;
- dont_mount(new_dentry);
- }
- mutex_unlock(&target->i_mutex);
- if (d_unhashed(new_dentry))
- d_rehash(new_dentry);
- dput(new_dentry);
+ target->i_flags |= S_DEAD;
+ dont_mount(new_dentry);
}
+out:
+ if (target)
+ mutex_unlock(&target->i_mutex);
if (!error)
if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
d_move(old_dentry,new_dentry);
@@ -3113,7 +3006,7 @@
static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
- struct inode *target;
+ struct inode *target = new_dentry->d_inode;
int error;
error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
@@ -3121,19 +3014,22 @@
return error;
dget(new_dentry);
- target = new_dentry->d_inode;
if (target)
mutex_lock(&target->i_mutex);
+
+ error = -EBUSY;
if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
- error = -EBUSY;
- else
- error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
- if (!error) {
- if (target)
- dont_mount(new_dentry);
- if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
- d_move(old_dentry, new_dentry);
- }
+ goto out;
+
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ if (error)
+ goto out;
+
+ if (target)
+ dont_mount(new_dentry);
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
+ d_move(old_dentry, new_dentry);
+out:
if (target)
mutex_unlock(&target->i_mutex);
dput(new_dentry);
diff --git a/fs/namespace.c b/fs/namespace.c
index d99bcf5..fe59bd1 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1695,7 +1695,7 @@
static int flags_to_propagation_type(int flags)
{
- int type = flags & ~MS_REC;
+ int type = flags & ~(MS_REC | MS_SILENT);
/* Fail if any non-propagation flags are set */
if (type & ~(MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index f6946bb..e3e646b 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -1033,6 +1033,8 @@
DPRINTK("ncp_rmdir: removing %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
+ dentry_unhash(dentry);
+
error = -EBUSY;
if (!d_unhashed(dentry))
goto out;
@@ -1139,6 +1141,9 @@
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
ncp_age_dentry(server, old_dentry);
ncp_age_dentry(server, new_dentry);
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
index a7c07b4..e5d71b2 100644
--- a/fs/ncpfs/mmap.c
+++ b/fs/ncpfs/mmap.c
@@ -16,6 +16,7 @@
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/fcntl.h>
+#include <linux/memcontrol.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -92,6 +93,7 @@
* -- wli
*/
count_vm_event(PGMAJFAULT);
+ mem_cgroup_count_vm_event(area->vm_mm, PGMAJFAULT);
return VM_FAULT_MAJOR;
}
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index 546849b..1102a5f 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -334,6 +334,8 @@
struct nilfs_transaction_info ti;
int err;
+ dentry_unhash(dentry);
+
err = nilfs_transaction_begin(dir->i_sb, &ti, 0);
if (err)
return err;
@@ -369,6 +371,9 @@
struct nilfs_transaction_info ti;
int err;
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
err = nilfs_transaction_begin(old_dir->i_sb, &ti, 1);
if (unlikely(err))
return err;
diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile
index d8a0313..f17e58b 100644
--- a/fs/ocfs2/Makefile
+++ b/fs/ocfs2/Makefile
@@ -30,6 +30,7 @@
namei.o \
refcounttree.o \
reservations.o \
+ move_extents.o \
resize.o \
slot_map.o \
suballoc.o \
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 48aa9c7..ed553c6 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -29,6 +29,7 @@
#include <linux/highmem.h>
#include <linux/swap.h>
#include <linux/quotaops.h>
+#include <linux/blkdev.h>
#include <cluster/masklog.h>
@@ -7184,3 +7185,168 @@
out:
return ret;
}
+
+static int ocfs2_trim_extent(struct super_block *sb,
+ struct ocfs2_group_desc *gd,
+ u32 start, u32 count)
+{
+ u64 discard, bcount;
+
+ bcount = ocfs2_clusters_to_blocks(sb, count);
+ discard = le64_to_cpu(gd->bg_blkno) +
+ ocfs2_clusters_to_blocks(sb, start);
+
+ trace_ocfs2_trim_extent(sb, (unsigned long long)discard, bcount);
+
+ return sb_issue_discard(sb, discard, bcount, GFP_NOFS, 0);
+}
+
+static int ocfs2_trim_group(struct super_block *sb,
+ struct ocfs2_group_desc *gd,
+ u32 start, u32 max, u32 minbits)
+{
+ int ret = 0, count = 0, next;
+ void *bitmap = gd->bg_bitmap;
+
+ if (le16_to_cpu(gd->bg_free_bits_count) < minbits)
+ return 0;
+
+ trace_ocfs2_trim_group((unsigned long long)le64_to_cpu(gd->bg_blkno),
+ start, max, minbits);
+
+ while (start < max) {
+ start = ocfs2_find_next_zero_bit(bitmap, max, start);
+ if (start >= max)
+ break;
+ next = ocfs2_find_next_bit(bitmap, max, start);
+
+ if ((next - start) >= minbits) {
+ ret = ocfs2_trim_extent(sb, gd,
+ start, next - start);
+ if (ret < 0) {
+ mlog_errno(ret);
+ break;
+ }
+ count += next - start;
+ }
+ start = next + 1;
+
+ if (fatal_signal_pending(current)) {
+ count = -ERESTARTSYS;
+ break;
+ }
+
+ if ((le16_to_cpu(gd->bg_free_bits_count) - count) < minbits)
+ break;
+ }
+
+ if (ret < 0)
+ count = ret;
+
+ return count;
+}
+
+int ocfs2_trim_fs(struct super_block *sb, struct fstrim_range *range)
+{
+ struct ocfs2_super *osb = OCFS2_SB(sb);
+ u64 start, len, trimmed, first_group, last_group, group;
+ int ret, cnt;
+ u32 first_bit, last_bit, minlen;
+ struct buffer_head *main_bm_bh = NULL;
+ struct inode *main_bm_inode = NULL;
+ struct buffer_head *gd_bh = NULL;
+ struct ocfs2_dinode *main_bm;
+ struct ocfs2_group_desc *gd = NULL;
+
+ start = range->start >> osb->s_clustersize_bits;
+ len = range->len >> osb->s_clustersize_bits;
+ minlen = range->minlen >> osb->s_clustersize_bits;
+ trimmed = 0;
+
+ if (!len) {
+ range->len = 0;
+ return 0;
+ }
+
+ if (minlen >= osb->bitmap_cpg)
+ return -EINVAL;
+
+ main_bm_inode = ocfs2_get_system_file_inode(osb,
+ GLOBAL_BITMAP_SYSTEM_INODE,
+ OCFS2_INVALID_SLOT);
+ if (!main_bm_inode) {
+ ret = -EIO;
+ mlog_errno(ret);
+ goto out;
+ }
+
+ mutex_lock(&main_bm_inode->i_mutex);
+
+ ret = ocfs2_inode_lock(main_bm_inode, &main_bm_bh, 0);
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out_mutex;
+ }
+ main_bm = (struct ocfs2_dinode *)main_bm_bh->b_data;
+
+ if (start >= le32_to_cpu(main_bm->i_clusters)) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (start + len > le32_to_cpu(main_bm->i_clusters))
+ len = le32_to_cpu(main_bm->i_clusters) - start;
+
+ trace_ocfs2_trim_fs(start, len, minlen);
+
+ /* Determine first and last group to examine based on start and len */
+ first_group = ocfs2_which_cluster_group(main_bm_inode, start);
+ if (first_group == osb->first_cluster_group_blkno)
+ first_bit = start;
+ else
+ first_bit = start - ocfs2_blocks_to_clusters(sb, first_group);
+ last_group = ocfs2_which_cluster_group(main_bm_inode, start + len - 1);
+ last_bit = osb->bitmap_cpg;
+
+ for (group = first_group; group <= last_group;) {
+ if (first_bit + len >= osb->bitmap_cpg)
+ last_bit = osb->bitmap_cpg;
+ else
+ last_bit = first_bit + len;
+
+ ret = ocfs2_read_group_descriptor(main_bm_inode,
+ main_bm, group,
+ &gd_bh);
+ if (ret < 0) {
+ mlog_errno(ret);
+ break;
+ }
+
+ gd = (struct ocfs2_group_desc *)gd_bh->b_data;
+ cnt = ocfs2_trim_group(sb, gd, first_bit, last_bit, minlen);
+ brelse(gd_bh);
+ gd_bh = NULL;
+ if (cnt < 0) {
+ ret = cnt;
+ mlog_errno(ret);
+ break;
+ }
+
+ trimmed += cnt;
+ len -= osb->bitmap_cpg - first_bit;
+ first_bit = 0;
+ if (group == osb->first_cluster_group_blkno)
+ group = ocfs2_clusters_to_blocks(sb, osb->bitmap_cpg);
+ else
+ group += ocfs2_clusters_to_blocks(sb, osb->bitmap_cpg);
+ }
+ range->len = trimmed * sb->s_blocksize;
+out_unlock:
+ ocfs2_inode_unlock(main_bm_inode, 0);
+ brelse(main_bm_bh);
+out_mutex:
+ mutex_unlock(&main_bm_inode->i_mutex);
+ iput(main_bm_inode);
+out:
+ return ret;
+}
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index 3bd08a0..ca381c5 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -239,6 +239,7 @@
struct buffer_head **leaf_bh);
int ocfs2_search_extent_list(struct ocfs2_extent_list *el, u32 v_cluster);
+int ocfs2_trim_fs(struct super_block *sb, struct fstrim_range *range);
/*
* Helper function to look at the # of clusters in an extent record.
*/
diff --git a/fs/ocfs2/cluster/sys.c b/fs/ocfs2/cluster/sys.c
index bc702da..a4b0773 100644
--- a/fs/ocfs2/cluster/sys.c
+++ b/fs/ocfs2/cluster/sys.c
@@ -57,7 +57,6 @@
void o2cb_sys_shutdown(void)
{
mlog_sys_shutdown();
- sysfs_remove_link(NULL, "o2cb");
kset_unregister(o2cb_kset);
}
@@ -69,14 +68,6 @@
if (!o2cb_kset)
return -ENOMEM;
- /*
- * Create this symlink for backwards compatibility with old
- * versions of ocfs2-tools which look for things in /sys/o2cb.
- */
- ret = sysfs_create_link(NULL, &o2cb_kset->kobj, "o2cb");
- if (ret)
- goto error;
-
ret = sysfs_create_group(&o2cb_kset->kobj, &o2cb_attr_group);
if (ret)
goto error;
diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h
index 4bdf7ba..d602abb 100644
--- a/fs/ocfs2/dlm/dlmcommon.h
+++ b/fs/ocfs2/dlm/dlmcommon.h
@@ -144,6 +144,7 @@
wait_queue_head_t dlm_join_events;
unsigned long live_nodes_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
unsigned long domain_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
+ unsigned long exit_domain_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
unsigned long recovery_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
struct dlm_recovery_ctxt reco;
spinlock_t master_lock;
@@ -401,6 +402,18 @@
return 1;
}
+static inline char *dlm_list_in_text(enum dlm_lockres_list idx)
+{
+ if (idx == DLM_GRANTED_LIST)
+ return "granted";
+ else if (idx == DLM_CONVERTING_LIST)
+ return "converting";
+ else if (idx == DLM_BLOCKED_LIST)
+ return "blocked";
+ else
+ return "unknown";
+}
+
static inline struct list_head *
dlm_list_idx_to_ptr(struct dlm_lock_resource *res, enum dlm_lockres_list idx)
{
@@ -448,6 +461,7 @@
DLM_FINALIZE_RECO_MSG = 518,
DLM_QUERY_REGION = 519,
DLM_QUERY_NODEINFO = 520,
+ DLM_BEGIN_EXIT_DOMAIN_MSG = 521,
};
struct dlm_reco_node_data
diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c
index 04a32be..56f82cb 100644
--- a/fs/ocfs2/dlm/dlmdebug.c
+++ b/fs/ocfs2/dlm/dlmdebug.c
@@ -756,6 +756,12 @@
buf + out, len - out);
out += snprintf(buf + out, len - out, "\n");
+ /* Exit Domain Map: xx xx xx */
+ out += snprintf(buf + out, len - out, "Exit Domain Map: ");
+ out += stringify_nodemap(dlm->exit_domain_map, O2NM_MAX_NODES,
+ buf + out, len - out);
+ out += snprintf(buf + out, len - out, "\n");
+
/* Live Map: xx xx xx */
out += snprintf(buf + out, len - out, "Live Map: ");
out += stringify_nodemap(dlm->live_nodes_map, O2NM_MAX_NODES,
diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c
index 3b179d6..6ed6b95 100644
--- a/fs/ocfs2/dlm/dlmdomain.c
+++ b/fs/ocfs2/dlm/dlmdomain.c
@@ -132,10 +132,12 @@
* New in version 1.1:
* - Message DLM_QUERY_REGION added to support global heartbeat
* - Message DLM_QUERY_NODEINFO added to allow online node removes
+ * New in version 1.2:
+ * - Message DLM_BEGIN_EXIT_DOMAIN_MSG added to mark start of exit domain
*/
static const struct dlm_protocol_version dlm_protocol = {
.pv_major = 1,
- .pv_minor = 1,
+ .pv_minor = 2,
};
#define DLM_DOMAIN_BACKOFF_MS 200
@@ -449,14 +451,18 @@
dropped = dlm_empty_lockres(dlm, res);
spin_lock(&res->spinlock);
- __dlm_lockres_calc_usage(dlm, res);
- iter = res->hash_node.next;
+ if (dropped)
+ __dlm_lockres_calc_usage(dlm, res);
+ else
+ iter = res->hash_node.next;
spin_unlock(&res->spinlock);
dlm_lockres_put(res);
- if (dropped)
+ if (dropped) {
+ cond_resched_lock(&dlm->spinlock);
goto redo_bucket;
+ }
}
cond_resched_lock(&dlm->spinlock);
num += n;
@@ -486,6 +492,28 @@
return ret;
}
+static int dlm_begin_exit_domain_handler(struct o2net_msg *msg, u32 len,
+ void *data, void **ret_data)
+{
+ struct dlm_ctxt *dlm = data;
+ unsigned int node;
+ struct dlm_exit_domain *exit_msg = (struct dlm_exit_domain *) msg->buf;
+
+ if (!dlm_grab(dlm))
+ return 0;
+
+ node = exit_msg->node_idx;
+ mlog(0, "%s: Node %u sent a begin exit domain message\n", dlm->name, node);
+
+ spin_lock(&dlm->spinlock);
+ set_bit(node, dlm->exit_domain_map);
+ spin_unlock(&dlm->spinlock);
+
+ dlm_put(dlm);
+
+ return 0;
+}
+
static void dlm_mark_domain_leaving(struct dlm_ctxt *dlm)
{
/* Yikes, a double spinlock! I need domain_lock for the dlm
@@ -542,6 +570,7 @@
spin_lock(&dlm->spinlock);
clear_bit(node, dlm->domain_map);
+ clear_bit(node, dlm->exit_domain_map);
__dlm_print_nodes(dlm);
/* notify anything attached to the heartbeat events */
@@ -554,29 +583,56 @@
return 0;
}
-static int dlm_send_one_domain_exit(struct dlm_ctxt *dlm,
+static int dlm_send_one_domain_exit(struct dlm_ctxt *dlm, u32 msg_type,
unsigned int node)
{
int status;
struct dlm_exit_domain leave_msg;
- mlog(0, "Asking node %u if we can leave the domain %s me = %u\n",
- node, dlm->name, dlm->node_num);
+ mlog(0, "%s: Sending domain exit message %u to node %u\n", dlm->name,
+ msg_type, node);
memset(&leave_msg, 0, sizeof(leave_msg));
leave_msg.node_idx = dlm->node_num;
- status = o2net_send_message(DLM_EXIT_DOMAIN_MSG, dlm->key,
- &leave_msg, sizeof(leave_msg), node,
- NULL);
+ status = o2net_send_message(msg_type, dlm->key, &leave_msg,
+ sizeof(leave_msg), node, NULL);
if (status < 0)
- mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to "
- "node %u\n", status, DLM_EXIT_DOMAIN_MSG, dlm->key, node);
- mlog(0, "status return %d from o2net_send_message\n", status);
+ mlog(ML_ERROR, "Error %d sending domain exit message %u "
+ "to node %u on domain %s\n", status, msg_type, node,
+ dlm->name);
return status;
}
+static void dlm_begin_exit_domain(struct dlm_ctxt *dlm)
+{
+ int node = -1;
+
+ /* Support for begin exit domain was added in 1.2 */
+ if (dlm->dlm_locking_proto.pv_major == 1 &&
+ dlm->dlm_locking_proto.pv_minor < 2)
+ return;
+
+ /*
+ * Unlike DLM_EXIT_DOMAIN_MSG, DLM_BEGIN_EXIT_DOMAIN_MSG is purely
+ * informational. Meaning if a node does not receive the message,
+ * so be it.
+ */
+ spin_lock(&dlm->spinlock);
+ while (1) {
+ node = find_next_bit(dlm->domain_map, O2NM_MAX_NODES, node + 1);
+ if (node >= O2NM_MAX_NODES)
+ break;
+ if (node == dlm->node_num)
+ continue;
+
+ spin_unlock(&dlm->spinlock);
+ dlm_send_one_domain_exit(dlm, DLM_BEGIN_EXIT_DOMAIN_MSG, node);
+ spin_lock(&dlm->spinlock);
+ }
+ spin_unlock(&dlm->spinlock);
+}
static void dlm_leave_domain(struct dlm_ctxt *dlm)
{
@@ -602,7 +658,8 @@
clear_node = 1;
- status = dlm_send_one_domain_exit(dlm, node);
+ status = dlm_send_one_domain_exit(dlm, DLM_EXIT_DOMAIN_MSG,
+ node);
if (status < 0 &&
status != -ENOPROTOOPT &&
status != -ENOTCONN) {
@@ -677,6 +734,7 @@
if (leave) {
mlog(0, "shutting down domain %s\n", dlm->name);
+ dlm_begin_exit_domain(dlm);
/* We changed dlm state, notify the thread */
dlm_kick_thread(dlm, NULL);
@@ -909,6 +967,7 @@
* leftover join state. */
BUG_ON(dlm->joining_node != assert->node_idx);
set_bit(assert->node_idx, dlm->domain_map);
+ clear_bit(assert->node_idx, dlm->exit_domain_map);
__dlm_set_joining_node(dlm, DLM_LOCK_RES_OWNER_UNKNOWN);
printk(KERN_NOTICE "o2dlm: Node %u joins domain %s\n",
@@ -1793,6 +1852,13 @@
if (status)
goto bail;
+ status = o2net_register_handler(DLM_BEGIN_EXIT_DOMAIN_MSG, dlm->key,
+ sizeof(struct dlm_exit_domain),
+ dlm_begin_exit_domain_handler,
+ dlm, NULL, &dlm->dlm_domain_handlers);
+ if (status)
+ goto bail;
+
bail:
if (status)
dlm_unregister_domain_handlers(dlm);
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index 84d1663..11eefb8 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -2339,65 +2339,55 @@
dlm_lockres_put(res);
}
-/* Checks whether the lockres can be migrated. Returns 0 if yes, < 0
- * if not. If 0, numlocks is set to the number of locks in the lockres.
+/*
+ * A migrateable resource is one that is :
+ * 1. locally mastered, and,
+ * 2. zero local locks, and,
+ * 3. one or more non-local locks, or, one or more references
+ * Returns 1 if yes, 0 if not.
*/
static int dlm_is_lockres_migrateable(struct dlm_ctxt *dlm,
- struct dlm_lock_resource *res,
- int *numlocks,
- int *hasrefs)
+ struct dlm_lock_resource *res)
{
- int ret;
- int i;
- int count = 0;
+ enum dlm_lockres_list idx;
+ int nonlocal = 0, node_ref;
struct list_head *queue;
struct dlm_lock *lock;
+ u64 cookie;
assert_spin_locked(&res->spinlock);
- *numlocks = 0;
- *hasrefs = 0;
+ if (res->owner != dlm->node_num)
+ return 0;
- ret = -EINVAL;
- if (res->owner == DLM_LOCK_RES_OWNER_UNKNOWN) {
- mlog(0, "cannot migrate lockres with unknown owner!\n");
- goto leave;
- }
-
- if (res->owner != dlm->node_num) {
- mlog(0, "cannot migrate lockres this node doesn't own!\n");
- goto leave;
- }
-
- ret = 0;
- queue = &res->granted;
- for (i = 0; i < 3; i++) {
+ for (idx = DLM_GRANTED_LIST; idx <= DLM_BLOCKED_LIST; idx++) {
+ queue = dlm_list_idx_to_ptr(res, idx);
list_for_each_entry(lock, queue, list) {
- ++count;
- if (lock->ml.node == dlm->node_num) {
- mlog(0, "found a lock owned by this node still "
- "on the %s queue! will not migrate this "
- "lockres\n", (i == 0 ? "granted" :
- (i == 1 ? "converting" :
- "blocked")));
- ret = -ENOTEMPTY;
- goto leave;
+ if (lock->ml.node != dlm->node_num) {
+ nonlocal++;
+ continue;
}
+ cookie = be64_to_cpu(lock->ml.cookie);
+ mlog(0, "%s: Not migrateable res %.*s, lock %u:%llu on "
+ "%s list\n", dlm->name, res->lockname.len,
+ res->lockname.name,
+ dlm_get_lock_cookie_node(cookie),
+ dlm_get_lock_cookie_seq(cookie),
+ dlm_list_in_text(idx));
+ return 0;
}
- queue++;
}
- *numlocks = count;
+ if (!nonlocal) {
+ node_ref = find_next_bit(res->refmap, O2NM_MAX_NODES, 0);
+ if (node_ref >= O2NM_MAX_NODES)
+ return 0;
+ }
- count = find_next_bit(res->refmap, O2NM_MAX_NODES, 0);
- if (count < O2NM_MAX_NODES)
- *hasrefs = 1;
+ mlog(0, "%s: res %.*s, Migrateable\n", dlm->name, res->lockname.len,
+ res->lockname.name);
- mlog(0, "%s: res %.*s, Migrateable, locks %d, refs %d\n", dlm->name,
- res->lockname.len, res->lockname.name, *numlocks, *hasrefs);
-
-leave:
- return ret;
+ return 1;
}
/*
@@ -2406,8 +2396,7 @@
static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
- struct dlm_lock_resource *res,
- u8 target)
+ struct dlm_lock_resource *res, u8 target)
{
struct dlm_master_list_entry *mle = NULL;
struct dlm_master_list_entry *oldmle = NULL;
@@ -2416,37 +2405,20 @@
const char *name;
unsigned int namelen;
int mle_added = 0;
- int numlocks, hasrefs;
int wake = 0;
if (!dlm_grab(dlm))
return -EINVAL;
+ BUG_ON(target == O2NM_MAX_NODES);
+
name = res->lockname.name;
namelen = res->lockname.len;
- mlog(0, "%s: Migrating %.*s to %u\n", dlm->name, namelen, name, target);
+ mlog(0, "%s: Migrating %.*s to node %u\n", dlm->name, namelen, name,
+ target);
- /*
- * ensure this lockres is a proper candidate for migration
- */
- spin_lock(&res->spinlock);
- ret = dlm_is_lockres_migrateable(dlm, res, &numlocks, &hasrefs);
- if (ret < 0) {
- spin_unlock(&res->spinlock);
- goto leave;
- }
- spin_unlock(&res->spinlock);
-
- /* no work to do */
- if (numlocks == 0 && !hasrefs)
- goto leave;
-
- /*
- * preallocate up front
- * if this fails, abort
- */
-
+ /* preallocate up front. if this fails, abort */
ret = -ENOMEM;
mres = (struct dlm_migratable_lockres *) __get_free_page(GFP_NOFS);
if (!mres) {
@@ -2462,35 +2434,10 @@
ret = 0;
/*
- * find a node to migrate the lockres to
- */
-
- spin_lock(&dlm->spinlock);
- /* pick a new node */
- if (!test_bit(target, dlm->domain_map) ||
- target >= O2NM_MAX_NODES) {
- target = dlm_pick_migration_target(dlm, res);
- }
- mlog(0, "%s: res %.*s, Node %u chosen for migration\n", dlm->name,
- namelen, name, target);
-
- if (target >= O2NM_MAX_NODES ||
- !test_bit(target, dlm->domain_map)) {
- /* target chosen is not alive */
- ret = -EINVAL;
- }
-
- if (ret) {
- spin_unlock(&dlm->spinlock);
- goto fail;
- }
-
- mlog(0, "continuing with target = %u\n", target);
-
- /*
* clear any existing master requests and
* add the migration mle to the list
*/
+ spin_lock(&dlm->spinlock);
spin_lock(&dlm->master_lock);
ret = dlm_add_migration_mle(dlm, res, mle, &oldmle, name,
namelen, target, dlm->node_num);
@@ -2531,6 +2478,7 @@
dlm_put_mle(mle);
} else if (mle) {
kmem_cache_free(dlm_mle_cache, mle);
+ mle = NULL;
}
goto leave;
}
@@ -2652,69 +2600,52 @@
if (wake)
wake_up(&res->wq);
- /* TODO: cleanup */
if (mres)
free_page((unsigned long)mres);
dlm_put(dlm);
- mlog(0, "returning %d\n", ret);
+ mlog(0, "%s: Migrating %.*s to %u, returns %d\n", dlm->name, namelen,
+ name, target, ret);
return ret;
}
#define DLM_MIGRATION_RETRY_MS 100
-/* Should be called only after beginning the domain leave process.
+/*
+ * Should be called only after beginning the domain leave process.
* There should not be any remaining locks on nonlocal lock resources,
* and there should be no local locks left on locally mastered resources.
*
* Called with the dlm spinlock held, may drop it to do migration, but
* will re-acquire before exit.
*
- * Returns: 1 if dlm->spinlock was dropped/retaken, 0 if never dropped */
+ * Returns: 1 if dlm->spinlock was dropped/retaken, 0 if never dropped
+ */
int dlm_empty_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res)
{
int ret;
int lock_dropped = 0;
- int numlocks, hasrefs;
+ u8 target = O2NM_MAX_NODES;
+
+ assert_spin_locked(&dlm->spinlock);
spin_lock(&res->spinlock);
- if (res->owner != dlm->node_num) {
- if (!__dlm_lockres_unused(res)) {
- mlog(ML_ERROR, "%s:%.*s: this node is not master, "
- "trying to free this but locks remain\n",
- dlm->name, res->lockname.len, res->lockname.name);
- }
- spin_unlock(&res->spinlock);
- goto leave;
- }
-
- /* No need to migrate a lockres having no locks */
- ret = dlm_is_lockres_migrateable(dlm, res, &numlocks, &hasrefs);
- if (ret >= 0 && numlocks == 0 && !hasrefs) {
- spin_unlock(&res->spinlock);
- goto leave;
- }
+ if (dlm_is_lockres_migrateable(dlm, res))
+ target = dlm_pick_migration_target(dlm, res);
spin_unlock(&res->spinlock);
+ if (target == O2NM_MAX_NODES)
+ goto leave;
+
/* Wheee! Migrate lockres here! Will sleep so drop spinlock. */
spin_unlock(&dlm->spinlock);
lock_dropped = 1;
- while (1) {
- ret = dlm_migrate_lockres(dlm, res, O2NM_MAX_NODES);
- if (ret >= 0)
- break;
- if (ret == -ENOTEMPTY) {
- mlog(ML_ERROR, "lockres %.*s still has local locks!\n",
- res->lockname.len, res->lockname.name);
- BUG();
- }
-
- mlog(0, "lockres %.*s: migrate failed, "
- "retrying\n", res->lockname.len,
- res->lockname.name);
- msleep(DLM_MIGRATION_RETRY_MS);
- }
+ ret = dlm_migrate_lockres(dlm, res, target);
+ if (ret)
+ mlog(0, "%s: res %.*s, Migrate to node %u failed with %d\n",
+ dlm->name, res->lockname.len, res->lockname.name,
+ target, ret);
spin_lock(&dlm->spinlock);
leave:
return lock_dropped;
@@ -2898,61 +2829,55 @@
}
}
-/* for now this is not too intelligent. we will
- * need stats to make this do the right thing.
- * this just finds the first lock on one of the
- * queues and uses that node as the target. */
+/*
+ * Pick a node to migrate the lock resource to. This function selects a
+ * potential target based first on the locks and then on refmap. It skips
+ * nodes that are in the process of exiting the domain.
+ */
static u8 dlm_pick_migration_target(struct dlm_ctxt *dlm,
struct dlm_lock_resource *res)
{
- int i;
+ enum dlm_lockres_list idx;
struct list_head *queue = &res->granted;
struct dlm_lock *lock;
- int nodenum;
+ int noderef;
+ u8 nodenum = O2NM_MAX_NODES;
assert_spin_locked(&dlm->spinlock);
+ assert_spin_locked(&res->spinlock);
- spin_lock(&res->spinlock);
- for (i=0; i<3; i++) {
+ /* Go through all the locks */
+ for (idx = DLM_GRANTED_LIST; idx <= DLM_BLOCKED_LIST; idx++) {
+ queue = dlm_list_idx_to_ptr(res, idx);
list_for_each_entry(lock, queue, list) {
- /* up to the caller to make sure this node
- * is alive */
- if (lock->ml.node != dlm->node_num) {
- spin_unlock(&res->spinlock);
- return lock->ml.node;
- }
+ if (lock->ml.node == dlm->node_num)
+ continue;
+ if (test_bit(lock->ml.node, dlm->exit_domain_map))
+ continue;
+ nodenum = lock->ml.node;
+ goto bail;
}
- queue++;
}
- nodenum = find_next_bit(res->refmap, O2NM_MAX_NODES, 0);
- if (nodenum < O2NM_MAX_NODES) {
- spin_unlock(&res->spinlock);
- return nodenum;
- }
- spin_unlock(&res->spinlock);
- mlog(0, "have not found a suitable target yet! checking domain map\n");
-
- /* ok now we're getting desperate. pick anyone alive. */
- nodenum = -1;
+ /* Go thru the refmap */
+ noderef = -1;
while (1) {
- nodenum = find_next_bit(dlm->domain_map,
- O2NM_MAX_NODES, nodenum+1);
- mlog(0, "found %d in domain map\n", nodenum);
- if (nodenum >= O2NM_MAX_NODES)
+ noderef = find_next_bit(res->refmap, O2NM_MAX_NODES,
+ noderef + 1);
+ if (noderef >= O2NM_MAX_NODES)
break;
- if (nodenum != dlm->node_num) {
- mlog(0, "picking %d\n", nodenum);
- return nodenum;
- }
+ if (noderef == dlm->node_num)
+ continue;
+ if (test_bit(noderef, dlm->exit_domain_map))
+ continue;
+ nodenum = noderef;
+ goto bail;
}
- mlog(0, "giving up. no master to migrate to\n");
- return DLM_LOCK_RES_OWNER_UNKNOWN;
+bail:
+ return nodenum;
}
-
-
/* this is called by the new master once all lockres
* data has been received */
static int dlm_do_migrate_request(struct dlm_ctxt *dlm,
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index f1beb6f..7efab6d 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -2393,6 +2393,7 @@
mlog(0, "node %u being removed from domain map!\n", idx);
clear_bit(idx, dlm->domain_map);
+ clear_bit(idx, dlm->exit_domain_map);
/* wake up migration waiters if a node goes down.
* perhaps later we can genericize this for other waiters. */
wake_up(&dlm->migration_wq);
diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index 8c5c0ed..b420767 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -88,7 +88,7 @@
* signifies a bast fired on the lock.
*/
#define DLMFS_CAPABILITIES "bast stackglue"
-extern int param_set_dlmfs_capabilities(const char *val,
+static int param_set_dlmfs_capabilities(const char *val,
struct kernel_param *kp)
{
printk(KERN_ERR "%s: readonly parameter\n", kp->name);
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 89659d6..b1e35a3 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -2670,6 +2670,7 @@
.flock = ocfs2_flock,
.splice_read = ocfs2_file_splice_read,
.splice_write = ocfs2_file_splice_write,
+ .fallocate = ocfs2_fallocate,
};
const struct file_operations ocfs2_dops_no_plocks = {
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 8f13c59..bc91072 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -22,6 +22,11 @@
#include "ioctl.h"
#include "resize.h"
#include "refcounttree.h"
+#include "sysfile.h"
+#include "dir.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
+#include "move_extents.h"
#include <linux/ext2_fs.h>
@@ -35,31 +40,27 @@
* be -EFAULT. The error will be returned from the ioctl(2) call. It's
* just a best-effort to tell userspace that this request caused the error.
*/
-static inline void __o2info_set_request_error(struct ocfs2_info_request *kreq,
+static inline void o2info_set_request_error(struct ocfs2_info_request *kreq,
struct ocfs2_info_request __user *req)
{
kreq->ir_flags |= OCFS2_INFO_FL_ERROR;
(void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags));
}
-#define o2info_set_request_error(a, b) \
- __o2info_set_request_error((struct ocfs2_info_request *)&(a), b)
-
-static inline void __o2info_set_request_filled(struct ocfs2_info_request *req)
+static inline void o2info_set_request_filled(struct ocfs2_info_request *req)
{
req->ir_flags |= OCFS2_INFO_FL_FILLED;
}
-#define o2info_set_request_filled(a) \
- __o2info_set_request_filled((struct ocfs2_info_request *)&(a))
-
-static inline void __o2info_clear_request_filled(struct ocfs2_info_request *req)
+static inline void o2info_clear_request_filled(struct ocfs2_info_request *req)
{
req->ir_flags &= ~OCFS2_INFO_FL_FILLED;
}
-#define o2info_clear_request_filled(a) \
- __o2info_clear_request_filled((struct ocfs2_info_request *)&(a))
+static inline int o2info_coherent(struct ocfs2_info_request *req)
+{
+ return (!(req->ir_flags & OCFS2_INFO_FL_NON_COHERENT));
+}
static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
{
@@ -153,7 +154,7 @@
oib.ib_blocksize = inode->i_sb->s_blocksize;
- o2info_set_request_filled(oib);
+ o2info_set_request_filled(&oib.ib_req);
if (o2info_to_user(oib, req))
goto bail;
@@ -161,7 +162,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oib, req);
+ o2info_set_request_error(&oib.ib_req, req);
return status;
}
@@ -178,7 +179,7 @@
oic.ic_clustersize = osb->s_clustersize;
- o2info_set_request_filled(oic);
+ o2info_set_request_filled(&oic.ic_req);
if (o2info_to_user(oic, req))
goto bail;
@@ -186,7 +187,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oic, req);
+ o2info_set_request_error(&oic.ic_req, req);
return status;
}
@@ -203,7 +204,7 @@
oim.im_max_slots = osb->max_slots;
- o2info_set_request_filled(oim);
+ o2info_set_request_filled(&oim.im_req);
if (o2info_to_user(oim, req))
goto bail;
@@ -211,7 +212,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oim, req);
+ o2info_set_request_error(&oim.im_req, req);
return status;
}
@@ -228,7 +229,7 @@
memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
- o2info_set_request_filled(oil);
+ o2info_set_request_filled(&oil.il_req);
if (o2info_to_user(oil, req))
goto bail;
@@ -236,7 +237,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oil, req);
+ o2info_set_request_error(&oil.il_req, req);
return status;
}
@@ -253,7 +254,7 @@
memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1);
- o2info_set_request_filled(oiu);
+ o2info_set_request_filled(&oiu.iu_req);
if (o2info_to_user(oiu, req))
goto bail;
@@ -261,7 +262,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oiu, req);
+ o2info_set_request_error(&oiu.iu_req, req);
return status;
}
@@ -280,7 +281,7 @@
oif.if_incompat_features = osb->s_feature_incompat;
oif.if_ro_compat_features = osb->s_feature_ro_compat;
- o2info_set_request_filled(oif);
+ o2info_set_request_filled(&oif.if_req);
if (o2info_to_user(oif, req))
goto bail;
@@ -288,7 +289,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oif, req);
+ o2info_set_request_error(&oif.if_req, req);
return status;
}
@@ -305,7 +306,7 @@
oij.ij_journal_size = osb->journal->j_inode->i_size;
- o2info_set_request_filled(oij);
+ o2info_set_request_filled(&oij.ij_req);
if (o2info_to_user(oij, req))
goto bail;
@@ -313,7 +314,408 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oij, req);
+ o2info_set_request_error(&oij.ij_req, req);
+
+ return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct ocfs2_super *osb,
+ struct inode *inode_alloc, u64 blkno,
+ struct ocfs2_info_freeinode *fi, u32 slot)
+{
+ int status = 0, unlock = 0;
+
+ struct buffer_head *bh = NULL;
+ struct ocfs2_dinode *dinode_alloc = NULL;
+
+ if (inode_alloc)
+ mutex_lock(&inode_alloc->i_mutex);
+
+ if (o2info_coherent(&fi->ifi_req)) {
+ status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+ if (status < 0) {
+ mlog_errno(status);
+ goto bail;
+ }
+ unlock = 1;
+ } else {
+ status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
+ if (status < 0) {
+ mlog_errno(status);
+ goto bail;
+ }
+ }
+
+ dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+ fi->ifi_stat[slot].lfi_total =
+ le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+ fi->ifi_stat[slot].lfi_free =
+ le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+ le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+
+bail:
+ if (unlock)
+ ocfs2_inode_unlock(inode_alloc, 0);
+
+ if (inode_alloc)
+ mutex_unlock(&inode_alloc->i_mutex);
+
+ brelse(bh);
+
+ return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+ struct ocfs2_info_request __user *req)
+{
+ u32 i;
+ u64 blkno = -1;
+ char namebuf[40];
+ int status = -EFAULT, type = INODE_ALLOC_SYSTEM_INODE;
+ struct ocfs2_info_freeinode *oifi = NULL;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct inode *inode_alloc = NULL;
+
+ oifi = kzalloc(sizeof(struct ocfs2_info_freeinode), GFP_KERNEL);
+ if (!oifi) {
+ status = -ENOMEM;
+ mlog_errno(status);
+ goto bail;
+ }
+
+ if (o2info_from_user(*oifi, req))
+ goto bail;
+
+ oifi->ifi_slotnum = osb->max_slots;
+
+ for (i = 0; i < oifi->ifi_slotnum; i++) {
+ if (o2info_coherent(&oifi->ifi_req)) {
+ inode_alloc = ocfs2_get_system_file_inode(osb, type, i);
+ if (!inode_alloc) {
+ mlog(ML_ERROR, "unable to get alloc inode in "
+ "slot %u\n", i);
+ status = -EIO;
+ goto bail;
+ }
+ } else {
+ ocfs2_sprintf_system_inode_name(namebuf,
+ sizeof(namebuf),
+ type, i);
+ status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
+ namebuf,
+ strlen(namebuf),
+ &blkno);
+ if (status < 0) {
+ status = -ENOENT;
+ goto bail;
+ }
+ }
+
+ status = ocfs2_info_scan_inode_alloc(osb, inode_alloc, blkno, oifi, i);
+ if (status < 0)
+ goto bail;
+
+ iput(inode_alloc);
+ inode_alloc = NULL;
+ }
+
+ o2info_set_request_filled(&oifi->ifi_req);
+
+ if (o2info_to_user(*oifi, req))
+ goto bail;
+
+ status = 0;
+bail:
+ if (status)
+ o2info_set_request_error(&oifi->ifi_req, req);
+
+ kfree(oifi);
+
+ return status;
+}
+
+static void o2ffg_update_histogram(struct ocfs2_info_free_chunk_list *hist,
+ unsigned int chunksize)
+{
+ int index;
+
+ index = __ilog2_u32(chunksize);
+ if (index >= OCFS2_INFO_MAX_HIST)
+ index = OCFS2_INFO_MAX_HIST - 1;
+
+ hist->fc_chunks[index]++;
+ hist->fc_clusters[index] += chunksize;
+}
+
+static void o2ffg_update_stats(struct ocfs2_info_freefrag_stats *stats,
+ unsigned int chunksize)
+{
+ if (chunksize > stats->ffs_max)
+ stats->ffs_max = chunksize;
+
+ if (chunksize < stats->ffs_min)
+ stats->ffs_min = chunksize;
+
+ stats->ffs_avg += chunksize;
+ stats->ffs_free_chunks_real++;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+ unsigned int chunksize)
+{
+ o2ffg_update_histogram(&(ffg->iff_ffs.ffs_fc_hist), chunksize);
+ o2ffg_update_stats(&(ffg->iff_ffs), chunksize);
+}
+
+int ocfs2_info_freefrag_scan_chain(struct ocfs2_super *osb,
+ struct inode *gb_inode,
+ struct ocfs2_dinode *gb_dinode,
+ struct ocfs2_chain_rec *rec,
+ struct ocfs2_info_freefrag *ffg,
+ u32 chunks_in_group)
+{
+ int status = 0, used;
+ u64 blkno;
+
+ struct buffer_head *bh = NULL;
+ struct ocfs2_group_desc *bg = NULL;
+
+ unsigned int max_bits, num_clusters;
+ unsigned int offset = 0, cluster, chunk;
+ unsigned int chunk_free, last_chunksize = 0;
+
+ if (!le32_to_cpu(rec->c_free))
+ goto bail;
+
+ do {
+ if (!bg)
+ blkno = le64_to_cpu(rec->c_blkno);
+ else
+ blkno = le64_to_cpu(bg->bg_next_group);
+
+ if (bh) {
+ brelse(bh);
+ bh = NULL;
+ }
+
+ if (o2info_coherent(&ffg->iff_req))
+ status = ocfs2_read_group_descriptor(gb_inode,
+ gb_dinode,
+ blkno, &bh);
+ else
+ status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
+
+ if (status < 0) {
+ mlog(ML_ERROR, "Can't read the group descriptor # "
+ "%llu from device.", (unsigned long long)blkno);
+ status = -EIO;
+ goto bail;
+ }
+
+ bg = (struct ocfs2_group_desc *)bh->b_data;
+
+ if (!le16_to_cpu(bg->bg_free_bits_count))
+ continue;
+
+ max_bits = le16_to_cpu(bg->bg_bits);
+ offset = 0;
+
+ for (chunk = 0; chunk < chunks_in_group; chunk++) {
+ /*
+ * last chunk may be not an entire one.
+ */
+ if ((offset + ffg->iff_chunksize) > max_bits)
+ num_clusters = max_bits - offset;
+ else
+ num_clusters = ffg->iff_chunksize;
+
+ chunk_free = 0;
+ for (cluster = 0; cluster < num_clusters; cluster++) {
+ used = ocfs2_test_bit(offset,
+ (unsigned long *)bg->bg_bitmap);
+ /*
+ * - chunk_free counts free clusters in #N chunk.
+ * - last_chunksize records the size(in) clusters
+ * for the last real free chunk being counted.
+ */
+ if (!used) {
+ last_chunksize++;
+ chunk_free++;
+ }
+
+ if (used && last_chunksize) {
+ ocfs2_info_update_ffg(ffg,
+ last_chunksize);
+ last_chunksize = 0;
+ }
+
+ offset++;
+ }
+
+ if (chunk_free == ffg->iff_chunksize)
+ ffg->iff_ffs.ffs_free_chunks++;
+ }
+
+ /*
+ * need to update the info for last free chunk.
+ */
+ if (last_chunksize)
+ ocfs2_info_update_ffg(ffg, last_chunksize);
+
+ } while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+ brelse(bh);
+
+ return status;
+}
+
+int ocfs2_info_freefrag_scan_bitmap(struct ocfs2_super *osb,
+ struct inode *gb_inode, u64 blkno,
+ struct ocfs2_info_freefrag *ffg)
+{
+ u32 chunks_in_group;
+ int status = 0, unlock = 0, i;
+
+ struct buffer_head *bh = NULL;
+ struct ocfs2_chain_list *cl = NULL;
+ struct ocfs2_chain_rec *rec = NULL;
+ struct ocfs2_dinode *gb_dinode = NULL;
+
+ if (gb_inode)
+ mutex_lock(&gb_inode->i_mutex);
+
+ if (o2info_coherent(&ffg->iff_req)) {
+ status = ocfs2_inode_lock(gb_inode, &bh, 0);
+ if (status < 0) {
+ mlog_errno(status);
+ goto bail;
+ }
+ unlock = 1;
+ } else {
+ status = ocfs2_read_blocks_sync(osb, blkno, 1, &bh);
+ if (status < 0) {
+ mlog_errno(status);
+ goto bail;
+ }
+ }
+
+ gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+ cl = &(gb_dinode->id2.i_chain);
+
+ /*
+ * Chunksize(in) clusters from userspace should be
+ * less than clusters in a group.
+ */
+ if (ffg->iff_chunksize > le16_to_cpu(cl->cl_cpg)) {
+ status = -EINVAL;
+ goto bail;
+ }
+
+ memset(&ffg->iff_ffs, 0, sizeof(struct ocfs2_info_freefrag_stats));
+
+ ffg->iff_ffs.ffs_min = ~0U;
+ ffg->iff_ffs.ffs_clusters =
+ le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+ ffg->iff_ffs.ffs_free_clusters = ffg->iff_ffs.ffs_clusters -
+ le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+ chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->iff_chunksize + 1;
+
+ for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+ rec = &(cl->cl_recs[i]);
+ status = ocfs2_info_freefrag_scan_chain(osb, gb_inode,
+ gb_dinode,
+ rec, ffg,
+ chunks_in_group);
+ if (status)
+ goto bail;
+ }
+
+ if (ffg->iff_ffs.ffs_free_chunks_real)
+ ffg->iff_ffs.ffs_avg = (ffg->iff_ffs.ffs_avg /
+ ffg->iff_ffs.ffs_free_chunks_real);
+bail:
+ if (unlock)
+ ocfs2_inode_unlock(gb_inode, 0);
+
+ if (gb_inode)
+ mutex_unlock(&gb_inode->i_mutex);
+
+ if (gb_inode)
+ iput(gb_inode);
+
+ brelse(bh);
+
+ return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+ struct ocfs2_info_request __user *req)
+{
+ u64 blkno = -1;
+ char namebuf[40];
+ int status = -EFAULT, type = GLOBAL_BITMAP_SYSTEM_INODE;
+
+ struct ocfs2_info_freefrag *oiff;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct inode *gb_inode = NULL;
+
+ oiff = kzalloc(sizeof(struct ocfs2_info_freefrag), GFP_KERNEL);
+ if (!oiff) {
+ status = -ENOMEM;
+ mlog_errno(status);
+ goto bail;
+ }
+
+ if (o2info_from_user(*oiff, req))
+ goto bail;
+ /*
+ * chunksize from userspace should be power of 2.
+ */
+ if ((oiff->iff_chunksize & (oiff->iff_chunksize - 1)) ||
+ (!oiff->iff_chunksize)) {
+ status = -EINVAL;
+ goto bail;
+ }
+
+ if (o2info_coherent(&oiff->iff_req)) {
+ gb_inode = ocfs2_get_system_file_inode(osb, type,
+ OCFS2_INVALID_SLOT);
+ if (!gb_inode) {
+ mlog(ML_ERROR, "unable to get global_bitmap inode\n");
+ status = -EIO;
+ goto bail;
+ }
+ } else {
+ ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type,
+ OCFS2_INVALID_SLOT);
+ status = ocfs2_lookup_ino_from_name(osb->sys_root_inode,
+ namebuf,
+ strlen(namebuf),
+ &blkno);
+ if (status < 0) {
+ status = -ENOENT;
+ goto bail;
+ }
+ }
+
+ status = ocfs2_info_freefrag_scan_bitmap(osb, gb_inode, blkno, oiff);
+ if (status < 0)
+ goto bail;
+
+ o2info_set_request_filled(&oiff->iff_req);
+
+ if (o2info_to_user(*oiff, req))
+ goto bail;
+
+ status = 0;
+bail:
+ if (status)
+ o2info_set_request_error(&oiff->iff_req, req);
+
+ kfree(oiff);
return status;
}
@@ -327,7 +729,7 @@
if (o2info_from_user(oir, req))
goto bail;
- o2info_clear_request_filled(oir);
+ o2info_clear_request_filled(&oir);
if (o2info_to_user(oir, req))
goto bail;
@@ -335,7 +737,7 @@
status = 0;
bail:
if (status)
- o2info_set_request_error(oir, req);
+ o2info_set_request_error(&oir, req);
return status;
}
@@ -389,6 +791,14 @@
if (oir.ir_size == sizeof(struct ocfs2_info_journal_size))
status = ocfs2_info_handle_journal_size(inode, req);
break;
+ case OCFS2_INFO_FREEINODE:
+ if (oir.ir_size == sizeof(struct ocfs2_info_freeinode))
+ status = ocfs2_info_handle_freeinode(inode, req);
+ break;
+ case OCFS2_INFO_FREEFRAG:
+ if (oir.ir_size == sizeof(struct ocfs2_info_freefrag))
+ status = ocfs2_info_handle_freefrag(inode, req);
+ break;
default:
status = ocfs2_info_handle_unknown(inode, req);
break;
@@ -542,6 +952,31 @@
return -EFAULT;
return ocfs2_info_handle(inode, &info, 0);
+ case FITRIM:
+ {
+ struct super_block *sb = inode->i_sb;
+ struct fstrim_range range;
+ int ret = 0;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&range, (struct fstrim_range *)arg,
+ sizeof(range)))
+ return -EFAULT;
+
+ ret = ocfs2_trim_fs(sb, &range);
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user((struct fstrim_range *)arg, &range,
+ sizeof(range)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case OCFS2_IOC_MOVE_EXT:
+ return ocfs2_ioctl_move_extents(filp, (void __user *)arg);
default:
return -ENOTTY;
}
@@ -569,6 +1004,7 @@
case OCFS2_IOC_GROUP_EXTEND:
case OCFS2_IOC_GROUP_ADD:
case OCFS2_IOC_GROUP_ADD64:
+ case FITRIM:
break;
case OCFS2_IOC_REFLINK:
if (copy_from_user(&args, (struct reflink_arguments *)arg,
@@ -584,6 +1020,8 @@
return -EFAULT;
return ocfs2_info_handle(inode, &info, 1);
+ case OCFS2_IOC_MOVE_EXT:
+ break;
default:
return -ENOIOCTLCMD;
}
diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c
new file mode 100644
index 0000000..4c54884
--- /dev/null
+++ b/fs/ocfs2/move_extents.c
@@ -0,0 +1,1153 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * move_extents.c
+ *
+ * Copyright (C) 2011 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/mount.h>
+#include <linux/swap.h>
+
+#include <cluster/masklog.h>
+
+#include "ocfs2.h"
+#include "ocfs2_ioctl.h"
+
+#include "alloc.h"
+#include "aops.h"
+#include "dlmglue.h"
+#include "extent_map.h"
+#include "inode.h"
+#include "journal.h"
+#include "suballoc.h"
+#include "uptodate.h"
+#include "super.h"
+#include "dir.h"
+#include "buffer_head_io.h"
+#include "sysfile.h"
+#include "suballoc.h"
+#include "refcounttree.h"
+#include "move_extents.h"
+
+struct ocfs2_move_extents_context {
+ struct inode *inode;
+ struct file *file;
+ int auto_defrag;
+ int partial;
+ int credits;
+ u32 new_phys_cpos;
+ u32 clusters_moved;
+ u64 refcount_loc;
+ struct ocfs2_move_extents *range;
+ struct ocfs2_extent_tree et;
+ struct ocfs2_alloc_context *meta_ac;
+ struct ocfs2_alloc_context *data_ac;
+ struct ocfs2_cached_dealloc_ctxt dealloc;
+};
+
+static int __ocfs2_move_extent(handle_t *handle,
+ struct ocfs2_move_extents_context *context,
+ u32 cpos, u32 len, u32 p_cpos, u32 new_p_cpos,
+ int ext_flags)
+{
+ int ret = 0, index;
+ struct inode *inode = context->inode;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct ocfs2_extent_rec *rec, replace_rec;
+ struct ocfs2_path *path = NULL;
+ struct ocfs2_extent_list *el;
+ u64 ino = ocfs2_metadata_cache_owner(context->et.et_ci);
+ u64 old_blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cpos);
+
+ ret = ocfs2_duplicate_clusters_by_page(handle, context->file, cpos,
+ p_cpos, new_p_cpos, len);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ memset(&replace_rec, 0, sizeof(replace_rec));
+ replace_rec.e_cpos = cpu_to_le32(cpos);
+ replace_rec.e_leaf_clusters = cpu_to_le16(len);
+ replace_rec.e_blkno = cpu_to_le64(ocfs2_clusters_to_blocks(inode->i_sb,
+ new_p_cpos));
+
+ path = ocfs2_new_path_from_et(&context->et);
+ if (!path) {
+ ret = -ENOMEM;
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ret = ocfs2_find_path(INODE_CACHE(inode), path, cpos);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ el = path_leaf_el(path);
+
+ index = ocfs2_search_extent_list(el, cpos);
+ if (index == -1 || index >= le16_to_cpu(el->l_next_free_rec)) {
+ ocfs2_error(inode->i_sb,
+ "Inode %llu has an extent at cpos %u which can no "
+ "longer be found.\n",
+ (unsigned long long)ino, cpos);
+ ret = -EROFS;
+ goto out;
+ }
+
+ rec = &el->l_recs[index];
+
+ BUG_ON(ext_flags != rec->e_flags);
+ /*
+ * after moving/defraging to new location, the extent is not going
+ * to be refcounted anymore.
+ */
+ replace_rec.e_flags = ext_flags & ~OCFS2_EXT_REFCOUNTED;
+
+ ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode),
+ context->et.et_root_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ret = ocfs2_split_extent(handle, &context->et, path, index,
+ &replace_rec, context->meta_ac,
+ &context->dealloc);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ocfs2_journal_dirty(handle, context->et.et_root_bh);
+
+ context->new_phys_cpos = new_p_cpos;
+
+ /*
+ * need I to append truncate log for old clusters?
+ */
+ if (old_blkno) {
+ if (ext_flags & OCFS2_EXT_REFCOUNTED)
+ ret = ocfs2_decrease_refcount(inode, handle,
+ ocfs2_blocks_to_clusters(osb->sb,
+ old_blkno),
+ len, context->meta_ac,
+ &context->dealloc, 1);
+ else
+ ret = ocfs2_truncate_log_append(osb, handle,
+ old_blkno, len);
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * lock allocators, and reserving appropriate number of bits for
+ * meta blocks and data clusters.
+ *
+ * in some cases, we don't need to reserve clusters, just let data_ac
+ * be NULL.
+ */
+static int ocfs2_lock_allocators_move_extents(struct inode *inode,
+ struct ocfs2_extent_tree *et,
+ u32 clusters_to_move,
+ u32 extents_to_split,
+ struct ocfs2_alloc_context **meta_ac,
+ struct ocfs2_alloc_context **data_ac,
+ int extra_blocks,
+ int *credits)
+{
+ int ret, num_free_extents;
+ unsigned int max_recs_needed = 2 * extents_to_split + clusters_to_move;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+ num_free_extents = ocfs2_num_free_extents(osb, et);
+ if (num_free_extents < 0) {
+ ret = num_free_extents;
+ mlog_errno(ret);
+ goto out;
+ }
+
+ if (!num_free_extents ||
+ (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed))
+ extra_blocks += ocfs2_extend_meta_needed(et->et_root_el);
+
+ ret = ocfs2_reserve_new_metadata_blocks(osb, extra_blocks, meta_ac);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ if (data_ac) {
+ ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+ }
+
+ *credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el,
+ clusters_to_move + 2);
+
+ mlog(0, "reserve metadata_blocks: %d, data_clusters: %u, credits: %d\n",
+ extra_blocks, clusters_to_move, *credits);
+out:
+ if (ret) {
+ if (*meta_ac) {
+ ocfs2_free_alloc_context(*meta_ac);
+ *meta_ac = NULL;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Using one journal handle to guarantee the data consistency in case
+ * crash happens anywhere.
+ *
+ * XXX: defrag can end up with finishing partial extent as requested,
+ * due to not enough contiguous clusters can be found in allocator.
+ */
+static int ocfs2_defrag_extent(struct ocfs2_move_extents_context *context,
+ u32 cpos, u32 phys_cpos, u32 *len, int ext_flags)
+{
+ int ret, credits = 0, extra_blocks = 0, partial = context->partial;
+ handle_t *handle;
+ struct inode *inode = context->inode;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct inode *tl_inode = osb->osb_tl_inode;
+ struct ocfs2_refcount_tree *ref_tree = NULL;
+ u32 new_phys_cpos, new_len;
+ u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+
+ if ((ext_flags & OCFS2_EXT_REFCOUNTED) && *len) {
+
+ BUG_ON(!(OCFS2_I(inode)->ip_dyn_features &
+ OCFS2_HAS_REFCOUNT_FL));
+
+ BUG_ON(!context->refcount_loc);
+
+ ret = ocfs2_lock_refcount_tree(osb, context->refcount_loc, 1,
+ &ref_tree, NULL);
+ if (ret) {
+ mlog_errno(ret);
+ return ret;
+ }
+
+ ret = ocfs2_prepare_refcount_change_for_del(inode,
+ context->refcount_loc,
+ phys_blkno,
+ *len,
+ &credits,
+ &extra_blocks);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+ }
+
+ ret = ocfs2_lock_allocators_move_extents(inode, &context->et, *len, 1,
+ &context->meta_ac,
+ &context->data_ac,
+ extra_blocks, &credits);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ /*
+ * should be using allocation reservation strategy there?
+ *
+ * if (context->data_ac)
+ * context->data_ac->ac_resv = &OCFS2_I(inode)->ip_la_data_resv;
+ */
+
+ mutex_lock(&tl_inode->i_mutex);
+
+ if (ocfs2_truncate_log_needs_flush(osb)) {
+ ret = __ocfs2_flush_truncate_log(osb);
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out_unlock_mutex;
+ }
+ }
+
+ handle = ocfs2_start_trans(osb, credits);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ mlog_errno(ret);
+ goto out_unlock_mutex;
+ }
+
+ ret = __ocfs2_claim_clusters(handle, context->data_ac, 1, *len,
+ &new_phys_cpos, &new_len);
+ if (ret) {
+ mlog_errno(ret);
+ goto out_commit;
+ }
+
+ /*
+ * allowing partial extent moving is kind of 'pros and cons', it makes
+ * whole defragmentation less likely to fail, on the contrary, the bad
+ * thing is it may make the fs even more fragmented after moving, let
+ * userspace make a good decision here.
+ */
+ if (new_len != *len) {
+ mlog(0, "len_claimed: %u, len: %u\n", new_len, *len);
+ if (!partial) {
+ context->range->me_flags &= ~OCFS2_MOVE_EXT_FL_COMPLETE;
+ ret = -ENOSPC;
+ goto out_commit;
+ }
+ }
+
+ mlog(0, "cpos: %u, phys_cpos: %u, new_phys_cpos: %u\n", cpos,
+ phys_cpos, new_phys_cpos);
+
+ ret = __ocfs2_move_extent(handle, context, cpos, new_len, phys_cpos,
+ new_phys_cpos, ext_flags);
+ if (ret)
+ mlog_errno(ret);
+
+ if (partial && (new_len != *len))
+ *len = new_len;
+
+ /*
+ * Here we should write the new page out first if we are
+ * in write-back mode.
+ */
+ ret = ocfs2_cow_sync_writeback(inode->i_sb, context->inode, cpos, *len);
+ if (ret)
+ mlog_errno(ret);
+
+out_commit:
+ ocfs2_commit_trans(osb, handle);
+
+out_unlock_mutex:
+ mutex_unlock(&tl_inode->i_mutex);
+
+ if (context->data_ac) {
+ ocfs2_free_alloc_context(context->data_ac);
+ context->data_ac = NULL;
+ }
+
+ if (context->meta_ac) {
+ ocfs2_free_alloc_context(context->meta_ac);
+ context->meta_ac = NULL;
+ }
+
+out:
+ if (ref_tree)
+ ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
+
+ return ret;
+}
+
+/*
+ * find the victim alloc group, where #blkno fits.
+ */
+static int ocfs2_find_victim_alloc_group(struct inode *inode,
+ u64 vict_blkno,
+ int type, int slot,
+ int *vict_bit,
+ struct buffer_head **ret_bh)
+{
+ int ret, i, blocks_per_unit = 1;
+ u64 blkno;
+ char namebuf[40];
+
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct buffer_head *ac_bh = NULL, *gd_bh = NULL;
+ struct ocfs2_chain_list *cl;
+ struct ocfs2_chain_rec *rec;
+ struct ocfs2_dinode *ac_dinode;
+ struct ocfs2_group_desc *bg;
+
+ ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, slot);
+ ret = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf,
+ strlen(namebuf), &blkno);
+ if (ret) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = ocfs2_read_blocks_sync(osb, blkno, 1, &ac_bh);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ac_dinode = (struct ocfs2_dinode *)ac_bh->b_data;
+ cl = &(ac_dinode->id2.i_chain);
+ rec = &(cl->cl_recs[0]);
+
+ if (type == GLOBAL_BITMAP_SYSTEM_INODE)
+ blocks_per_unit <<= (osb->s_clustersize_bits -
+ inode->i_sb->s_blocksize_bits);
+ /*
+ * 'vict_blkno' was out of the valid range.
+ */
+ if ((vict_blkno < le64_to_cpu(rec->c_blkno)) ||
+ (vict_blkno >= (le32_to_cpu(ac_dinode->id1.bitmap1.i_total) *
+ blocks_per_unit))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+ rec = &(cl->cl_recs[i]);
+ if (!rec)
+ continue;
+
+ bg = NULL;
+
+ do {
+ if (!bg)
+ blkno = le64_to_cpu(rec->c_blkno);
+ else
+ blkno = le64_to_cpu(bg->bg_next_group);
+
+ if (gd_bh) {
+ brelse(gd_bh);
+ gd_bh = NULL;
+ }
+
+ ret = ocfs2_read_blocks_sync(osb, blkno, 1, &gd_bh);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ bg = (struct ocfs2_group_desc *)gd_bh->b_data;
+
+ if (vict_blkno < (le64_to_cpu(bg->bg_blkno) +
+ le16_to_cpu(bg->bg_bits))) {
+
+ *ret_bh = gd_bh;
+ *vict_bit = (vict_blkno - blkno) /
+ blocks_per_unit;
+ mlog(0, "find the victim group: #%llu, "
+ "total_bits: %u, vict_bit: %u\n",
+ blkno, le16_to_cpu(bg->bg_bits),
+ *vict_bit);
+ goto out;
+ }
+
+ } while (le64_to_cpu(bg->bg_next_group));
+ }
+
+ ret = -EINVAL;
+out:
+ brelse(ac_bh);
+
+ /*
+ * caller has to release the gd_bh properly.
+ */
+ return ret;
+}
+
+/*
+ * XXX: helper to validate and adjust moving goal.
+ */
+static int ocfs2_validate_and_adjust_move_goal(struct inode *inode,
+ struct ocfs2_move_extents *range)
+{
+ int ret, goal_bit = 0;
+
+ struct buffer_head *gd_bh = NULL;
+ struct ocfs2_group_desc *bg;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ int c_to_b = 1 << (osb->s_clustersize_bits -
+ inode->i_sb->s_blocksize_bits);
+
+ /*
+ * validate goal sits within global_bitmap, and return the victim
+ * group desc
+ */
+ ret = ocfs2_find_victim_alloc_group(inode, range->me_goal,
+ GLOBAL_BITMAP_SYSTEM_INODE,
+ OCFS2_INVALID_SLOT,
+ &goal_bit, &gd_bh);
+ if (ret)
+ goto out;
+
+ bg = (struct ocfs2_group_desc *)gd_bh->b_data;
+
+ /*
+ * make goal become cluster aligned.
+ */
+ if (range->me_goal % c_to_b)
+ range->me_goal = range->me_goal / c_to_b * c_to_b;
+
+ /*
+ * moving goal is not allowd to start with a group desc blok(#0 blk)
+ * let's compromise to the latter cluster.
+ */
+ if (range->me_goal == le64_to_cpu(bg->bg_blkno))
+ range->me_goal += c_to_b;
+
+ /*
+ * movement is not gonna cross two groups.
+ */
+ if ((le16_to_cpu(bg->bg_bits) - goal_bit) * osb->s_clustersize <
+ range->me_len) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /*
+ * more exact validations/adjustments will be performed later during
+ * moving operation for each extent range.
+ */
+ mlog(0, "extents get ready to be moved to #%llu block\n",
+ range->me_goal);
+
+out:
+ brelse(gd_bh);
+
+ return ret;
+}
+
+static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh,
+ int *goal_bit, u32 move_len, u32 max_hop,
+ u32 *phys_cpos)
+{
+ int i, used, last_free_bits = 0, base_bit = *goal_bit;
+ struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data;
+ u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb,
+ le64_to_cpu(gd->bg_blkno));
+
+ for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) {
+
+ used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap);
+ if (used) {
+ /*
+ * we even tried searching the free chunk by jumping
+ * a 'max_hop' distance, but still failed.
+ */
+ if ((i - base_bit) > max_hop) {
+ *phys_cpos = 0;
+ break;
+ }
+
+ if (last_free_bits)
+ last_free_bits = 0;
+
+ continue;
+ } else
+ last_free_bits++;
+
+ if (last_free_bits == move_len) {
+ *goal_bit = i;
+ *phys_cpos = base_cpos + i;
+ break;
+ }
+ }
+
+ mlog(0, "found phys_cpos: %u to fit the wanted moving.\n", *phys_cpos);
+}
+
+static int ocfs2_alloc_dinode_update_counts(struct inode *inode,
+ handle_t *handle,
+ struct buffer_head *di_bh,
+ u32 num_bits,
+ u16 chain)
+{
+ int ret;
+ u32 tmp_used;
+ struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data;
+ struct ocfs2_chain_list *cl =
+ (struct ocfs2_chain_list *) &di->id2.i_chain;
+
+ ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ tmp_used = le32_to_cpu(di->id1.bitmap1.i_used);
+ di->id1.bitmap1.i_used = cpu_to_le32(num_bits + tmp_used);
+ le32_add_cpu(&cl->cl_recs[chain].c_free, -num_bits);
+ ocfs2_journal_dirty(handle, di_bh);
+
+out:
+ return ret;
+}
+
+static inline int ocfs2_block_group_set_bits(handle_t *handle,
+ struct inode *alloc_inode,
+ struct ocfs2_group_desc *bg,
+ struct buffer_head *group_bh,
+ unsigned int bit_off,
+ unsigned int num_bits)
+{
+ int status;
+ void *bitmap = bg->bg_bitmap;
+ int journal_type = OCFS2_JOURNAL_ACCESS_WRITE;
+
+ /* All callers get the descriptor via
+ * ocfs2_read_group_descriptor(). Any corruption is a code bug. */
+ BUG_ON(!OCFS2_IS_VALID_GROUP_DESC(bg));
+ BUG_ON(le16_to_cpu(bg->bg_free_bits_count) < num_bits);
+
+ mlog(0, "block_group_set_bits: off = %u, num = %u\n", bit_off,
+ num_bits);
+
+ if (ocfs2_is_cluster_bitmap(alloc_inode))
+ journal_type = OCFS2_JOURNAL_ACCESS_UNDO;
+
+ status = ocfs2_journal_access_gd(handle,
+ INODE_CACHE(alloc_inode),
+ group_bh,
+ journal_type);
+ if (status < 0) {
+ mlog_errno(status);
+ goto bail;
+ }
+
+ le16_add_cpu(&bg->bg_free_bits_count, -num_bits);
+ if (le16_to_cpu(bg->bg_free_bits_count) > le16_to_cpu(bg->bg_bits)) {
+ ocfs2_error(alloc_inode->i_sb, "Group descriptor # %llu has bit"
+ " count %u but claims %u are freed. num_bits %d",
+ (unsigned long long)le64_to_cpu(bg->bg_blkno),
+ le16_to_cpu(bg->bg_bits),
+ le16_to_cpu(bg->bg_free_bits_count), num_bits);
+ return -EROFS;
+ }
+ while (num_bits--)
+ ocfs2_set_bit(bit_off++, bitmap);
+
+ ocfs2_journal_dirty(handle, group_bh);
+
+bail:
+ return status;
+}
+
+static int ocfs2_move_extent(struct ocfs2_move_extents_context *context,
+ u32 cpos, u32 phys_cpos, u32 *new_phys_cpos,
+ u32 len, int ext_flags)
+{
+ int ret, credits = 0, extra_blocks = 0, goal_bit = 0;
+ handle_t *handle;
+ struct inode *inode = context->inode;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct inode *tl_inode = osb->osb_tl_inode;
+ struct inode *gb_inode = NULL;
+ struct buffer_head *gb_bh = NULL;
+ struct buffer_head *gd_bh = NULL;
+ struct ocfs2_group_desc *gd;
+ struct ocfs2_refcount_tree *ref_tree = NULL;
+ u32 move_max_hop = ocfs2_blocks_to_clusters(inode->i_sb,
+ context->range->me_threshold);
+ u64 phys_blkno, new_phys_blkno;
+
+ phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+
+ if ((ext_flags & OCFS2_EXT_REFCOUNTED) && len) {
+
+ BUG_ON(!(OCFS2_I(inode)->ip_dyn_features &
+ OCFS2_HAS_REFCOUNT_FL));
+
+ BUG_ON(!context->refcount_loc);
+
+ ret = ocfs2_lock_refcount_tree(osb, context->refcount_loc, 1,
+ &ref_tree, NULL);
+ if (ret) {
+ mlog_errno(ret);
+ return ret;
+ }
+
+ ret = ocfs2_prepare_refcount_change_for_del(inode,
+ context->refcount_loc,
+ phys_blkno,
+ len,
+ &credits,
+ &extra_blocks);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+ }
+
+ ret = ocfs2_lock_allocators_move_extents(inode, &context->et, len, 1,
+ &context->meta_ac,
+ NULL, extra_blocks, &credits);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ /*
+ * need to count 2 extra credits for global_bitmap inode and
+ * group descriptor.
+ */
+ credits += OCFS2_INODE_UPDATE_CREDITS + 1;
+
+ /*
+ * ocfs2_move_extent() didn't reserve any clusters in lock_allocators()
+ * logic, while we still need to lock the global_bitmap.
+ */
+ gb_inode = ocfs2_get_system_file_inode(osb, GLOBAL_BITMAP_SYSTEM_INODE,
+ OCFS2_INVALID_SLOT);
+ if (!gb_inode) {
+ mlog(ML_ERROR, "unable to get global_bitmap inode\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ mutex_lock(&gb_inode->i_mutex);
+
+ ret = ocfs2_inode_lock(gb_inode, &gb_bh, 1);
+ if (ret) {
+ mlog_errno(ret);
+ goto out_unlock_gb_mutex;
+ }
+
+ mutex_lock(&tl_inode->i_mutex);
+
+ handle = ocfs2_start_trans(osb, credits);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ mlog_errno(ret);
+ goto out_unlock_tl_inode;
+ }
+
+ new_phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, *new_phys_cpos);
+ ret = ocfs2_find_victim_alloc_group(inode, new_phys_blkno,
+ GLOBAL_BITMAP_SYSTEM_INODE,
+ OCFS2_INVALID_SLOT,
+ &goal_bit, &gd_bh);
+ if (ret) {
+ mlog_errno(ret);
+ goto out_commit;
+ }
+
+ /*
+ * probe the victim cluster group to find a proper
+ * region to fit wanted movement, it even will perfrom
+ * a best-effort attempt by compromising to a threshold
+ * around the goal.
+ */
+ ocfs2_probe_alloc_group(inode, gd_bh, &goal_bit, len, move_max_hop,
+ new_phys_cpos);
+ if (!new_phys_cpos) {
+ ret = -ENOSPC;
+ goto out_commit;
+ }
+
+ ret = __ocfs2_move_extent(handle, context, cpos, len, phys_cpos,
+ *new_phys_cpos, ext_flags);
+ if (ret) {
+ mlog_errno(ret);
+ goto out_commit;
+ }
+
+ gd = (struct ocfs2_group_desc *)gd_bh->b_data;
+ ret = ocfs2_alloc_dinode_update_counts(gb_inode, handle, gb_bh, len,
+ le16_to_cpu(gd->bg_chain));
+ if (ret) {
+ mlog_errno(ret);
+ goto out_commit;
+ }
+
+ ret = ocfs2_block_group_set_bits(handle, gb_inode, gd, gd_bh,
+ goal_bit, len);
+ if (ret)
+ mlog_errno(ret);
+
+ /*
+ * Here we should write the new page out first if we are
+ * in write-back mode.
+ */
+ ret = ocfs2_cow_sync_writeback(inode->i_sb, context->inode, cpos, len);
+ if (ret)
+ mlog_errno(ret);
+
+out_commit:
+ ocfs2_commit_trans(osb, handle);
+ brelse(gd_bh);
+
+out_unlock_tl_inode:
+ mutex_unlock(&tl_inode->i_mutex);
+
+ ocfs2_inode_unlock(gb_inode, 1);
+out_unlock_gb_mutex:
+ mutex_unlock(&gb_inode->i_mutex);
+ brelse(gb_bh);
+ iput(gb_inode);
+
+out:
+ if (context->meta_ac) {
+ ocfs2_free_alloc_context(context->meta_ac);
+ context->meta_ac = NULL;
+ }
+
+ if (ref_tree)
+ ocfs2_unlock_refcount_tree(osb, ref_tree, 1);
+
+ return ret;
+}
+
+/*
+ * Helper to calculate the defraging length in one run according to threshold.
+ */
+static void ocfs2_calc_extent_defrag_len(u32 *alloc_size, u32 *len_defraged,
+ u32 threshold, int *skip)
+{
+ if ((*alloc_size + *len_defraged) < threshold) {
+ /*
+ * proceed defragmentation until we meet the thresh
+ */
+ *len_defraged += *alloc_size;
+ } else if (*len_defraged == 0) {
+ /*
+ * XXX: skip a large extent.
+ */
+ *skip = 1;
+ } else {
+ /*
+ * split this extent to coalesce with former pieces as
+ * to reach the threshold.
+ *
+ * we're done here with one cycle of defragmentation
+ * in a size of 'thresh', resetting 'len_defraged'
+ * forces a new defragmentation.
+ */
+ *alloc_size = threshold - *len_defraged;
+ *len_defraged = 0;
+ }
+}
+
+static int __ocfs2_move_extents_range(struct buffer_head *di_bh,
+ struct ocfs2_move_extents_context *context)
+{
+ int ret = 0, flags, do_defrag, skip = 0;
+ u32 cpos, phys_cpos, move_start, len_to_move, alloc_size;
+ u32 len_defraged = 0, defrag_thresh = 0, new_phys_cpos = 0;
+
+ struct inode *inode = context->inode;
+ struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+ struct ocfs2_move_extents *range = context->range;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+ if ((inode->i_size == 0) || (range->me_len == 0))
+ return 0;
+
+ if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)
+ return 0;
+
+ context->refcount_loc = le64_to_cpu(di->i_refcount_loc);
+
+ ocfs2_init_dinode_extent_tree(&context->et, INODE_CACHE(inode), di_bh);
+ ocfs2_init_dealloc_ctxt(&context->dealloc);
+
+ /*
+ * TO-DO XXX:
+ *
+ * - xattr extents.
+ */
+
+ do_defrag = context->auto_defrag;
+
+ /*
+ * extents moving happens in unit of clusters, for the sake
+ * of simplicity, we may ignore two clusters where 'byte_start'
+ * and 'byte_start + len' were within.
+ */
+ move_start = ocfs2_clusters_for_bytes(osb->sb, range->me_start);
+ len_to_move = (range->me_start + range->me_len) >>
+ osb->s_clustersize_bits;
+ if (len_to_move >= move_start)
+ len_to_move -= move_start;
+ else
+ len_to_move = 0;
+
+ if (do_defrag) {
+ defrag_thresh = range->me_threshold >> osb->s_clustersize_bits;
+ if (defrag_thresh <= 1)
+ goto done;
+ } else
+ new_phys_cpos = ocfs2_blocks_to_clusters(inode->i_sb,
+ range->me_goal);
+
+ mlog(0, "Inode: %llu, start: %llu, len: %llu, cstart: %u, clen: %u, "
+ "thresh: %u\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ (unsigned long long)range->me_start,
+ (unsigned long long)range->me_len,
+ move_start, len_to_move, defrag_thresh);
+
+ cpos = move_start;
+ while (len_to_move) {
+ ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &alloc_size,
+ &flags);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ if (alloc_size > len_to_move)
+ alloc_size = len_to_move;
+
+ /*
+ * XXX: how to deal with a hole:
+ *
+ * - skip the hole of course
+ * - force a new defragmentation
+ */
+ if (!phys_cpos) {
+ if (do_defrag)
+ len_defraged = 0;
+
+ goto next;
+ }
+
+ if (do_defrag) {
+ ocfs2_calc_extent_defrag_len(&alloc_size, &len_defraged,
+ defrag_thresh, &skip);
+ /*
+ * skip large extents
+ */
+ if (skip) {
+ skip = 0;
+ goto next;
+ }
+
+ mlog(0, "#Defrag: cpos: %u, phys_cpos: %u, "
+ "alloc_size: %u, len_defraged: %u\n",
+ cpos, phys_cpos, alloc_size, len_defraged);
+
+ ret = ocfs2_defrag_extent(context, cpos, phys_cpos,
+ &alloc_size, flags);
+ } else {
+ ret = ocfs2_move_extent(context, cpos, phys_cpos,
+ &new_phys_cpos, alloc_size,
+ flags);
+
+ new_phys_cpos += alloc_size;
+ }
+
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ context->clusters_moved += alloc_size;
+next:
+ cpos += alloc_size;
+ len_to_move -= alloc_size;
+ }
+
+done:
+ range->me_flags |= OCFS2_MOVE_EXT_FL_COMPLETE;
+
+out:
+ range->me_moved_len = ocfs2_clusters_to_bytes(osb->sb,
+ context->clusters_moved);
+ range->me_new_offset = ocfs2_clusters_to_bytes(osb->sb,
+ context->new_phys_cpos);
+
+ ocfs2_schedule_truncate_log_flush(osb, 1);
+ ocfs2_run_deallocs(osb, &context->dealloc);
+
+ return ret;
+}
+
+static int ocfs2_move_extents(struct ocfs2_move_extents_context *context)
+{
+ int status;
+ handle_t *handle;
+ struct inode *inode = context->inode;
+ struct ocfs2_dinode *di;
+ struct buffer_head *di_bh = NULL;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+ if (!inode)
+ return -ENOENT;
+
+ if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb))
+ return -EROFS;
+
+ mutex_lock(&inode->i_mutex);
+
+ /*
+ * This prevents concurrent writes from other nodes
+ */
+ status = ocfs2_rw_lock(inode, 1);
+ if (status) {
+ mlog_errno(status);
+ goto out;
+ }
+
+ status = ocfs2_inode_lock(inode, &di_bh, 1);
+ if (status) {
+ mlog_errno(status);
+ goto out_rw_unlock;
+ }
+
+ /*
+ * rememer ip_xattr_sem also needs to be held if necessary
+ */
+ down_write(&OCFS2_I(inode)->ip_alloc_sem);
+
+ status = __ocfs2_move_extents_range(di_bh, context);
+
+ up_write(&OCFS2_I(inode)->ip_alloc_sem);
+ if (status) {
+ mlog_errno(status);
+ goto out_inode_unlock;
+ }
+
+ /*
+ * We update ctime for these changes
+ */
+ handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
+ if (IS_ERR(handle)) {
+ status = PTR_ERR(handle);
+ mlog_errno(status);
+ goto out_inode_unlock;
+ }
+
+ status = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (status) {
+ mlog_errno(status);
+ goto out_commit;
+ }
+
+ di = (struct ocfs2_dinode *)di_bh->b_data;
+ inode->i_ctime = CURRENT_TIME;
+ di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
+ di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+
+ ocfs2_journal_dirty(handle, di_bh);
+
+out_commit:
+ ocfs2_commit_trans(osb, handle);
+
+out_inode_unlock:
+ brelse(di_bh);
+ ocfs2_inode_unlock(inode, 1);
+out_rw_unlock:
+ ocfs2_rw_unlock(inode, 1);
+out:
+ mutex_unlock(&inode->i_mutex);
+
+ return status;
+}
+
+int ocfs2_ioctl_move_extents(struct file *filp, void __user *argp)
+{
+ int status;
+
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct ocfs2_move_extents range;
+ struct ocfs2_move_extents_context *context = NULL;
+
+ status = mnt_want_write(filp->f_path.mnt);
+ if (status)
+ return status;
+
+ if ((!S_ISREG(inode->i_mode)) || !(filp->f_mode & FMODE_WRITE))
+ goto out;
+
+ if (inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
+ status = -EPERM;
+ goto out;
+ }
+
+ context = kzalloc(sizeof(struct ocfs2_move_extents_context), GFP_NOFS);
+ if (!context) {
+ status = -ENOMEM;
+ mlog_errno(status);
+ goto out;
+ }
+
+ context->inode = inode;
+ context->file = filp;
+
+ if (argp) {
+ if (copy_from_user(&range, (struct ocfs2_move_extents *)argp,
+ sizeof(range))) {
+ status = -EFAULT;
+ goto out;
+ }
+ } else {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (range.me_start > i_size_read(inode))
+ goto out;
+
+ if (range.me_start + range.me_len > i_size_read(inode))
+ range.me_len = i_size_read(inode) - range.me_start;
+
+ context->range = ⦥
+
+ if (range.me_flags & OCFS2_MOVE_EXT_FL_AUTO_DEFRAG) {
+ context->auto_defrag = 1;
+ /*
+ * ok, the default theshold for the defragmentation
+ * is 1M, since our maximum clustersize was 1M also.
+ * any thought?
+ */
+ if (!range.me_threshold)
+ range.me_threshold = 1024 * 1024;
+
+ if (range.me_threshold > i_size_read(inode))
+ range.me_threshold = i_size_read(inode);
+
+ if (range.me_flags & OCFS2_MOVE_EXT_FL_PART_DEFRAG)
+ context->partial = 1;
+ } else {
+ /*
+ * first best-effort attempt to validate and adjust the goal
+ * (physical address in block), while it can't guarantee later
+ * operation can succeed all the time since global_bitmap may
+ * change a bit over time.
+ */
+
+ status = ocfs2_validate_and_adjust_move_goal(inode, &range);
+ if (status)
+ goto out;
+ }
+
+ status = ocfs2_move_extents(context);
+ if (status)
+ mlog_errno(status);
+out:
+ /*
+ * movement/defragmentation may end up being partially completed,
+ * that's the reason why we need to return userspace the finished
+ * length and new_offset even if failure happens somewhere.
+ */
+ if (argp) {
+ if (copy_to_user((struct ocfs2_move_extents *)argp, &range,
+ sizeof(range)))
+ status = -EFAULT;
+ }
+
+ kfree(context);
+
+ mnt_drop_write(filp->f_path.mnt);
+
+ return status;
+}
diff --git a/fs/ocfs2/move_extents.h b/fs/ocfs2/move_extents.h
new file mode 100644
index 0000000..4e143e8
--- /dev/null
+++ b/fs/ocfs2/move_extents.h
@@ -0,0 +1,22 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * move_extents.h
+ *
+ * Copyright (C) 2011 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#ifndef OCFS2_MOVE_EXTENTS_H
+#define OCFS2_MOVE_EXTENTS_H
+
+int ocfs2_ioctl_move_extents(struct file *filp, void __user *argp);
+
+#endif /* OCFS2_MOVE_EXTENTS_H */
diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
index b46f39b..5b27ff1 100644
--- a/fs/ocfs2/ocfs2_ioctl.h
+++ b/fs/ocfs2/ocfs2_ioctl.h
@@ -142,6 +142,38 @@
__u64 ij_journal_size;
};
+struct ocfs2_info_freeinode {
+ struct ocfs2_info_request ifi_req;
+ struct ocfs2_info_local_freeinode {
+ __u64 lfi_total;
+ __u64 lfi_free;
+ } ifi_stat[OCFS2_MAX_SLOTS];
+ __u32 ifi_slotnum; /* out */
+ __u32 ifi_pad;
+};
+
+#define OCFS2_INFO_MAX_HIST (32)
+
+struct ocfs2_info_freefrag {
+ struct ocfs2_info_request iff_req;
+ struct ocfs2_info_freefrag_stats { /* (out) */
+ struct ocfs2_info_free_chunk_list {
+ __u32 fc_chunks[OCFS2_INFO_MAX_HIST];
+ __u32 fc_clusters[OCFS2_INFO_MAX_HIST];
+ } ffs_fc_hist;
+ __u32 ffs_clusters;
+ __u32 ffs_free_clusters;
+ __u32 ffs_free_chunks;
+ __u32 ffs_free_chunks_real;
+ __u32 ffs_min; /* Minimum free chunksize in clusters */
+ __u32 ffs_max;
+ __u32 ffs_avg;
+ __u32 ffs_pad;
+ } iff_ffs;
+ __u32 iff_chunksize; /* chunksize in clusters(in) */
+ __u32 iff_pad;
+};
+
/* Codes for ocfs2_info_request */
enum ocfs2_info_type {
OCFS2_INFO_CLUSTERSIZE = 1,
@@ -151,6 +183,8 @@
OCFS2_INFO_UUID,
OCFS2_INFO_FS_FEATURES,
OCFS2_INFO_JOURNAL_SIZE,
+ OCFS2_INFO_FREEINODE,
+ OCFS2_INFO_FREEFRAG,
OCFS2_INFO_NUM_TYPES
};
@@ -171,4 +205,38 @@
#define OCFS2_IOC_INFO _IOR('o', 5, struct ocfs2_info)
+struct ocfs2_move_extents {
+/* All values are in bytes */
+ /* in */
+ __u64 me_start; /* Virtual start in the file to move */
+ __u64 me_len; /* Length of the extents to be moved */
+ __u64 me_goal; /* Physical offset of the goal,
+ it's in block unit */
+ __u64 me_threshold; /* Maximum distance from goal or threshold
+ for auto defragmentation */
+ __u64 me_flags; /* Flags for the operation:
+ * - auto defragmentation.
+ * - refcount,xattr cases.
+ */
+ /* out */
+ __u64 me_moved_len; /* Moved/defraged length */
+ __u64 me_new_offset; /* Resulting physical location */
+ __u32 me_reserved[2]; /* Reserved for futhure */
+};
+
+#define OCFS2_MOVE_EXT_FL_AUTO_DEFRAG (0x00000001) /* Kernel manages to
+ claim new clusters
+ as the goal place
+ for extents moving */
+#define OCFS2_MOVE_EXT_FL_PART_DEFRAG (0x00000002) /* Allow partial extent
+ moving, is to make
+ movement less likely
+ to fail, may make fs
+ even more fragmented */
+#define OCFS2_MOVE_EXT_FL_COMPLETE (0x00000004) /* Move or defragmenation
+ completely gets done.
+ */
+
+#define OCFS2_IOC_MOVE_EXT _IOW('o', 6, struct ocfs2_move_extents)
+
#endif /* OCFS2_IOCTL_H */
diff --git a/fs/ocfs2/ocfs2_trace.h b/fs/ocfs2/ocfs2_trace.h
index a1dae5b..3b481f4 100644
--- a/fs/ocfs2/ocfs2_trace.h
+++ b/fs/ocfs2/ocfs2_trace.h
@@ -688,6 +688,31 @@
__entry->blkno, __entry->bit)
);
+TRACE_EVENT(ocfs2_trim_extent,
+ TP_PROTO(struct super_block *sb, unsigned long long blk,
+ unsigned long long count),
+ TP_ARGS(sb, blk, count),
+ TP_STRUCT__entry(
+ __field(int, dev_major)
+ __field(int, dev_minor)
+ __field(unsigned long long, blk)
+ __field(__u64, count)
+ ),
+ TP_fast_assign(
+ __entry->dev_major = MAJOR(sb->s_dev);
+ __entry->dev_minor = MINOR(sb->s_dev);
+ __entry->blk = blk;
+ __entry->count = count;
+ ),
+ TP_printk("%d %d %llu %llu",
+ __entry->dev_major, __entry->dev_minor,
+ __entry->blk, __entry->count)
+);
+
+DEFINE_OCFS2_ULL_UINT_UINT_UINT_EVENT(ocfs2_trim_group);
+
+DEFINE_OCFS2_ULL_ULL_ULL_EVENT(ocfs2_trim_fs);
+
/* End of trace events for fs/ocfs2/alloc.c. */
/* Trace events for fs/ocfs2/localalloc.c. */
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index 3c7606c..ebfd382 100644
--- a/fs/ocfs2/refcounttree.c
+++ b/fs/ocfs2/refcounttree.c
@@ -66,7 +66,7 @@
u32 *num_clusters,
unsigned int *extent_flags);
int (*cow_duplicate_clusters)(handle_t *handle,
- struct ocfs2_cow_context *context,
+ struct file *file,
u32 cpos, u32 old_cluster,
u32 new_cluster, u32 new_len);
};
@@ -2921,20 +2921,21 @@
return 0;
}
-static int ocfs2_duplicate_clusters_by_page(handle_t *handle,
- struct ocfs2_cow_context *context,
- u32 cpos, u32 old_cluster,
- u32 new_cluster, u32 new_len)
+int ocfs2_duplicate_clusters_by_page(handle_t *handle,
+ struct file *file,
+ u32 cpos, u32 old_cluster,
+ u32 new_cluster, u32 new_len)
{
int ret = 0, partial;
- struct ocfs2_caching_info *ci = context->data_et.et_ci;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct ocfs2_caching_info *ci = INODE_CACHE(inode);
struct super_block *sb = ocfs2_metadata_cache_get_super(ci);
u64 new_block = ocfs2_clusters_to_blocks(sb, new_cluster);
struct page *page;
pgoff_t page_index;
unsigned int from, to, readahead_pages;
loff_t offset, end, map_end;
- struct address_space *mapping = context->inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
trace_ocfs2_duplicate_clusters_by_page(cpos, old_cluster,
new_cluster, new_len);
@@ -2948,8 +2949,8 @@
* We only duplicate pages until we reach the page contains i_size - 1.
* So trim 'end' to i_size.
*/
- if (end > i_size_read(context->inode))
- end = i_size_read(context->inode);
+ if (end > i_size_read(inode))
+ end = i_size_read(inode);
while (offset < end) {
page_index = offset >> PAGE_CACHE_SHIFT;
@@ -2972,10 +2973,9 @@
if (PAGE_CACHE_SIZE <= OCFS2_SB(sb)->s_clustersize)
BUG_ON(PageDirty(page));
- if (PageReadahead(page) && context->file) {
+ if (PageReadahead(page)) {
page_cache_async_readahead(mapping,
- &context->file->f_ra,
- context->file,
+ &file->f_ra, file,
page, page_index,
readahead_pages);
}
@@ -2999,8 +2999,7 @@
}
}
- ocfs2_map_and_dirty_page(context->inode,
- handle, from, to,
+ ocfs2_map_and_dirty_page(inode, handle, from, to,
page, 0, &new_block);
mark_page_accessed(page);
unlock:
@@ -3015,14 +3014,15 @@
return ret;
}
-static int ocfs2_duplicate_clusters_by_jbd(handle_t *handle,
- struct ocfs2_cow_context *context,
- u32 cpos, u32 old_cluster,
- u32 new_cluster, u32 new_len)
+int ocfs2_duplicate_clusters_by_jbd(handle_t *handle,
+ struct file *file,
+ u32 cpos, u32 old_cluster,
+ u32 new_cluster, u32 new_len)
{
int ret = 0;
- struct super_block *sb = context->inode->i_sb;
- struct ocfs2_caching_info *ci = context->data_et.et_ci;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct ocfs2_caching_info *ci = INODE_CACHE(inode);
int i, blocks = ocfs2_clusters_to_blocks(sb, new_len);
u64 old_block = ocfs2_clusters_to_blocks(sb, old_cluster);
u64 new_block = ocfs2_clusters_to_blocks(sb, new_cluster);
@@ -3145,8 +3145,8 @@
/*If the old clusters is unwritten, no need to duplicate. */
if (!(ext_flags & OCFS2_EXT_UNWRITTEN)) {
- ret = context->cow_duplicate_clusters(handle, context, cpos,
- old, new, len);
+ ret = context->cow_duplicate_clusters(handle, context->file,
+ cpos, old, new, len);
if (ret) {
mlog_errno(ret);
goto out;
@@ -3162,22 +3162,22 @@
return ret;
}
-static int ocfs2_cow_sync_writeback(struct super_block *sb,
- struct ocfs2_cow_context *context,
- u32 cpos, u32 num_clusters)
+int ocfs2_cow_sync_writeback(struct super_block *sb,
+ struct inode *inode,
+ u32 cpos, u32 num_clusters)
{
int ret = 0;
loff_t offset, end, map_end;
pgoff_t page_index;
struct page *page;
- if (ocfs2_should_order_data(context->inode))
+ if (ocfs2_should_order_data(inode))
return 0;
offset = ((loff_t)cpos) << OCFS2_SB(sb)->s_clustersize_bits;
end = offset + (num_clusters << OCFS2_SB(sb)->s_clustersize_bits);
- ret = filemap_fdatawrite_range(context->inode->i_mapping,
+ ret = filemap_fdatawrite_range(inode->i_mapping,
offset, end - 1);
if (ret < 0) {
mlog_errno(ret);
@@ -3190,7 +3190,7 @@
if (map_end > end)
map_end = end;
- page = find_or_create_page(context->inode->i_mapping,
+ page = find_or_create_page(inode->i_mapping,
page_index, GFP_NOFS);
BUG_ON(!page);
@@ -3349,7 +3349,7 @@
* in write-back mode.
*/
if (context->get_clusters == ocfs2_di_get_clusters) {
- ret = ocfs2_cow_sync_writeback(sb, context, cpos,
+ ret = ocfs2_cow_sync_writeback(sb, context->inode, cpos,
orig_num_clusters);
if (ret)
mlog_errno(ret);
diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h
index c8ce46f7..7754608 100644
--- a/fs/ocfs2/refcounttree.h
+++ b/fs/ocfs2/refcounttree.h
@@ -84,6 +84,17 @@
struct buffer_head *ref_root_bh,
u32 cpos, u32 write_len,
struct ocfs2_post_refcount *post);
+int ocfs2_duplicate_clusters_by_page(handle_t *handle,
+ struct file *file,
+ u32 cpos, u32 old_cluster,
+ u32 new_cluster, u32 new_len);
+int ocfs2_duplicate_clusters_by_jbd(handle_t *handle,
+ struct file *file,
+ u32 cpos, u32 old_cluster,
+ u32 new_cluster, u32 new_len);
+int ocfs2_cow_sync_writeback(struct super_block *sb,
+ struct inode *inode,
+ u32 cpos, u32 num_clusters);
int ocfs2_add_refcount_flag(struct inode *inode,
struct ocfs2_extent_tree *data_et,
struct ocfs2_caching_info *ref_ci,
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 5a521c7..cdbaf5e 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -41,6 +41,7 @@
#include <linux/mount.h>
#include <linux/seq_file.h>
#include <linux/quotaops.h>
+#include <linux/cleancache.h>
#define CREATE_TRACE_POINTS
#include "ocfs2_trace.h"
@@ -1566,7 +1567,7 @@
if (osb->preferred_slot != OCFS2_INVALID_SLOT)
seq_printf(s, ",preferred_slot=%d", osb->preferred_slot);
- if (osb->s_atime_quantum != OCFS2_DEFAULT_ATIME_QUANTUM)
+ if (!(mnt->mnt_flags & MNT_NOATIME) && !(mnt->mnt_flags & MNT_RELATIME))
seq_printf(s, ",atime_quantum=%u", osb->s_atime_quantum);
if (osb->osb_commit_interval)
@@ -2352,6 +2353,7 @@
mlog_errno(status);
goto bail;
}
+ cleancache_init_shared_fs((char *)&uuid_net_key, sb);
bail:
return status;
diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c
index de4ff29..c368360c 100644
--- a/fs/omfs/dir.c
+++ b/fs/omfs/dir.c
@@ -240,8 +240,12 @@
struct inode *inode = dentry->d_inode;
int ret;
- if (S_ISDIR(inode->i_mode) && !omfs_dir_is_empty(inode))
- return -ENOTEMPTY;
+
+ if (S_ISDIR(inode->i_mode)) {
+ dentry_unhash(dentry);
+ if (!omfs_dir_is_empty(inode))
+ return -ENOTEMPTY;
+ }
ret = omfs_delete_entry(dentry);
if (ret)
@@ -378,6 +382,9 @@
int err;
if (new_inode) {
+ if (S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* overwriting existing file/dir */
err = omfs_remove(new_dir, new_dentry);
if (err)
diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c
index 19d6750..6296b40 100644
--- a/fs/partitions/efi.c
+++ b/fs/partitions/efi.c
@@ -310,6 +310,15 @@
goto fail;
}
+ /* Check the GUID Partition Table header size */
+ if (le32_to_cpu((*gpt)->header_size) >
+ bdev_logical_block_size(state->bdev)) {
+ pr_debug("GUID Partition Table Header size is wrong: %u > %u\n",
+ le32_to_cpu((*gpt)->header_size),
+ bdev_logical_block_size(state->bdev));
+ goto fail;
+ }
+
/* Check the GUID Partition Table CRC */
origcrc = le32_to_cpu((*gpt)->header_crc32);
(*gpt)->header_crc32 = 0;
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 5e4f776..9b45ee8 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -131,7 +131,7 @@
* you can test for combinations of others with
* simple bit tests.
*/
-static const char *task_state_array[] = {
+static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
@@ -147,7 +147,7 @@
static inline const char *get_task_state(struct task_struct *tsk)
{
unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state;
- const char **p = &task_state_array[0];
+ const char * const *p = &task_state_array[0];
BUILD_BUG_ON(1 + ilog2(TASK_STATE_MAX) != ARRAY_SIZE(task_state_array));
diff --git a/fs/proc/base.c b/fs/proc/base.c
index dc8bca7..4ede550 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -894,18 +894,18 @@
if (!task)
goto out_no_task;
- mm = check_mem_permission(task);
- copied = PTR_ERR(mm);
- if (IS_ERR(mm))
- goto out_task;
-
- copied = -EIO;
- if (file->private_data != (void *)((long)current->self_exec_id))
- goto out_mm;
-
copied = -ENOMEM;
page = (char *)__get_free_page(GFP_TEMPORARY);
if (!page)
+ goto out_task;
+
+ mm = check_mem_permission(task);
+ copied = PTR_ERR(mm);
+ if (IS_ERR(mm))
+ goto out_free;
+
+ copied = -EIO;
+ if (file->private_data != (void *)((long)current->self_exec_id))
goto out_mm;
copied = 0;
@@ -929,9 +929,11 @@
count -= retval;
}
*ppos = dst;
- free_page((unsigned long) page);
+
out_mm:
mmput(mm);
+out_free:
+ free_page((unsigned long) page);
out_task:
put_task_struct(task);
out_no_task:
@@ -1059,7 +1061,7 @@
{
struct task_struct *task;
char buffer[PROC_NUMBUF];
- long oom_adjust;
+ int oom_adjust;
unsigned long flags;
int err;
@@ -1071,7 +1073,7 @@
goto out;
}
- err = strict_strtol(strstrip(buffer), 0, &oom_adjust);
+ err = kstrtoint(strstrip(buffer), 0, &oom_adjust);
if (err)
goto out;
if ((oom_adjust < OOM_ADJUST_MIN || oom_adjust > OOM_ADJUST_MAX) &&
@@ -1168,7 +1170,7 @@
struct task_struct *task;
char buffer[PROC_NUMBUF];
unsigned long flags;
- long oom_score_adj;
+ int oom_score_adj;
int err;
memset(buffer, 0, sizeof(buffer));
@@ -1179,7 +1181,7 @@
goto out;
}
- err = strict_strtol(strstrip(buffer), 0, &oom_score_adj);
+ err = kstrtoint(strstrip(buffer), 0, &oom_score_adj);
if (err)
goto out;
if (oom_score_adj < OOM_SCORE_ADJ_MIN ||
@@ -1468,7 +1470,7 @@
struct inode *inode = file->f_path.dentry->d_inode;
struct task_struct *p;
char buffer[PROC_NUMBUF];
- long nice;
+ int nice;
int err;
memset(buffer, 0, sizeof(buffer));
@@ -1477,9 +1479,9 @@
if (copy_from_user(buffer, buf, count))
return -EFAULT;
- err = strict_strtol(strstrip(buffer), 0, &nice);
- if (err)
- return -EINVAL;
+ err = kstrtoint(strstrip(buffer), 0, &nice);
+ if (err < 0)
+ return err;
p = get_proc_task(inode);
if (!p)
@@ -1576,57 +1578,6 @@
.release = single_release,
};
-/*
- * We added or removed a vma mapping the executable. The vmas are only mapped
- * during exec and are not mapped with the mmap system call.
- * Callers must hold down_write() on the mm's mmap_sem for these
- */
-void added_exe_file_vma(struct mm_struct *mm)
-{
- mm->num_exe_file_vmas++;
-}
-
-void removed_exe_file_vma(struct mm_struct *mm)
-{
- mm->num_exe_file_vmas--;
- if ((mm->num_exe_file_vmas == 0) && mm->exe_file){
- fput(mm->exe_file);
- mm->exe_file = NULL;
- }
-
-}
-
-void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
-{
- if (new_exe_file)
- get_file(new_exe_file);
- if (mm->exe_file)
- fput(mm->exe_file);
- mm->exe_file = new_exe_file;
- mm->num_exe_file_vmas = 0;
-}
-
-struct file *get_mm_exe_file(struct mm_struct *mm)
-{
- struct file *exe_file;
-
- /* We need mmap_sem to protect against races with removal of
- * VM_EXECUTABLE vmas */
- down_read(&mm->mmap_sem);
- exe_file = mm->exe_file;
- if (exe_file)
- get_file(exe_file);
- up_read(&mm->mmap_sem);
- return exe_file;
-}
-
-void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm)
-{
- /* It's safe to write the exe_file pointer without exe_file_lock because
- * this is called during fork when the task is not yet in /proc */
- newmm->exe_file = get_mm_exe_file(oldmm);
-}
-
static int proc_exe_link(struct inode *inode, struct path *exe_path)
{
struct task_struct *task;
diff --git a/fs/proc/stat.c b/fs/proc/stat.c
index 1cffa2b..9758b65 100644
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -138,9 +138,9 @@
struct seq_file *m;
int res;
- /* don't ask for more than the kmalloc() max size, currently 128 KB */
- if (size > 128 * 1024)
- size = 128 * 1024;
+ /* don't ask for more than the kmalloc() max size */
+ if (size > KMALLOC_MAX_SIZE)
+ size = KMALLOC_MAX_SIZE;
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 2c9db29..25b6a88 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -211,7 +211,7 @@
{
struct mm_struct *mm = vma->vm_mm;
struct file *file = vma->vm_file;
- int flags = vma->vm_flags;
+ vm_flags_t flags = vma->vm_flags;
unsigned long ino = 0;
unsigned long long pgoff = 0;
unsigned long start, end;
@@ -536,15 +536,17 @@
char buffer[PROC_NUMBUF];
struct mm_struct *mm;
struct vm_area_struct *vma;
- long type;
+ int type;
+ int rv;
memset(buffer, 0, sizeof(buffer));
if (count > sizeof(buffer) - 1)
count = sizeof(buffer) - 1;
if (copy_from_user(buffer, buf, count))
return -EFAULT;
- if (strict_strtol(strstrip(buffer), 10, &type))
- return -EINVAL;
+ rv = kstrtoint(strstrip(buffer), 10, &type);
+ if (rv < 0)
+ return rv;
if (type < CLEAR_REFS_ALL || type > CLEAR_REFS_MAPPED)
return -EINVAL;
task = get_proc_task(file->f_path.dentry->d_inode);
@@ -769,18 +771,12 @@
if (!task)
goto out;
- mm = mm_for_maps(task);
- ret = PTR_ERR(mm);
- if (!mm || IS_ERR(mm))
- goto out_task;
-
ret = -EINVAL;
/* file position must be aligned */
if ((*ppos % PM_ENTRY_BYTES) || (count % PM_ENTRY_BYTES))
goto out_task;
ret = 0;
-
if (!count)
goto out_task;
@@ -788,7 +784,12 @@
pm.buffer = kmalloc(pm.len, GFP_TEMPORARY);
ret = -ENOMEM;
if (!pm.buffer)
- goto out_mm;
+ goto out_task;
+
+ mm = mm_for_maps(task);
+ ret = PTR_ERR(mm);
+ if (!mm || IS_ERR(mm))
+ goto out_free;
pagemap_walk.pmd_entry = pagemap_pte_range;
pagemap_walk.pte_hole = pagemap_pte_hole;
@@ -831,7 +832,7 @@
len = min(count, PM_ENTRY_BYTES * pm.pos);
if (copy_to_user(buf, pm.buffer, len)) {
ret = -EFAULT;
- goto out_free;
+ goto out_mm;
}
copied += len;
buf += len;
@@ -841,10 +842,10 @@
if (!ret || ret == PM_END_OF_BUFFER)
ret = copied;
-out_free:
- kfree(pm.buffer);
out_mm:
mmput(mm);
+out_free:
+ kfree(pm.buffer);
out_task:
put_task_struct(task);
out:
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 74802bc5..cd99bf5 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -35,6 +35,46 @@
static struct proc_dir_entry *proc_vmcore = NULL;
+/*
+ * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error
+ * The called function has to take care of module refcounting.
+ */
+static int (*oldmem_pfn_is_ram)(unsigned long pfn);
+
+int register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn))
+{
+ if (oldmem_pfn_is_ram)
+ return -EBUSY;
+ oldmem_pfn_is_ram = fn;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_oldmem_pfn_is_ram);
+
+void unregister_oldmem_pfn_is_ram(void)
+{
+ oldmem_pfn_is_ram = NULL;
+ wmb();
+}
+EXPORT_SYMBOL_GPL(unregister_oldmem_pfn_is_ram);
+
+static int pfn_is_ram(unsigned long pfn)
+{
+ int (*fn)(unsigned long pfn);
+ /* pfn is ram unless fn() checks pagetype */
+ int ret = 1;
+
+ /*
+ * Ask hypervisor if the pfn is really ram.
+ * A ballooned page contains no data and reading from such a page
+ * will cause high load in the hypervisor.
+ */
+ fn = oldmem_pfn_is_ram;
+ if (fn)
+ ret = fn(pfn);
+
+ return ret;
+}
+
/* Reads a page from the oldmem device from given offset. */
static ssize_t read_from_oldmem(char *buf, size_t count,
u64 *ppos, int userbuf)
@@ -55,9 +95,15 @@
else
nr_bytes = count;
- tmp = copy_oldmem_page(pfn, buf, nr_bytes, offset, userbuf);
- if (tmp < 0)
- return tmp;
+ /* If pfn is not ram, return zeros for sparse dump files */
+ if (pfn_is_ram(pfn) == 0)
+ memset(buf, 0, nr_bytes);
+ else {
+ tmp = copy_oldmem_page(pfn, buf, nr_bytes,
+ offset, userbuf);
+ if (tmp < 0)
+ return tmp;
+ }
*ppos += nr_bytes;
count -= nr_bytes;
buf += nr_bytes;
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 1186626..76c8164 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -831,6 +831,8 @@
INITIALIZE_PATH(path);
struct reiserfs_dir_entry de;
+ dentry_unhash(dentry);
+
/* we will be doing 2 balancings and update 2 stat data, we change quotas
* of the owner of the directory and of the owner of the parent directory.
* The quota structure is possibly deleted only on last iput => outside
@@ -1225,6 +1227,9 @@
unsigned long savelink = 1;
struct timespec ctime;
+ if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/* three balancings: (1) old name removal, (2) new name insertion
and (3) maybe "save" link insertion
stat data updates: (1) old directory,
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 47d2a44..50f1abc 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -105,7 +105,6 @@
mutex_unlock(&dentry->d_inode->i_mutex);
if (!error)
d_delete(dentry);
- dput(dentry);
return error;
}
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index 8ab48bc..ed0eb2a 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 4b5a3fb..f744be9 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -393,19 +393,36 @@
/*
* Read a filesystem table (uncompressed sequence of bytes) from disk
*/
-int squashfs_read_table(struct super_block *sb, void *buffer, u64 block,
- int length)
+void *squashfs_read_table(struct super_block *sb, u64 block, int length)
{
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
int i, res;
- void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
+ void *table, *buffer, **data;
+
+ table = buffer = kmalloc(length, GFP_KERNEL);
+ if (table == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
+ if (data == NULL) {
+ res = -ENOMEM;
+ goto failed;
+ }
for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
data[i] = buffer;
+
res = squashfs_read_data(sb, data, block, length |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages);
+
kfree(data);
- return res;
+
+ if (res < 0)
+ goto failed;
+
+ return table;
+
+failed:
+ kfree(table);
+ return ERR_PTR(res);
}
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index e921bd2..9f1b0bb 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
index 099745a..8ba70cf 100644
--- a/fs/squashfs/decompressor.h
+++ b/fs/squashfs/decompressor.h
@@ -4,7 +4,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c
index 3f79cd1..9dfe2ce 100644
--- a/fs/squashfs/dir.c
+++ b/fs/squashfs/dir.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/export.c b/fs/squashfs/export.c
index 7f93d5a..730c562 100644
--- a/fs/squashfs/export.c
+++ b/fs/squashfs/export.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -121,30 +121,38 @@
* Read uncompressed inode lookup table indexes off disk into memory
*/
__le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
- u64 lookup_table_start, unsigned int inodes)
+ u64 lookup_table_start, u64 next_table, unsigned int inodes)
{
unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
- __le64 *inode_lookup_table;
- int err;
+ __le64 *table;
TRACE("In read_inode_lookup_table, length %d\n", length);
- /* Allocate inode lookup table indexes */
- inode_lookup_table = kmalloc(length, GFP_KERNEL);
- if (inode_lookup_table == NULL) {
- ERROR("Failed to allocate inode lookup table\n");
- return ERR_PTR(-ENOMEM);
+ /* Sanity check values */
+
+ /* there should always be at least one inode */
+ if (inodes == 0)
+ return ERR_PTR(-EINVAL);
+
+ /* length bytes should not extend into the next table - this check
+ * also traps instances where lookup_table_start is incorrectly larger
+ * than the next table start
+ */
+ if (lookup_table_start + length > next_table)
+ return ERR_PTR(-EINVAL);
+
+ table = squashfs_read_table(sb, lookup_table_start, length);
+
+ /*
+ * table[0] points to the first inode lookup table metadata block,
+ * this should be less than lookup_table_start
+ */
+ if (!IS_ERR(table) && table[0] >= lookup_table_start) {
+ kfree(table);
+ return ERR_PTR(-EINVAL);
}
- err = squashfs_read_table(sb, inode_lookup_table, lookup_table_start,
- length);
- if (err < 0) {
- ERROR("unable to read inode lookup table\n");
- kfree(inode_lookup_table);
- return ERR_PTR(err);
- }
-
- return inode_lookup_table;
+ return table;
}
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index a25c506..38bb1c6 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c
index 7eef571..1516a649 100644
--- a/fs/squashfs/fragment.c
+++ b/fs/squashfs/fragment.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -71,26 +71,29 @@
* Read the uncompressed fragment lookup table indexes off disk into memory
*/
__le64 *squashfs_read_fragment_index_table(struct super_block *sb,
- u64 fragment_table_start, unsigned int fragments)
+ u64 fragment_table_start, u64 next_table, unsigned int fragments)
{
unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments);
- __le64 *fragment_index;
- int err;
+ __le64 *table;
- /* Allocate fragment lookup table indexes */
- fragment_index = kmalloc(length, GFP_KERNEL);
- if (fragment_index == NULL) {
- ERROR("Failed to allocate fragment index table\n");
- return ERR_PTR(-ENOMEM);
+ /*
+ * Sanity check, length bytes should not extend into the next table -
+ * this check also traps instances where fragment_table_start is
+ * incorrectly larger than the next table start
+ */
+ if (fragment_table_start + length > next_table)
+ return ERR_PTR(-EINVAL);
+
+ table = squashfs_read_table(sb, fragment_table_start, length);
+
+ /*
+ * table[0] points to the first fragment table metadata block, this
+ * should be less than fragment_table_start
+ */
+ if (!IS_ERR(table) && table[0] >= fragment_table_start) {
+ kfree(table);
+ return ERR_PTR(-EINVAL);
}
- err = squashfs_read_table(sb, fragment_index, fragment_table_start,
- length);
- if (err < 0) {
- ERROR("unable to read fragment index table\n");
- kfree(fragment_index);
- return ERR_PTR(err);
- }
-
- return fragment_index;
+ return table;
}
diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c
index d8f3245..a70858e 100644
--- a/fs/squashfs/id.c
+++ b/fs/squashfs/id.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -66,27 +66,37 @@
* Read uncompressed id lookup table indexes from disk into memory
*/
__le64 *squashfs_read_id_index_table(struct super_block *sb,
- u64 id_table_start, unsigned short no_ids)
+ u64 id_table_start, u64 next_table, unsigned short no_ids)
{
unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
- __le64 *id_table;
- int err;
+ __le64 *table;
TRACE("In read_id_index_table, length %d\n", length);
- /* Allocate id lookup table indexes */
- id_table = kmalloc(length, GFP_KERNEL);
- if (id_table == NULL) {
- ERROR("Failed to allocate id index table\n");
- return ERR_PTR(-ENOMEM);
+ /* Sanity check values */
+
+ /* there should always be at least one id */
+ if (no_ids == 0)
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * length bytes should not extend into the next table - this check
+ * also traps instances where id_table_start is incorrectly larger
+ * than the next table start
+ */
+ if (id_table_start + length > next_table)
+ return ERR_PTR(-EINVAL);
+
+ table = squashfs_read_table(sb, id_table_start, length);
+
+ /*
+ * table[0] points to the first id lookup table metadata block, this
+ * should be less than id_table_start
+ */
+ if (!IS_ERR(table) && table[0] >= id_table_start) {
+ kfree(table);
+ return ERR_PTR(-EINVAL);
}
- err = squashfs_read_table(sb, id_table, id_table_start, length);
- if (err < 0) {
- ERROR("unable to read id index table\n");
- kfree(id_table);
- return ERR_PTR(err);
- }
-
- return id_table;
+ return table;
}
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
index 62e63ad..04bebca 100644
--- a/fs/squashfs/inode.c
+++ b/fs/squashfs/inode.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c
index 5d922a6..4bc63ac 100644
--- a/fs/squashfs/namei.c
+++ b/fs/squashfs/namei.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 1f2e608..e3be6a7 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -44,24 +44,24 @@
u64, int);
extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *,
u64, int);
-extern int squashfs_read_table(struct super_block *, void *, u64, int);
+extern void *squashfs_read_table(struct super_block *, u64, int);
/* decompressor.c */
extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
/* export.c */
-extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
+extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
unsigned int);
/* fragment.c */
extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
- u64, unsigned int);
+ u64, u64, unsigned int);
/* id.c */
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
-extern __le64 *squashfs_read_id_index_table(struct super_block *, u64,
+extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64,
unsigned short);
/* inode.c */
diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
index 4582c56..b4a4e53 100644
--- a/fs/squashfs/squashfs_fs.h
+++ b/fs/squashfs/squashfs_fs.h
@@ -4,7 +4,7 @@
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/squashfs_fs_i.h b/fs/squashfs/squashfs_fs_i.h
index 359baef..73588e7 100644
--- a/fs/squashfs/squashfs_fs_i.h
+++ b/fs/squashfs/squashfs_fs_i.h
@@ -4,7 +4,7 @@
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index d9037a5..651f0b3 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -4,7 +4,7 @@
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 5c8184c..6f26abe 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -83,7 +83,7 @@
long long root_inode;
unsigned short flags;
unsigned int fragments;
- u64 lookup_table_start, xattr_id_table_start;
+ u64 lookup_table_start, xattr_id_table_start, next_table;
int err;
TRACE("Entered squashfs_fill_superblock\n");
@@ -95,12 +95,6 @@
}
msblk = sb->s_fs_info;
- sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
- if (sblk == NULL) {
- ERROR("Failed to allocate squashfs_super_block\n");
- goto failure;
- }
-
msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
msblk->devblksize_log2 = ffz(~msblk->devblksize);
@@ -114,10 +108,12 @@
* of bytes_used) we need to set it to an initial sensible dummy value
*/
msblk->bytes_used = sizeof(*sblk);
- err = squashfs_read_table(sb, sblk, SQUASHFS_START, sizeof(*sblk));
+ sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk));
- if (err < 0) {
+ if (IS_ERR(sblk)) {
ERROR("unable to read squashfs_super_block\n");
+ err = PTR_ERR(sblk);
+ sblk = NULL;
goto failed_mount;
}
@@ -218,18 +214,61 @@
goto failed_mount;
}
+ /* Handle xattrs */
+ sb->s_xattr = squashfs_xattr_handlers;
+ xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start);
+ if (xattr_id_table_start == SQUASHFS_INVALID_BLK) {
+ next_table = msblk->bytes_used;
+ goto allocate_id_index_table;
+ }
+
+ /* Allocate and read xattr id lookup table */
+ msblk->xattr_id_table = squashfs_read_xattr_id_table(sb,
+ xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids);
+ if (IS_ERR(msblk->xattr_id_table)) {
+ ERROR("unable to read xattr id index table\n");
+ err = PTR_ERR(msblk->xattr_id_table);
+ msblk->xattr_id_table = NULL;
+ if (err != -ENOTSUPP)
+ goto failed_mount;
+ }
+ next_table = msblk->xattr_table;
+
+allocate_id_index_table:
/* Allocate and read id index table */
msblk->id_table = squashfs_read_id_index_table(sb,
- le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids));
+ le64_to_cpu(sblk->id_table_start), next_table,
+ le16_to_cpu(sblk->no_ids));
if (IS_ERR(msblk->id_table)) {
+ ERROR("unable to read id index table\n");
err = PTR_ERR(msblk->id_table);
msblk->id_table = NULL;
goto failed_mount;
}
+ next_table = msblk->id_table[0];
+ /* Handle inode lookup table */
+ lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
+ if (lookup_table_start == SQUASHFS_INVALID_BLK)
+ goto handle_fragments;
+
+ /* Allocate and read inode lookup table */
+ msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
+ lookup_table_start, next_table, msblk->inodes);
+ if (IS_ERR(msblk->inode_lookup_table)) {
+ ERROR("unable to read inode lookup table\n");
+ err = PTR_ERR(msblk->inode_lookup_table);
+ msblk->inode_lookup_table = NULL;
+ goto failed_mount;
+ }
+ next_table = msblk->inode_lookup_table[0];
+
+ sb->s_export_op = &squashfs_export_ops;
+
+handle_fragments:
fragments = le32_to_cpu(sblk->fragments);
if (fragments == 0)
- goto allocate_lookup_table;
+ goto check_directory_table;
msblk->fragment_cache = squashfs_cache_init("fragment",
SQUASHFS_CACHED_FRAGMENTS, msblk->block_size);
@@ -240,45 +279,29 @@
/* Allocate and read fragment index table */
msblk->fragment_index = squashfs_read_fragment_index_table(sb,
- le64_to_cpu(sblk->fragment_table_start), fragments);
+ le64_to_cpu(sblk->fragment_table_start), next_table, fragments);
if (IS_ERR(msblk->fragment_index)) {
+ ERROR("unable to read fragment index table\n");
err = PTR_ERR(msblk->fragment_index);
msblk->fragment_index = NULL;
goto failed_mount;
}
+ next_table = msblk->fragment_index[0];
-allocate_lookup_table:
- lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
- if (lookup_table_start == SQUASHFS_INVALID_BLK)
- goto allocate_xattr_table;
-
- /* Allocate and read inode lookup table */
- msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
- lookup_table_start, msblk->inodes);
- if (IS_ERR(msblk->inode_lookup_table)) {
- err = PTR_ERR(msblk->inode_lookup_table);
- msblk->inode_lookup_table = NULL;
+check_directory_table:
+ /* Sanity check directory_table */
+ if (msblk->directory_table >= next_table) {
+ err = -EINVAL;
goto failed_mount;
}
- sb->s_export_op = &squashfs_export_ops;
-
-allocate_xattr_table:
- sb->s_xattr = squashfs_xattr_handlers;
- xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start);
- if (xattr_id_table_start == SQUASHFS_INVALID_BLK)
- goto allocate_root;
-
- /* Allocate and read xattr id lookup table */
- msblk->xattr_id_table = squashfs_read_xattr_id_table(sb,
- xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids);
- if (IS_ERR(msblk->xattr_id_table)) {
- err = PTR_ERR(msblk->xattr_id_table);
- msblk->xattr_id_table = NULL;
- if (err != -ENOTSUPP)
- goto failed_mount;
+ /* Sanity check inode_table */
+ if (msblk->inode_table >= msblk->directory_table) {
+ err = -EINVAL;
+ goto failed_mount;
}
-allocate_root:
+
+ /* allocate root */
root = new_inode(sb);
if (!root) {
err = -ENOMEM;
@@ -318,11 +341,6 @@
sb->s_fs_info = NULL;
kfree(sblk);
return err;
-
-failure:
- kfree(sb->s_fs_info);
- sb->s_fs_info = NULL;
- return -ENOMEM;
}
@@ -475,5 +493,5 @@
module_init(init_squashfs_fs);
module_exit(exit_squashfs_fs);
MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem");
-MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
+MODULE_AUTHOR("Phillip Lougher <phillip@squashfs.org.uk>");
MODULE_LICENSE("GPL");
diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c
index ec86434..1191817 100644
--- a/fs/squashfs/symlink.c
+++ b/fs/squashfs/symlink.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/xattr.c b/fs/squashfs/xattr.c
index 3876c36..92fcde7 100644
--- a/fs/squashfs/xattr.c
+++ b/fs/squashfs/xattr.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2010
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h
index b634efc..c83f5d9 100644
--- a/fs/squashfs/xattr.h
+++ b/fs/squashfs/xattr.h
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2010
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,6 +31,7 @@
u64 start, u64 *xattr_table_start, int *xattr_ids)
{
ERROR("Xattrs in filesystem, these will be ignored\n");
+ *xattr_table_start = start;
return ERR_PTR(-ENOTSUPP);
}
diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c
index 05385db..c89607d 100644
--- a/fs/squashfs/xattr_id.c
+++ b/fs/squashfs/xattr_id.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2010
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -67,34 +67,29 @@
u64 *xattr_table_start, int *xattr_ids)
{
unsigned int len;
- __le64 *xid_table;
- struct squashfs_xattr_id_table id_table;
- int err;
+ struct squashfs_xattr_id_table *id_table;
- err = squashfs_read_table(sb, &id_table, start, sizeof(id_table));
- if (err < 0) {
- ERROR("unable to read xattr id table\n");
- return ERR_PTR(err);
- }
- *xattr_table_start = le64_to_cpu(id_table.xattr_table_start);
- *xattr_ids = le32_to_cpu(id_table.xattr_ids);
+ id_table = squashfs_read_table(sb, start, sizeof(*id_table));
+ if (IS_ERR(id_table))
+ return (__le64 *) id_table;
+
+ *xattr_table_start = le64_to_cpu(id_table->xattr_table_start);
+ *xattr_ids = le32_to_cpu(id_table->xattr_ids);
+ kfree(id_table);
+
+ /* Sanity check values */
+
+ /* there is always at least one xattr id */
+ if (*xattr_ids == 0)
+ return ERR_PTR(-EINVAL);
+
+ /* xattr_table should be less than start */
+ if (*xattr_table_start >= start)
+ return ERR_PTR(-EINVAL);
+
len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids);
TRACE("In read_xattr_index_table, length %d\n", len);
- /* Allocate xattr id lookup table indexes */
- xid_table = kmalloc(len, GFP_KERNEL);
- if (xid_table == NULL) {
- ERROR("Failed to allocate xattr id index table\n");
- return ERR_PTR(-ENOMEM);
- }
-
- err = squashfs_read_table(sb, xid_table, start + sizeof(id_table), len);
- if (err < 0) {
- ERROR("unable to read xattr id index table\n");
- kfree(xid_table);
- return ERR_PTR(err);
- }
-
- return xid_table;
+ return squashfs_read_table(sb, start + sizeof(*id_table), len);
}
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
index aa47a28..1760b7d1 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 517688b3..55d918f 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -2,7 +2,7 @@
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
- * Phillip Lougher <phillip@lougher.demon.co.uk>
+ * Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/fs/super.c b/fs/super.c
index c04f7e0..c755939 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -31,6 +31,7 @@
#include <linux/mutex.h>
#include <linux/backing-dev.h>
#include <linux/rculist_bl.h>
+#include <linux/cleancache.h>
#include "internal.h"
@@ -112,6 +113,7 @@
s->s_maxbytes = MAX_NON_LFS;
s->s_op = &default_op;
s->s_time_gran = 1000000000;
+ s->cleancache_poolid = -1;
}
out:
return s;
@@ -177,6 +179,7 @@
{
struct file_system_type *fs = s->s_type;
if (atomic_dec_and_test(&s->s_active)) {
+ cleancache_flush_fs(s);
fs->kill_sb(s);
/*
* We need to call rcu_barrier so all the delayed rcu free
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index e474fbc..e2cc675 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -196,6 +196,8 @@
struct inode *inode = dentry->d_inode;
int err = -ENOTEMPTY;
+ dentry_unhash(dentry);
+
if (sysv_empty_dir(inode)) {
err = sysv_unlink(dir, dentry);
if (!err) {
@@ -222,6 +224,9 @@
struct sysv_dir_entry * old_de;
int err = -ENOENT;
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = sysv_find_entry(old_dentry, &old_page);
if (!old_de)
goto out;
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index ef5abd3..c2b8094 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -656,6 +656,8 @@
struct ubifs_inode *dir_ui = ubifs_inode(dir);
struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
+ dentry_unhash(dentry);
+
/*
* Budget request settings: deletion direntry, deletion inode and
* changing the parent inode. If budgeting fails, go ahead anyway
@@ -976,6 +978,9 @@
.dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
struct timespec time;
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
/*
* Budget request settings: deletion direntry, new direntry, removing
* the old inode, and changing old and new parent directory inodes.
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index f1dce84..4d76594 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -783,6 +783,8 @@
struct fileIdentDesc *fi, cfi;
struct kernel_lb_addr tloc;
+ dentry_unhash(dentry);
+
retval = -ENOENT;
fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
if (!fi)
@@ -1081,6 +1083,9 @@
struct kernel_lb_addr tloc;
struct udf_inode_info *old_iinfo = UDF_I(old_inode);
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
if (ofi) {
if (ofibh.sbh != ofibh.ebh)
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
index 46f7a80..42694e1 100644
--- a/fs/ufs/balloc.c
+++ b/fs/ufs/balloc.c
@@ -424,8 +424,7 @@
ufs_cpu_to_data_ptr(sb, p, result);
*err = 0;
UFS_I(inode)->i_lastfrag =
- max_t(u32, UFS_I(inode)->i_lastfrag,
- fragment + count);
+ max(UFS_I(inode)->i_lastfrag, fragment + count);
ufs_clear_frags(inode, result + oldcount,
newcount - oldcount, locked_page != NULL);
}
@@ -440,7 +439,8 @@
result = ufs_add_fragments (inode, tmp, oldcount, newcount, err);
if (result) {
*err = 0;
- UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
+ UFS_I(inode)->i_lastfrag = max(UFS_I(inode)->i_lastfrag,
+ fragment + count);
ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
locked_page != NULL);
unlock_super(sb);
@@ -479,7 +479,8 @@
uspi->s_sbbase + result, locked_page);
ufs_cpu_to_data_ptr(sb, p, result);
*err = 0;
- UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
+ UFS_I(inode)->i_lastfrag = max(UFS_I(inode)->i_lastfrag,
+ fragment + count);
unlock_super(sb);
if (newcount < request)
ufs_free_fragments (inode, result + newcount, request - newcount);
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index 29309e2..953ebdf 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -258,6 +258,8 @@
struct inode * inode = dentry->d_inode;
int err= -ENOTEMPTY;
+ dentry_unhash(dentry);
+
lock_ufs(dir->i_sb);
if (ufs_empty_dir (inode)) {
err = ufs_unlink(dir, dentry);
@@ -282,6 +284,9 @@
struct ufs_dir_entry *old_de;
int err = -ENOENT;
+ if (new_inode && S_ISDIR(new_inode->i_mode))
+ dentry_unhash(new_dentry);
+
old_de = ufs_find_entry(old_dir, &old_dentry->d_name, &old_page);
if (!old_de)
goto out;
diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
index 5f821db..f04f89f 100644
--- a/fs/ufs/truncate.c
+++ b/fs/ufs/truncate.c
@@ -84,7 +84,7 @@
retry = 0;
frag1 = DIRECT_FRAGMENT;
- frag4 = min_t(u32, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag);
+ frag4 = min_t(u64, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag);
frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1);
frag3 = frag4 & ~uspi->s_fpbmask;
block1 = block2 = 0;
diff --git a/fs/xfs/linux-2.6/xfs_discard.c b/fs/xfs/linux-2.6/xfs_discard.c
index d61611c..244e797 100644
--- a/fs/xfs/linux-2.6/xfs_discard.c
+++ b/fs/xfs/linux-2.6/xfs_discard.c
@@ -191,3 +191,32 @@
return -XFS_ERROR(EFAULT);
return 0;
}
+
+int
+xfs_discard_extents(
+ struct xfs_mount *mp,
+ struct list_head *list)
+{
+ struct xfs_busy_extent *busyp;
+ int error = 0;
+
+ list_for_each_entry(busyp, list, list) {
+ trace_xfs_discard_extent(mp, busyp->agno, busyp->bno,
+ busyp->length);
+
+ error = -blkdev_issue_discard(mp->m_ddev_targp->bt_bdev,
+ XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno),
+ XFS_FSB_TO_BB(mp, busyp->length),
+ GFP_NOFS, 0);
+ if (error && error != EOPNOTSUPP) {
+ xfs_info(mp,
+ "discard failed for extent [0x%llu,%u], error %d",
+ (unsigned long long)busyp->bno,
+ busyp->length,
+ error);
+ return error;
+ }
+ }
+
+ return 0;
+}
diff --git a/fs/xfs/linux-2.6/xfs_discard.h b/fs/xfs/linux-2.6/xfs_discard.h
index e82b6dd..344879a 100644
--- a/fs/xfs/linux-2.6/xfs_discard.h
+++ b/fs/xfs/linux-2.6/xfs_discard.h
@@ -2,7 +2,9 @@
#define XFS_DISCARD_H 1
struct fstrim_range;
+struct list_head;
extern int xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *);
+extern int xfs_discard_extents(struct xfs_mount *, struct list_head *);
#endif /* XFS_DISCARD_H */
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index b0aa59e..98b9c91 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -110,8 +110,10 @@
#define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */
#define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */
#define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */
-#define MNTOPT_DELAYLOG "delaylog" /* Delayed loging enabled */
-#define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed loging disabled */
+#define MNTOPT_DELAYLOG "delaylog" /* Delayed logging enabled */
+#define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed logging disabled */
+#define MNTOPT_DISCARD "discard" /* Discard unused blocks */
+#define MNTOPT_NODISCARD "nodiscard" /* Do not discard unused blocks */
/*
* Table driven mount option parser.
@@ -355,6 +357,10 @@
mp->m_flags |= XFS_MOUNT_DELAYLOG;
} else if (!strcmp(this_char, MNTOPT_NODELAYLOG)) {
mp->m_flags &= ~XFS_MOUNT_DELAYLOG;
+ } else if (!strcmp(this_char, MNTOPT_DISCARD)) {
+ mp->m_flags |= XFS_MOUNT_DISCARD;
+ } else if (!strcmp(this_char, MNTOPT_NODISCARD)) {
+ mp->m_flags &= ~XFS_MOUNT_DISCARD;
} else if (!strcmp(this_char, "ihashsize")) {
xfs_warn(mp,
"ihashsize no longer used, option is deprecated.");
@@ -388,6 +394,13 @@
return EINVAL;
}
+ if ((mp->m_flags & XFS_MOUNT_DISCARD) &&
+ !(mp->m_flags & XFS_MOUNT_DELAYLOG)) {
+ xfs_warn(mp,
+ "the discard option is incompatible with the nodelaylog option");
+ return EINVAL;
+ }
+
#ifndef CONFIG_XFS_QUOTA
if (XFS_IS_QUOTA_RUNNING(mp)) {
xfs_warn(mp, "quota support not available in this kernel.");
@@ -488,6 +501,7 @@
{ XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM },
{ XFS_MOUNT_GRPID, "," MNTOPT_GRPID },
{ XFS_MOUNT_DELAYLOG, "," MNTOPT_DELAYLOG },
+ { XFS_MOUNT_DISCARD, "," MNTOPT_DISCARD },
{ 0, NULL }
};
static struct proc_xfs_info xfs_info_unset[] = {
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h
index da0a561..6530769 100644
--- a/fs/xfs/xfs_ag.h
+++ b/fs/xfs/xfs_ag.h
@@ -187,6 +187,9 @@
xfs_agnumber_t agno;
xfs_agblock_t bno;
xfs_extlen_t length;
+ unsigned int flags;
+#define XFS_ALLOC_BUSY_DISCARDED 0x01 /* undergoing a discard op. */
+#define XFS_ALLOC_BUSY_SKIP_DISCARD 0x02 /* do not discard */
};
/*
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index acdced8..95862bb 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -2469,7 +2469,7 @@
error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno, len, 0);
if (!error)
- xfs_alloc_busy_insert(tp, args.agno, args.agbno, len);
+ xfs_alloc_busy_insert(tp, args.agno, args.agbno, len, 0);
error0:
xfs_perag_put(args.pag);
return error;
@@ -2480,7 +2480,8 @@
struct xfs_trans *tp,
xfs_agnumber_t agno,
xfs_agblock_t bno,
- xfs_extlen_t len)
+ xfs_extlen_t len,
+ unsigned int flags)
{
struct xfs_busy_extent *new;
struct xfs_busy_extent *busyp;
@@ -2504,6 +2505,7 @@
new->bno = bno;
new->length = len;
INIT_LIST_HEAD(&new->list);
+ new->flags = flags;
/* trace before insert to be able to see failed inserts */
trace_xfs_alloc_busy(tp->t_mountp, agno, bno, len);
@@ -2609,6 +2611,18 @@
xfs_agblock_t bend = bbno + busyp->length;
/*
+ * This extent is currently being discarded. Give the thread
+ * performing the discard a chance to mark the extent unbusy
+ * and retry.
+ */
+ if (busyp->flags & XFS_ALLOC_BUSY_DISCARDED) {
+ spin_unlock(&pag->pagb_lock);
+ delay(1);
+ spin_lock(&pag->pagb_lock);
+ return false;
+ }
+
+ /*
* If there is a busy extent overlapping a user allocation, we have
* no choice but to force the log and retry the search.
*
@@ -2813,7 +2827,8 @@
* If this is a metadata allocation, try to reuse the busy
* extent instead of trimming the allocation.
*/
- if (!args->userdata) {
+ if (!args->userdata &&
+ !(busyp->flags & XFS_ALLOC_BUSY_DISCARDED)) {
if (!xfs_alloc_busy_update_extent(args->mp, args->pag,
busyp, fbno, flen,
false))
@@ -2979,10 +2994,16 @@
kmem_free(busyp);
}
+/*
+ * Remove all extents on the passed in list from the busy extents tree.
+ * If do_discard is set skip extents that need to be discarded, and mark
+ * these as undergoing a discard operation instead.
+ */
void
xfs_alloc_busy_clear(
struct xfs_mount *mp,
- struct list_head *list)
+ struct list_head *list,
+ bool do_discard)
{
struct xfs_busy_extent *busyp, *n;
struct xfs_perag *pag = NULL;
@@ -2999,7 +3020,11 @@
agno = busyp->agno;
}
- xfs_alloc_busy_clear_one(mp, pag, busyp);
+ if (do_discard && busyp->length &&
+ !(busyp->flags & XFS_ALLOC_BUSY_SKIP_DISCARD))
+ busyp->flags = XFS_ALLOC_BUSY_DISCARDED;
+ else
+ xfs_alloc_busy_clear_one(mp, pag, busyp);
}
if (pag) {
diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h
index 240ad28..2f52b92 100644
--- a/fs/xfs/xfs_alloc.h
+++ b/fs/xfs/xfs_alloc.h
@@ -137,10 +137,11 @@
#ifdef __KERNEL__
void
xfs_alloc_busy_insert(struct xfs_trans *tp, xfs_agnumber_t agno,
- xfs_agblock_t bno, xfs_extlen_t len);
+ xfs_agblock_t bno, xfs_extlen_t len, unsigned int flags);
void
-xfs_alloc_busy_clear(struct xfs_mount *mp, struct list_head *list);
+xfs_alloc_busy_clear(struct xfs_mount *mp, struct list_head *list,
+ bool do_discard);
int
xfs_alloc_busy_search(struct xfs_mount *mp, xfs_agnumber_t agno,
diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c
index 8b469d5..2b35188 100644
--- a/fs/xfs/xfs_alloc_btree.c
+++ b/fs/xfs/xfs_alloc_btree.c
@@ -120,7 +120,8 @@
if (error)
return error;
- xfs_alloc_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1);
+ xfs_alloc_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1,
+ XFS_ALLOC_BUSY_SKIP_DISCARD);
xfs_trans_agbtree_delta(cur->bc_tp, -1);
return 0;
}
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index fa00788..e546a33 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -89,36 +89,19 @@
int *flags); /* inode logging flags */
/*
- * Called by xfs_bmapi to update file extent records and the btree
- * after allocating space (or doing a delayed allocation).
- */
-STATIC int /* error */
-xfs_bmap_add_extent(
- xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
- xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
- xfs_bmbt_irec_t *new, /* new data to add to file extents */
- xfs_fsblock_t *first, /* pointer to firstblock variable */
- xfs_bmap_free_t *flist, /* list of extents to be freed */
- int *logflagsp, /* inode logging flags */
- int whichfork, /* data or attr fork */
- int rsvd); /* OK to allocate reserved blocks */
-
-/*
* Called by xfs_bmap_add_extent to handle cases converting a delayed
* allocation to a real allocation.
*/
STATIC int /* error */
xfs_bmap_add_extent_delay_real(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
- int *logflagsp, /* inode logging flags */
- int rsvd); /* OK to allocate reserved blocks */
+ int *logflagsp); /* inode logging flags */
/*
* Called by xfs_bmap_add_extent to handle cases converting a hole
@@ -127,10 +110,9 @@
STATIC int /* error */
xfs_bmap_add_extent_hole_delay(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
- int *logflagsp,/* inode logging flags */
- int rsvd); /* OK to allocate reserved blocks */
+ int *logflagsp); /* inode logging flags */
/*
* Called by xfs_bmap_add_extent to handle cases converting a hole
@@ -139,7 +121,7 @@
STATIC int /* error */
xfs_bmap_add_extent_hole_real(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
@@ -152,7 +134,7 @@
STATIC int /* error */
xfs_bmap_add_extent_unwritten_real(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp); /* inode logging flags */
@@ -180,22 +162,6 @@
int whichfork); /* data or attr fork */
/*
- * Called by xfs_bmapi to update file extent records and the btree
- * after removing space (or undoing a delayed allocation).
- */
-STATIC int /* error */
-xfs_bmap_del_extent(
- xfs_inode_t *ip, /* incore inode pointer */
- xfs_trans_t *tp, /* current trans pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
- xfs_bmap_free_t *flist, /* list of extents to be freed */
- xfs_btree_cur_t *cur, /* if null, not a btree */
- xfs_bmbt_irec_t *new, /* new data to add to file extents */
- int *logflagsp,/* inode logging flags */
- int whichfork, /* data or attr fork */
- int rsvd); /* OK to allocate reserved blocks */
-
-/*
* Remove the entry "free" from the free item list. Prev points to the
* previous entry, unless "free" is the head of the list.
*/
@@ -474,14 +440,13 @@
STATIC int /* error */
xfs_bmap_add_extent(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
int *logflagsp, /* inode logging flags */
- int whichfork, /* data or attr fork */
- int rsvd) /* OK to use reserved data blocks */
+ int whichfork) /* data or attr fork */
{
xfs_btree_cur_t *cur; /* btree cursor or null */
xfs_filblks_t da_new; /* new count del alloc blocks used */
@@ -492,23 +457,27 @@
xfs_extnum_t nextents; /* number of extents in file now */
XFS_STATS_INC(xs_add_exlist);
+
cur = *curp;
ifp = XFS_IFORK_PTR(ip, whichfork);
nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
- ASSERT(idx <= nextents);
da_old = da_new = 0;
error = 0;
+
+ ASSERT(*idx >= 0);
+ ASSERT(*idx <= nextents);
+
/*
* This is the first extent added to a new/empty file.
* Special case this one, so other routines get to assume there are
* already extents in the list.
*/
if (nextents == 0) {
- xfs_iext_insert(ip, 0, 1, new,
+ xfs_iext_insert(ip, *idx, 1, new,
whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
ASSERT(cur == NULL);
- ifp->if_lastex = 0;
+
if (!isnullstartblock(new->br_startblock)) {
XFS_IFORK_NEXT_SET(ip, whichfork, 1);
logflags = XFS_ILOG_CORE | xfs_ilog_fext(whichfork);
@@ -522,27 +491,25 @@
if (cur)
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
- if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, new,
- &logflags, rsvd)))
- goto done;
+ error = xfs_bmap_add_extent_hole_delay(ip, idx, new,
+ &logflags);
}
/*
* Real allocation off the end of the file.
*/
- else if (idx == nextents) {
+ else if (*idx == nextents) {
if (cur)
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
- if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new,
- &logflags, whichfork)))
- goto done;
+ error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new,
+ &logflags, whichfork);
} else {
xfs_bmbt_irec_t prev; /* old extent at offset idx */
/*
* Get the record referred to by idx.
*/
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &prev);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &prev);
/*
* If it's a real allocation record, and the new allocation ends
* after the start of the referred to record, then we're filling
@@ -557,22 +524,18 @@
if (cur)
ASSERT(cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL);
- if ((error = xfs_bmap_add_extent_delay_real(ip,
- idx, &cur, new, &da_new, first, flist,
- &logflags, rsvd)))
- goto done;
- } else if (new->br_state == XFS_EXT_NORM) {
- ASSERT(new->br_state == XFS_EXT_NORM);
- if ((error = xfs_bmap_add_extent_unwritten_real(
- ip, idx, &cur, new, &logflags)))
- goto done;
+ error = xfs_bmap_add_extent_delay_real(ip,
+ idx, &cur, new, &da_new,
+ first, flist, &logflags);
} else {
- ASSERT(new->br_state == XFS_EXT_UNWRITTEN);
- if ((error = xfs_bmap_add_extent_unwritten_real(
- ip, idx, &cur, new, &logflags)))
+ ASSERT(new->br_state == XFS_EXT_NORM ||
+ new->br_state == XFS_EXT_UNWRITTEN);
+
+ error = xfs_bmap_add_extent_unwritten_real(ip,
+ idx, &cur, new, &logflags);
+ if (error)
goto done;
}
- ASSERT(*curp == cur || *curp == NULL);
}
/*
* Otherwise we're filling in a hole with an allocation.
@@ -581,13 +544,15 @@
if (cur)
ASSERT((cur->bc_private.b.flags &
XFS_BTCUR_BPRV_WASDEL) == 0);
- if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur,
- new, &logflags, whichfork)))
- goto done;
+ error = xfs_bmap_add_extent_hole_real(ip, idx, cur,
+ new, &logflags, whichfork);
}
}
+ if (error)
+ goto done;
ASSERT(*curp == cur || *curp == NULL);
+
/*
* Convert to a btree if necessary.
*/
@@ -615,7 +580,7 @@
ASSERT(nblks <= da_old);
if (nblks < da_old)
xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS,
- (int64_t)(da_old - nblks), rsvd);
+ (int64_t)(da_old - nblks), 0);
}
/*
* Clear out the allocated field, done with it now in any case.
@@ -640,14 +605,13 @@
STATIC int /* error */
xfs_bmap_add_extent_delay_real(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */
xfs_fsblock_t *first, /* pointer to firstblock variable */
xfs_bmap_free_t *flist, /* list of extents to be freed */
- int *logflagsp, /* inode logging flags */
- int rsvd) /* OK to use reserved data block allocation */
+ int *logflagsp) /* inode logging flags */
{
xfs_btree_cur_t *cur; /* btree cursor */
int diff; /* temp value */
@@ -673,7 +637,7 @@
*/
cur = *curp;
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
- ep = xfs_iext_get_ext(ifp, idx);
+ ep = xfs_iext_get_ext(ifp, *idx);
xfs_bmbt_get_all(ep, &PREV);
new_endoff = new->br_startoff + new->br_blockcount;
ASSERT(PREV.br_startoff <= new->br_startoff);
@@ -692,9 +656,9 @@
* Check and set flags if this segment has a left neighbor.
* Don't set contiguous if the combined extent would be too large.
*/
- if (idx > 0) {
+ if (*idx > 0) {
state |= BMAP_LEFT_VALID;
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &LEFT);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &LEFT);
if (isnullstartblock(LEFT.br_startblock))
state |= BMAP_LEFT_DELAY;
@@ -712,9 +676,9 @@
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
- if (idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
+ if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
state |= BMAP_RIGHT_VALID;
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx + 1), &RIGHT);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx + 1), &RIGHT);
if (isnullstartblock(RIGHT.br_startblock))
state |= BMAP_RIGHT_DELAY;
@@ -745,14 +709,14 @@
* Filling in all of a previously delayed allocation extent.
* The left and right neighbors are both contiguous with new.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
+ --*idx;
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
LEFT.br_blockcount + PREV.br_blockcount +
RIGHT.br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- xfs_iext_remove(ip, idx, 2, state);
- ip->i_df.if_lastex = idx - 1;
+ xfs_iext_remove(ip, *idx + 1, 2, state);
ip->i_d.di_nextents--;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -784,13 +748,14 @@
* Filling in all of a previously delayed allocation extent.
* The left neighbor is contiguous, the right is not.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
- LEFT.br_blockcount + PREV.br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ --*idx;
- ip->i_df.if_lastex = idx - 1;
- xfs_iext_remove(ip, idx, 1, state);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
+ LEFT.br_blockcount + PREV.br_blockcount);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+
+ xfs_iext_remove(ip, *idx + 1, 1, state);
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -814,14 +779,13 @@
* Filling in all of a previously delayed allocation extent.
* The right neighbor is contiguous, the left is not.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_startblock(ep, new->br_startblock);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount + RIGHT.br_blockcount);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ip->i_df.if_lastex = idx;
- xfs_iext_remove(ip, idx + 1, 1, state);
+ xfs_iext_remove(ip, *idx + 1, 1, state);
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -837,6 +801,7 @@
RIGHT.br_blockcount, PREV.br_state)))
goto done;
}
+
*dnew = 0;
break;
@@ -846,11 +811,10 @@
* Neither the left nor right neighbors are contiguous with
* the new one.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_startblock(ep, new->br_startblock);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ip->i_df.if_lastex = idx;
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -866,6 +830,7 @@
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
+
*dnew = 0;
break;
@@ -874,17 +839,16 @@
* Filling in the first part of a previous delayed allocation.
* The left neighbor is contiguous.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
+ trace_xfs_bmap_pre_update(ip, *idx - 1, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx - 1),
LEFT.br_blockcount + new->br_blockcount);
xfs_bmbt_set_startoff(ep,
PREV.br_startoff + new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx - 1, state, _THIS_IP_);
temp = PREV.br_blockcount - new->br_blockcount;
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
- ip->i_df.if_lastex = idx - 1;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -904,7 +868,9 @@
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
startblockval(PREV.br_startblock));
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+
+ --*idx;
*dnew = temp;
break;
@@ -913,12 +879,11 @@
* Filling in the first part of a previous delayed allocation.
* The left neighbor is not contiguous.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_startoff(ep, new_endoff);
temp = PREV.br_blockcount - new->br_blockcount;
xfs_bmbt_set_blockcount(ep, temp);
- xfs_iext_insert(ip, idx, 1, new, state);
- ip->i_df.if_lastex = idx;
+ xfs_iext_insert(ip, *idx, 1, new, state);
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -946,9 +911,10 @@
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
startblockval(PREV.br_startblock) -
(cur ? cur->bc_private.b.allocated : 0));
- ep = xfs_iext_get_ext(ifp, idx + 1);
+ ep = xfs_iext_get_ext(ifp, *idx + 1);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx + 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx + 1, state, _THIS_IP_);
+
*dnew = temp;
break;
@@ -958,15 +924,13 @@
* The right neighbor is contiguous with the new allocation.
*/
temp = PREV.br_blockcount - new->br_blockcount;
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
- trace_xfs_bmap_pre_update(ip, idx + 1, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx + 1, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
- xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1),
+ xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx + 1),
new->br_startoff, new->br_startblock,
new->br_blockcount + RIGHT.br_blockcount,
RIGHT.br_state);
- trace_xfs_bmap_post_update(ip, idx + 1, state, _THIS_IP_);
- ip->i_df.if_lastex = idx + 1;
+ trace_xfs_bmap_post_update(ip, *idx + 1, state, _THIS_IP_);
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -983,10 +947,14 @@
RIGHT.br_state)))
goto done;
}
+
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
startblockval(PREV.br_startblock));
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+
+ ++*idx;
*dnew = temp;
break;
@@ -996,10 +964,9 @@
* The right neighbor is not contiguous.
*/
temp = PREV.br_blockcount - new->br_blockcount;
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
- xfs_iext_insert(ip, idx + 1, 1, new, state);
- ip->i_df.if_lastex = idx + 1;
+ xfs_iext_insert(ip, *idx + 1, 1, new, state);
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1027,9 +994,11 @@
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
startblockval(PREV.br_startblock) -
(cur ? cur->bc_private.b.allocated : 0));
- ep = xfs_iext_get_ext(ifp, idx);
+ ep = xfs_iext_get_ext(ifp, *idx);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+
+ ++*idx;
*dnew = temp;
break;
@@ -1056,7 +1025,7 @@
*/
temp = new->br_startoff - PREV.br_startoff;
temp2 = PREV.br_startoff + PREV.br_blockcount - new_endoff;
- trace_xfs_bmap_pre_update(ip, idx, 0, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, 0, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp); /* truncate PREV */
LEFT = *new;
RIGHT.br_state = PREV.br_state;
@@ -1065,8 +1034,7 @@
RIGHT.br_startoff = new_endoff;
RIGHT.br_blockcount = temp2;
/* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
- xfs_iext_insert(ip, idx + 1, 2, &LEFT, state);
- ip->i_df.if_lastex = idx + 1;
+ xfs_iext_insert(ip, *idx + 1, 2, &LEFT, state);
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1097,7 +1065,7 @@
(cur ? cur->bc_private.b.allocated : 0));
if (diff > 0 &&
xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS,
- -((int64_t)diff), rsvd)) {
+ -((int64_t)diff), 0)) {
/*
* Ick gross gag me with a spoon.
*/
@@ -1109,7 +1077,7 @@
if (!diff ||
!xfs_icsb_modify_counters(ip->i_mount,
XFS_SBS_FDBLOCKS,
- -((int64_t)diff), rsvd))
+ -((int64_t)diff), 0))
break;
}
if (temp2) {
@@ -1118,18 +1086,20 @@
if (!diff ||
!xfs_icsb_modify_counters(ip->i_mount,
XFS_SBS_FDBLOCKS,
- -((int64_t)diff), rsvd))
+ -((int64_t)diff), 0))
break;
}
}
}
- ep = xfs_iext_get_ext(ifp, idx);
+ ep = xfs_iext_get_ext(ifp, *idx);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
- trace_xfs_bmap_pre_update(ip, idx + 2, state, _THIS_IP_);
- xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx + 2),
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx + 2, state, _THIS_IP_);
+ xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx + 2),
nullstartblock((int)temp2));
- trace_xfs_bmap_post_update(ip, idx + 2, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx + 2, state, _THIS_IP_);
+
+ ++*idx;
*dnew = temp + temp2;
break;
@@ -1161,7 +1131,7 @@
STATIC int /* error */
xfs_bmap_add_extent_unwritten_real(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp) /* inode logging flags */
@@ -1188,7 +1158,7 @@
error = 0;
cur = *curp;
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
- ep = xfs_iext_get_ext(ifp, idx);
+ ep = xfs_iext_get_ext(ifp, *idx);
xfs_bmbt_get_all(ep, &PREV);
newext = new->br_state;
oldext = (newext == XFS_EXT_UNWRITTEN) ?
@@ -1211,9 +1181,9 @@
* Check and set flags if this segment has a left neighbor.
* Don't set contiguous if the combined extent would be too large.
*/
- if (idx > 0) {
+ if (*idx > 0) {
state |= BMAP_LEFT_VALID;
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &LEFT);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &LEFT);
if (isnullstartblock(LEFT.br_startblock))
state |= BMAP_LEFT_DELAY;
@@ -1231,9 +1201,9 @@
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
- if (idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
+ if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
state |= BMAP_RIGHT_VALID;
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx + 1), &RIGHT);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx + 1), &RIGHT);
if (isnullstartblock(RIGHT.br_startblock))
state |= BMAP_RIGHT_DELAY;
}
@@ -1262,14 +1232,15 @@
* Setting all of a previous oldext extent to newext.
* The left and right neighbors are both contiguous with new.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
+ --*idx;
+
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
LEFT.br_blockcount + PREV.br_blockcount +
RIGHT.br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- xfs_iext_remove(ip, idx, 2, state);
- ip->i_df.if_lastex = idx - 1;
+ xfs_iext_remove(ip, *idx + 1, 2, state);
ip->i_d.di_nextents -= 2;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1305,13 +1276,14 @@
* Setting all of a previous oldext extent to newext.
* The left neighbor is contiguous, the right is not.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
- LEFT.br_blockcount + PREV.br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ --*idx;
- ip->i_df.if_lastex = idx - 1;
- xfs_iext_remove(ip, idx, 1, state);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
+ LEFT.br_blockcount + PREV.br_blockcount);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+
+ xfs_iext_remove(ip, *idx + 1, 1, state);
ip->i_d.di_nextents--;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1341,13 +1313,12 @@
* Setting all of a previous oldext extent to newext.
* The right neighbor is contiguous, the left is not.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount + RIGHT.br_blockcount);
xfs_bmbt_set_state(ep, newext);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
- ip->i_df.if_lastex = idx;
- xfs_iext_remove(ip, idx + 1, 1, state);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+ xfs_iext_remove(ip, *idx + 1, 1, state);
ip->i_d.di_nextents--;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1378,11 +1349,10 @@
* Neither the left nor right neighbors are contiguous with
* the new one.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_state(ep, newext);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ip->i_df.if_lastex = idx;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -1404,21 +1374,22 @@
* Setting the first part of a previous oldext extent to newext.
* The left neighbor is contiguous.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
+ trace_xfs_bmap_pre_update(ip, *idx - 1, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx - 1),
LEFT.br_blockcount + new->br_blockcount);
xfs_bmbt_set_startoff(ep,
PREV.br_startoff + new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx - 1, state, _THIS_IP_);
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_startblock(ep,
new->br_startblock + new->br_blockcount);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ip->i_df.if_lastex = idx - 1;
+ --*idx;
+
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -1449,17 +1420,16 @@
* Setting the first part of a previous oldext extent to newext.
* The left neighbor is not contiguous.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
ASSERT(ep && xfs_bmbt_get_state(ep) == oldext);
xfs_bmbt_set_startoff(ep, new_endoff);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
xfs_bmbt_set_startblock(ep,
new->br_startblock + new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- xfs_iext_insert(ip, idx, 1, new, state);
- ip->i_df.if_lastex = idx;
+ xfs_iext_insert(ip, *idx, 1, new, state);
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1488,17 +1458,19 @@
* Setting the last part of a previous oldext extent to newext.
* The right neighbor is contiguous with the new allocation.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
- trace_xfs_bmap_pre_update(ip, idx + 1, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
- xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1),
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+
+ ++*idx;
+
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
new->br_startoff, new->br_startblock,
new->br_blockcount + RIGHT.br_blockcount, newext);
- trace_xfs_bmap_post_update(ip, idx + 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ip->i_df.if_lastex = idx + 1;
if (cur == NULL)
rval = XFS_ILOG_DEXT;
else {
@@ -1528,13 +1500,14 @@
* Setting the last part of a previous oldext extent to newext.
* The right neighbor is not contiguous.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep,
PREV.br_blockcount - new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- xfs_iext_insert(ip, idx + 1, 1, new, state);
- ip->i_df.if_lastex = idx + 1;
+ ++*idx;
+ xfs_iext_insert(ip, *idx, 1, new, state);
+
ip->i_d.di_nextents++;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1568,10 +1541,10 @@
* newext. Contiguity is impossible here.
* One extent becomes three extents.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep,
new->br_startoff - PREV.br_startoff);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
r[0] = *new;
r[1].br_startoff = new_endoff;
@@ -1579,8 +1552,10 @@
PREV.br_startoff + PREV.br_blockcount - new_endoff;
r[1].br_startblock = new->br_startblock + new->br_blockcount;
r[1].br_state = oldext;
- xfs_iext_insert(ip, idx + 1, 2, &r[0], state);
- ip->i_df.if_lastex = idx + 1;
+
+ ++*idx;
+ xfs_iext_insert(ip, *idx, 2, &r[0], state);
+
ip->i_d.di_nextents += 2;
if (cur == NULL)
rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1650,12 +1625,10 @@
STATIC int /* error */
xfs_bmap_add_extent_hole_delay(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
- int *logflagsp, /* inode logging flags */
- int rsvd) /* OK to allocate reserved blocks */
+ int *logflagsp) /* inode logging flags */
{
- xfs_bmbt_rec_host_t *ep; /* extent record for idx */
xfs_ifork_t *ifp; /* inode fork pointer */
xfs_bmbt_irec_t left; /* left neighbor extent entry */
xfs_filblks_t newlen=0; /* new indirect size */
@@ -1665,16 +1638,15 @@
xfs_filblks_t temp=0; /* temp for indirect calculations */
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
- ep = xfs_iext_get_ext(ifp, idx);
state = 0;
ASSERT(isnullstartblock(new->br_startblock));
/*
* Check and set flags if this segment has a left neighbor
*/
- if (idx > 0) {
+ if (*idx > 0) {
state |= BMAP_LEFT_VALID;
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &left);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &left);
if (isnullstartblock(left.br_startblock))
state |= BMAP_LEFT_DELAY;
@@ -1684,9 +1656,9 @@
* Check and set flags if the current (right) segment exists.
* If it doesn't exist, we're converting the hole at end-of-file.
*/
- if (idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+ if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
state |= BMAP_RIGHT_VALID;
- xfs_bmbt_get_all(ep, &right);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
if (isnullstartblock(right.br_startblock))
state |= BMAP_RIGHT_DELAY;
@@ -1719,21 +1691,21 @@
* on the left and on the right.
* Merge all three into a single extent record.
*/
+ --*idx;
temp = left.br_blockcount + new->br_blockcount +
right.br_blockcount;
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
oldlen = startblockval(left.br_startblock) +
startblockval(new->br_startblock) +
startblockval(right.br_startblock);
newlen = xfs_bmap_worst_indlen(ip, temp);
- xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1),
+ xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
nullstartblock((int)newlen));
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- xfs_iext_remove(ip, idx, 1, state);
- ip->i_df.if_lastex = idx - 1;
+ xfs_iext_remove(ip, *idx + 1, 1, state);
break;
case BMAP_LEFT_CONTIG:
@@ -1742,17 +1714,17 @@
* on the left.
* Merge the new allocation with the left neighbor.
*/
+ --*idx;
temp = left.br_blockcount + new->br_blockcount;
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp);
+
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
oldlen = startblockval(left.br_startblock) +
startblockval(new->br_startblock);
newlen = xfs_bmap_worst_indlen(ip, temp);
- xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1),
+ xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
nullstartblock((int)newlen));
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
-
- ip->i_df.if_lastex = idx - 1;
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
break;
case BMAP_RIGHT_CONTIG:
@@ -1761,16 +1733,15 @@
* on the right.
* Merge the new allocation with the right neighbor.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
temp = new->br_blockcount + right.br_blockcount;
oldlen = startblockval(new->br_startblock) +
startblockval(right.br_startblock);
newlen = xfs_bmap_worst_indlen(ip, temp);
- xfs_bmbt_set_allf(ep, new->br_startoff,
+ xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
+ new->br_startoff,
nullstartblock((int)newlen), temp, right.br_state);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
-
- ip->i_df.if_lastex = idx;
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
break;
case 0:
@@ -1780,14 +1751,13 @@
* Insert a new entry.
*/
oldlen = newlen = 0;
- xfs_iext_insert(ip, idx, 1, new, state);
- ip->i_df.if_lastex = idx;
+ xfs_iext_insert(ip, *idx, 1, new, state);
break;
}
if (oldlen != newlen) {
ASSERT(oldlen > newlen);
xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS,
- (int64_t)(oldlen - newlen), rsvd);
+ (int64_t)(oldlen - newlen), 0);
/*
* Nothing to do for disk quota accounting here.
*/
@@ -1803,13 +1773,12 @@
STATIC int /* error */
xfs_bmap_add_extent_hole_real(
xfs_inode_t *ip, /* incore inode pointer */
- xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_extnum_t *idx, /* extent number to update/insert */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *new, /* new data to add to file extents */
int *logflagsp, /* inode logging flags */
int whichfork) /* data or attr fork */
{
- xfs_bmbt_rec_host_t *ep; /* pointer to extent entry ins. point */
int error; /* error return value */
int i; /* temp state */
xfs_ifork_t *ifp; /* inode fork pointer */
@@ -1819,8 +1788,7 @@
int state; /* state bits, accessed thru macros */
ifp = XFS_IFORK_PTR(ip, whichfork);
- ASSERT(idx <= ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t));
- ep = xfs_iext_get_ext(ifp, idx);
+ ASSERT(*idx <= ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t));
state = 0;
if (whichfork == XFS_ATTR_FORK)
@@ -1829,9 +1797,9 @@
/*
* Check and set flags if this segment has a left neighbor.
*/
- if (idx > 0) {
+ if (*idx > 0) {
state |= BMAP_LEFT_VALID;
- xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &left);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &left);
if (isnullstartblock(left.br_startblock))
state |= BMAP_LEFT_DELAY;
}
@@ -1840,9 +1808,9 @@
* Check and set flags if this segment has a current value.
* Not true if we're inserting into the "hole" at eof.
*/
- if (idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+ if (*idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
state |= BMAP_RIGHT_VALID;
- xfs_bmbt_get_all(ep, &right);
+ xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
if (isnullstartblock(right.br_startblock))
state |= BMAP_RIGHT_DELAY;
}
@@ -1879,14 +1847,15 @@
* left and on the right.
* Merge all three into a single extent record.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
+ --*idx;
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
left.br_blockcount + new->br_blockcount +
right.br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- xfs_iext_remove(ip, idx, 1, state);
- ifp->if_lastex = idx - 1;
+ xfs_iext_remove(ip, *idx + 1, 1, state);
+
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
if (cur == NULL) {
@@ -1921,12 +1890,12 @@
* on the left.
* Merge the new allocation with the left neighbor.
*/
- trace_xfs_bmap_pre_update(ip, idx - 1, state, _THIS_IP_);
- xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1),
+ --*idx;
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
left.br_blockcount + new->br_blockcount);
- trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ifp->if_lastex = idx - 1;
if (cur == NULL) {
rval = xfs_ilog_fext(whichfork);
} else {
@@ -1952,13 +1921,13 @@
* on the right.
* Merge the new allocation with the right neighbor.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
- xfs_bmbt_set_allf(ep, new->br_startoff, new->br_startblock,
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+ xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
+ new->br_startoff, new->br_startblock,
new->br_blockcount + right.br_blockcount,
right.br_state);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
- ifp->if_lastex = idx;
if (cur == NULL) {
rval = xfs_ilog_fext(whichfork);
} else {
@@ -1984,8 +1953,7 @@
* real allocation.
* Insert a new entry.
*/
- xfs_iext_insert(ip, idx, 1, new, state);
- ifp->if_lastex = idx;
+ xfs_iext_insert(ip, *idx, 1, new, state);
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
if (cur == NULL) {
@@ -2833,13 +2801,12 @@
xfs_bmap_del_extent(
xfs_inode_t *ip, /* incore inode pointer */
xfs_trans_t *tp, /* current transaction pointer */
- xfs_extnum_t idx, /* extent number to update/delete */
+ xfs_extnum_t *idx, /* extent number to update/delete */
xfs_bmap_free_t *flist, /* list of extents to be freed */
xfs_btree_cur_t *cur, /* if null, not a btree */
xfs_bmbt_irec_t *del, /* data to remove from extents */
int *logflagsp, /* inode logging flags */
- int whichfork, /* data or attr fork */
- int rsvd) /* OK to allocate reserved blocks */
+ int whichfork) /* data or attr fork */
{
xfs_filblks_t da_new; /* new delay-alloc indirect blocks */
xfs_filblks_t da_old; /* old delay-alloc indirect blocks */
@@ -2870,10 +2837,10 @@
mp = ip->i_mount;
ifp = XFS_IFORK_PTR(ip, whichfork);
- ASSERT((idx >= 0) && (idx < ifp->if_bytes /
+ ASSERT((*idx >= 0) && (*idx < ifp->if_bytes /
(uint)sizeof(xfs_bmbt_rec_t)));
ASSERT(del->br_blockcount > 0);
- ep = xfs_iext_get_ext(ifp, idx);
+ ep = xfs_iext_get_ext(ifp, *idx);
xfs_bmbt_get_all(ep, &got);
ASSERT(got.br_startoff <= del->br_startoff);
del_endoff = del->br_startoff + del->br_blockcount;
@@ -2947,11 +2914,12 @@
/*
* Matches the whole extent. Delete the entry.
*/
- xfs_iext_remove(ip, idx, 1,
+ xfs_iext_remove(ip, *idx, 1,
whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
- ifp->if_lastex = idx;
+ --*idx;
if (delay)
break;
+
XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
flags |= XFS_ILOG_CORE;
@@ -2968,21 +2936,20 @@
/*
* Deleting the first part of the extent.
*/
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_startoff(ep, del_endoff);
temp = got.br_blockcount - del->br_blockcount;
xfs_bmbt_set_blockcount(ep, temp);
- ifp->if_lastex = idx;
if (delay) {
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
da_old);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
da_new = temp;
break;
}
xfs_bmbt_set_startblock(ep, del_endblock);
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
if (!cur) {
flags |= xfs_ilog_fext(whichfork);
break;
@@ -2998,18 +2965,17 @@
* Deleting the last part of the extent.
*/
temp = got.br_blockcount - del->br_blockcount;
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
- ifp->if_lastex = idx;
if (delay) {
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
da_old);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
da_new = temp;
break;
}
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
if (!cur) {
flags |= xfs_ilog_fext(whichfork);
break;
@@ -3026,7 +2992,7 @@
* Deleting the middle of the extent.
*/
temp = del->br_startoff - got.br_startoff;
- trace_xfs_bmap_pre_update(ip, idx, state, _THIS_IP_);
+ trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
new.br_startoff = del_endoff;
temp2 = got_endoff - del_endoff;
@@ -3113,9 +3079,9 @@
}
}
}
- trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_);
- xfs_iext_insert(ip, idx + 1, 1, &new, state);
- ifp->if_lastex = idx + 1;
+ trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+ xfs_iext_insert(ip, *idx + 1, 1, &new, state);
+ ++*idx;
break;
}
/*
@@ -3142,7 +3108,7 @@
ASSERT(da_old >= da_new);
if (da_old > da_new) {
xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
- (int64_t)(da_old - da_new), rsvd);
+ (int64_t)(da_old - da_new), 0);
}
done:
*logflagsp = flags;
@@ -4562,29 +4528,24 @@
if (rt) {
error = xfs_mod_incore_sb(mp,
XFS_SBS_FREXTENTS,
- -((int64_t)extsz), (flags &
- XFS_BMAPI_RSVBLOCKS));
+ -((int64_t)extsz), 0);
} else {
error = xfs_icsb_modify_counters(mp,
XFS_SBS_FDBLOCKS,
- -((int64_t)alen), (flags &
- XFS_BMAPI_RSVBLOCKS));
+ -((int64_t)alen), 0);
}
if (!error) {
error = xfs_icsb_modify_counters(mp,
XFS_SBS_FDBLOCKS,
- -((int64_t)indlen), (flags &
- XFS_BMAPI_RSVBLOCKS));
+ -((int64_t)indlen), 0);
if (error && rt)
xfs_mod_incore_sb(mp,
XFS_SBS_FREXTENTS,
- (int64_t)extsz, (flags &
- XFS_BMAPI_RSVBLOCKS));
+ (int64_t)extsz, 0);
else if (error)
xfs_icsb_modify_counters(mp,
XFS_SBS_FDBLOCKS,
- (int64_t)alen, (flags &
- XFS_BMAPI_RSVBLOCKS));
+ (int64_t)alen, 0);
}
if (error) {
@@ -4701,13 +4662,12 @@
if (!wasdelay && (flags & XFS_BMAPI_PREALLOC))
got.br_state = XFS_EXT_UNWRITTEN;
}
- error = xfs_bmap_add_extent(ip, lastx, &cur, &got,
+ error = xfs_bmap_add_extent(ip, &lastx, &cur, &got,
firstblock, flist, &tmp_logflags,
- whichfork, (flags & XFS_BMAPI_RSVBLOCKS));
+ whichfork);
logflags |= tmp_logflags;
if (error)
goto error0;
- lastx = ifp->if_lastex;
ep = xfs_iext_get_ext(ifp, lastx);
nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
xfs_bmbt_get_all(ep, &got);
@@ -4803,13 +4763,12 @@
mval->br_state = (mval->br_state == XFS_EXT_UNWRITTEN)
? XFS_EXT_NORM
: XFS_EXT_UNWRITTEN;
- error = xfs_bmap_add_extent(ip, lastx, &cur, mval,
+ error = xfs_bmap_add_extent(ip, &lastx, &cur, mval,
firstblock, flist, &tmp_logflags,
- whichfork, (flags & XFS_BMAPI_RSVBLOCKS));
+ whichfork);
logflags |= tmp_logflags;
if (error)
goto error0;
- lastx = ifp->if_lastex;
ep = xfs_iext_get_ext(ifp, lastx);
nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
xfs_bmbt_get_all(ep, &got);
@@ -4868,14 +4827,14 @@
/*
* Else go on to the next record.
*/
- ep = xfs_iext_get_ext(ifp, ++lastx);
prev = got;
- if (lastx >= nextents)
- eof = 1;
- else
+ if (++lastx < nextents) {
+ ep = xfs_iext_get_ext(ifp, lastx);
xfs_bmbt_get_all(ep, &got);
+ } else {
+ eof = 1;
+ }
}
- ifp->if_lastex = lastx;
*nmap = n;
/*
* Transform from btree to extents, give it cur.
@@ -4984,7 +4943,6 @@
ASSERT(!isnullstartblock(got.br_startblock));
ASSERT(bno < got.br_startoff + got.br_blockcount);
*fsb = got.br_startblock + (bno - got.br_startoff);
- ifp->if_lastex = lastx;
return 0;
}
@@ -5026,7 +4984,6 @@
int tmp_logflags; /* partial logging flags */
int wasdel; /* was a delayed alloc extent */
int whichfork; /* data or attribute fork */
- int rsvd; /* OK to allocate reserved blocks */
xfs_fsblock_t sum;
trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
@@ -5044,7 +5001,7 @@
mp = ip->i_mount;
if (XFS_FORCED_SHUTDOWN(mp))
return XFS_ERROR(EIO);
- rsvd = (flags & XFS_BMAPI_RSVBLOCKS) != 0;
+
ASSERT(len > 0);
ASSERT(nexts >= 0);
ASSERT(ifp->if_ext_max ==
@@ -5160,9 +5117,9 @@
del.br_blockcount = mod;
}
del.br_state = XFS_EXT_UNWRITTEN;
- error = xfs_bmap_add_extent(ip, lastx, &cur, &del,
+ error = xfs_bmap_add_extent(ip, &lastx, &cur, &del,
firstblock, flist, &logflags,
- XFS_DATA_FORK, 0);
+ XFS_DATA_FORK);
if (error)
goto error0;
goto nodelete;
@@ -5188,9 +5145,12 @@
*/
ASSERT(bno >= del.br_blockcount);
bno -= del.br_blockcount;
- if (bno < got.br_startoff) {
- if (--lastx >= 0)
- xfs_bmbt_get_all(--ep, &got);
+ if (got.br_startoff > bno) {
+ if (--lastx >= 0) {
+ ep = xfs_iext_get_ext(ifp,
+ lastx);
+ xfs_bmbt_get_all(ep, &got);
+ }
}
continue;
} else if (del.br_state == XFS_EXT_UNWRITTEN) {
@@ -5214,18 +5174,19 @@
prev.br_startoff = start;
}
prev.br_state = XFS_EXT_UNWRITTEN;
- error = xfs_bmap_add_extent(ip, lastx - 1, &cur,
+ lastx--;
+ error = xfs_bmap_add_extent(ip, &lastx, &cur,
&prev, firstblock, flist, &logflags,
- XFS_DATA_FORK, 0);
+ XFS_DATA_FORK);
if (error)
goto error0;
goto nodelete;
} else {
ASSERT(del.br_state == XFS_EXT_NORM);
del.br_state = XFS_EXT_UNWRITTEN;
- error = xfs_bmap_add_extent(ip, lastx, &cur,
+ error = xfs_bmap_add_extent(ip, &lastx, &cur,
&del, firstblock, flist, &logflags,
- XFS_DATA_FORK, 0);
+ XFS_DATA_FORK);
if (error)
goto error0;
goto nodelete;
@@ -5240,13 +5201,13 @@
rtexts = XFS_FSB_TO_B(mp, del.br_blockcount);
do_div(rtexts, mp->m_sb.sb_rextsize);
xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS,
- (int64_t)rtexts, rsvd);
+ (int64_t)rtexts, 0);
(void)xfs_trans_reserve_quota_nblks(NULL,
ip, -((long)del.br_blockcount), 0,
XFS_QMOPT_RES_RTBLKS);
} else {
xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
- (int64_t)del.br_blockcount, rsvd);
+ (int64_t)del.br_blockcount, 0);
(void)xfs_trans_reserve_quota_nblks(NULL,
ip, -((long)del.br_blockcount), 0,
XFS_QMOPT_RES_REGBLKS);
@@ -5277,31 +5238,29 @@
error = XFS_ERROR(ENOSPC);
goto error0;
}
- error = xfs_bmap_del_extent(ip, tp, lastx, flist, cur, &del,
- &tmp_logflags, whichfork, rsvd);
+ error = xfs_bmap_del_extent(ip, tp, &lastx, flist, cur, &del,
+ &tmp_logflags, whichfork);
logflags |= tmp_logflags;
if (error)
goto error0;
bno = del.br_startoff - 1;
nodelete:
- lastx = ifp->if_lastex;
/*
* If not done go on to the next (previous) record.
- * Reset ep in case the extents array was re-alloced.
*/
- ep = xfs_iext_get_ext(ifp, lastx);
if (bno != (xfs_fileoff_t)-1 && bno >= start) {
- if (lastx >= XFS_IFORK_NEXTENTS(ip, whichfork) ||
- xfs_bmbt_get_startoff(ep) > bno) {
- if (--lastx >= 0)
- ep = xfs_iext_get_ext(ifp, lastx);
- }
- if (lastx >= 0)
+ if (lastx >= 0) {
+ ep = xfs_iext_get_ext(ifp, lastx);
+ if (xfs_bmbt_get_startoff(ep) > bno) {
+ if (--lastx >= 0)
+ ep = xfs_iext_get_ext(ifp,
+ lastx);
+ }
xfs_bmbt_get_all(ep, &got);
+ }
extno++;
}
}
- ifp->if_lastex = lastx;
*done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0;
ASSERT(ifp->if_ext_max ==
XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
index 3651191..c62234b 100644
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -69,7 +69,6 @@
#define XFS_BMAPI_ENTIRE 0x004 /* return entire extent, not trimmed */
#define XFS_BMAPI_METADATA 0x008 /* mapping metadata not user data */
#define XFS_BMAPI_ATTRFORK 0x010 /* use attribute fork not data */
-#define XFS_BMAPI_RSVBLOCKS 0x020 /* OK to alloc. reserved data blocks */
#define XFS_BMAPI_PREALLOC 0x040 /* preallocation op: unwritten space */
#define XFS_BMAPI_IGSTATE 0x080 /* Ignore state - */
/* combine contig. space */
@@ -87,7 +86,6 @@
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
{ XFS_BMAPI_METADATA, "METADATA" }, \
{ XFS_BMAPI_ATTRFORK, "ATTRFORK" }, \
- { XFS_BMAPI_RSVBLOCKS, "RSVBLOCKS" }, \
{ XFS_BMAPI_PREALLOC, "PREALLOC" }, \
{ XFS_BMAPI_IGSTATE, "IGSTATE" }, \
{ XFS_BMAPI_CONTIG, "CONTIG" }, \
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index c8e3349..a098a20 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -920,7 +920,6 @@
/*
* We know that the size is valid (it's checked in iformat_btree)
*/
- ifp->if_lastex = NULLEXTNUM;
ifp->if_bytes = ifp->if_real_bytes = 0;
ifp->if_flags |= XFS_IFEXTENTS;
xfs_iext_add(ifp, 0, nextents);
@@ -2558,12 +2557,9 @@
case XFS_DINODE_FMT_EXTENTS:
ASSERT((ifp->if_flags & XFS_IFEXTENTS) ||
!(iip->ili_format.ilf_fields & extflag[whichfork]));
- ASSERT((xfs_iext_get_ext(ifp, 0) != NULL) ||
- (ifp->if_bytes == 0));
- ASSERT((xfs_iext_get_ext(ifp, 0) == NULL) ||
- (ifp->if_bytes > 0));
if ((iip->ili_format.ilf_fields & extflag[whichfork]) &&
(ifp->if_bytes > 0)) {
+ ASSERT(xfs_iext_get_ext(ifp, 0));
ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
whichfork);
@@ -3112,6 +3108,8 @@
xfs_extnum_t idx) /* index of target extent */
{
ASSERT(idx >= 0);
+ ASSERT(idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
+
if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
return ifp->if_u1.if_ext_irec->er_extbuf;
} else if (ifp->if_flags & XFS_IFEXTIREC) {
@@ -3191,7 +3189,6 @@
}
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
ifp->if_real_bytes = 0;
- ifp->if_lastex = nextents + ext_diff;
}
/*
* Otherwise use a linear (direct) extent list.
@@ -3886,8 +3883,10 @@
xfs_extnum_t page_idx = *idxp; /* extent index in target list */
ASSERT(ifp->if_flags & XFS_IFEXTIREC);
- ASSERT(page_idx >= 0 && page_idx <=
- ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t));
+ ASSERT(page_idx >= 0);
+ ASSERT(page_idx <= ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
+ ASSERT(page_idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t) || realloc);
+
nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
erp_idx = 0;
low = 0;
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index ff4e2a3..3ae6d58 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -67,7 +67,6 @@
short if_broot_bytes; /* bytes allocated for root */
unsigned char if_flags; /* per-fork flags */
unsigned char if_ext_max; /* max # of extent records */
- xfs_extnum_t if_lastex; /* last if_extents used */
union {
xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */
xfs_ext_irec_t *if_ext_irec; /* irec map file exts */
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c
index 7d56e88..c7755d5 100644
--- a/fs/xfs/xfs_log_cil.c
+++ b/fs/xfs/xfs_log_cil.c
@@ -29,6 +29,7 @@
#include "xfs_mount.h"
#include "xfs_error.h"
#include "xfs_alloc.h"
+#include "xfs_discard.h"
/*
* Perform initial CIL structure initialisation. If the CIL is not
@@ -361,18 +362,28 @@
int abort)
{
struct xfs_cil_ctx *ctx = args;
+ struct xfs_mount *mp = ctx->cil->xc_log->l_mp;
xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, ctx->lv_chain,
ctx->start_lsn, abort);
xfs_alloc_busy_sort(&ctx->busy_extents);
- xfs_alloc_busy_clear(ctx->cil->xc_log->l_mp, &ctx->busy_extents);
+ xfs_alloc_busy_clear(mp, &ctx->busy_extents,
+ (mp->m_flags & XFS_MOUNT_DISCARD) && !abort);
spin_lock(&ctx->cil->xc_cil_lock);
list_del(&ctx->committing);
spin_unlock(&ctx->cil->xc_cil_lock);
xlog_cil_free_logvec(ctx->lv_chain);
+
+ if (!list_empty(&ctx->busy_extents)) {
+ ASSERT(mp->m_flags & XFS_MOUNT_DISCARD);
+
+ xfs_discard_extents(mp, &ctx->busy_extents);
+ xfs_alloc_busy_clear(mp, &ctx->busy_extents, false);
+ }
+
kmem_free(ctx);
}
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 19af0ab..3d68bb2 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -224,6 +224,7 @@
#define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem
operations, typically for
disk errors in metadata */
+#define XFS_MOUNT_DISCARD (1ULL << 5) /* discard unused blocks */
#define XFS_MOUNT_RETERR (1ULL << 6) /* return alignment errors to
user */
#define XFS_MOUNT_NOALIGN (1ULL << 7) /* turn off stripe alignment
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index d1f2485..7c7bc2b 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -609,7 +609,7 @@
struct xfs_trans *tp)
{
xfs_alloc_busy_sort(&tp->t_busy);
- xfs_alloc_busy_clear(tp->t_mountp, &tp->t_busy);
+ xfs_alloc_busy_clear(tp->t_mountp, &tp->t_busy, false);
atomic_dec(&tp->t_mountp->m_active_trans);
xfs_trans_free_dqinfo(tp);
diff --git a/include/asm-generic/bitops/find.h b/include/asm-generic/bitops/find.h
index 110fa70..71c7780 100644
--- a/include/asm-generic/bitops/find.h
+++ b/include/asm-generic/bitops/find.h
@@ -1,6 +1,7 @@
#ifndef _ASM_GENERIC_BITOPS_FIND_H_
#define _ASM_GENERIC_BITOPS_FIND_H_
+#ifndef find_next_bit
/**
* find_next_bit - find the next set bit in a memory region
* @addr: The address to base the search on
@@ -9,7 +10,9 @@
*/
extern unsigned long find_next_bit(const unsigned long *addr, unsigned long
size, unsigned long offset);
+#endif
+#ifndef find_next_zero_bit
/**
* find_next_zero_bit - find the next cleared bit in a memory region
* @addr: The address to base the search on
@@ -18,6 +21,7 @@
*/
extern unsigned long find_next_zero_bit(const unsigned long *addr, unsigned
long size, unsigned long offset);
+#endif
#ifdef CONFIG_GENERIC_FIND_FIRST_BIT
diff --git a/include/asm-generic/bitops/le.h b/include/asm-generic/bitops/le.h
index 946a21b..f95c663 100644
--- a/include/asm-generic/bitops/le.h
+++ b/include/asm-generic/bitops/le.h
@@ -30,13 +30,20 @@
#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7)
+#ifndef find_next_zero_bit_le
extern unsigned long find_next_zero_bit_le(const void *addr,
unsigned long size, unsigned long offset);
+#endif
+
+#ifndef find_next_bit_le
extern unsigned long find_next_bit_le(const void *addr,
unsigned long size, unsigned long offset);
+#endif
+#ifndef find_first_zero_bit_le
#define find_first_zero_bit_le(addr, size) \
find_next_zero_bit_le((addr), (size), 0)
+#endif
#else
#error "Please fix <asm/byteorder.h>"
diff --git a/include/asm-generic/ptrace.h b/include/asm-generic/ptrace.h
new file mode 100644
index 0000000..82e674f
--- /dev/null
+++ b/include/asm-generic/ptrace.h
@@ -0,0 +1,74 @@
+/*
+ * Common low level (register) ptrace helpers
+ *
+ * Copyright 2004-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __ASM_GENERIC_PTRACE_H__
+#define __ASM_GENERIC_PTRACE_H__
+
+#ifndef __ASSEMBLY__
+
+/* Helpers for working with the instruction pointer */
+#ifndef GET_IP
+#define GET_IP(regs) ((regs)->pc)
+#endif
+#ifndef SET_IP
+#define SET_IP(regs, val) (GET_IP(regs) = (val))
+#endif
+
+static inline unsigned long instruction_pointer(struct pt_regs *regs)
+{
+ return GET_IP(regs);
+}
+static inline void instruction_pointer_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ SET_IP(regs, val);
+}
+
+#ifndef profile_pc
+#define profile_pc(regs) instruction_pointer(regs)
+#endif
+
+/* Helpers for working with the user stack pointer */
+#ifndef GET_USP
+#define GET_USP(regs) ((regs)->usp)
+#endif
+#ifndef SET_USP
+#define SET_USP(regs, val) (GET_USP(regs) = (val))
+#endif
+
+static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+ return GET_USP(regs);
+}
+static inline void user_stack_pointer_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ SET_USP(regs, val);
+}
+
+/* Helpers for working with the frame pointer */
+#ifndef GET_FP
+#define GET_FP(regs) ((regs)->fp)
+#endif
+#ifndef SET_FP
+#define SET_FP(regs, val) (GET_FP(regs) = (val))
+#endif
+
+static inline unsigned long frame_pointer(struct pt_regs *regs)
+{
+ return GET_FP(regs);
+}
+static inline void frame_pointer_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ SET_FP(regs, val);
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif
diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h
index 198087a..1ae1271 100644
--- a/include/linux/basic_mmio_gpio.h
+++ b/include/linux/basic_mmio_gpio.h
@@ -13,8 +13,64 @@
#ifndef __BASIC_MMIO_GPIO_H
#define __BASIC_MMIO_GPIO_H
+#include <linux/gpio.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+
struct bgpio_pdata {
int base;
+ int ngpio;
};
+struct device;
+
+struct bgpio_chip {
+ struct gpio_chip gc;
+
+ unsigned long (*read_reg)(void __iomem *reg);
+ void (*write_reg)(void __iomem *reg, unsigned long data);
+
+ void __iomem *reg_dat;
+ void __iomem *reg_set;
+ void __iomem *reg_clr;
+ void __iomem *reg_dir;
+
+ /* Number of bits (GPIOs): <register width> * 8. */
+ int bits;
+
+ /*
+ * Some GPIO controllers work with the big-endian bits notation,
+ * e.g. in a 8-bits register, GPIO7 is the least significant bit.
+ */
+ unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);
+
+ /*
+ * Used to lock bgpio_chip->data. Also, this is needed to keep
+ * shadowed and real data registers writes together.
+ */
+ spinlock_t lock;
+
+ /* Shadowed data register to clear/set bits safely. */
+ unsigned long data;
+
+ /* Shadowed direction registers to clear/set direction safely. */
+ unsigned long dir;
+};
+
+static inline struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc)
+{
+ return container_of(gc, struct bgpio_chip, gc);
+}
+
+int __devexit bgpio_remove(struct bgpio_chip *bgc);
+int __devinit bgpio_init(struct bgpio_chip *bgc,
+ struct device *dev,
+ unsigned long sz,
+ void __iomem *dat,
+ void __iomem *set,
+ void __iomem *clr,
+ void __iomem *dirout,
+ void __iomem *dirin,
+ bool big_endian);
+
#endif /* __BASIC_MMIO_GPIO_H */
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index 2184c6b9..a3ef66a 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -148,7 +148,7 @@
#ifdef __KERNEL__
-#ifdef CONFIG_GENERIC_FIND_LAST_BIT
+#ifndef find_last_bit
/**
* find_last_bit - find the last set bit in a memory region
* @addr: The address to start the search at
@@ -158,7 +158,7 @@
*/
extern unsigned long find_last_bit(const unsigned long *addr,
unsigned long size);
-#endif /* CONFIG_GENERIC_FIND_LAST_BIT */
+#endif
#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index f5df235..503c8a6 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -217,8 +217,24 @@
get_block_t *, loff_t *);
int generic_cont_expand_simple(struct inode *inode, loff_t size);
int block_commit_write(struct page *page, unsigned from, unsigned to);
+int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+ get_block_t get_block);
int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
get_block_t get_block);
+/* Convert errno to return value from ->page_mkwrite() call */
+static inline int block_page_mkwrite_return(int err)
+{
+ if (err == 0)
+ return VM_FAULT_LOCKED;
+ if (err == -EFAULT)
+ return VM_FAULT_NOPAGE;
+ if (err == -ENOMEM)
+ return VM_FAULT_OOM;
+ if (err == -EAGAIN)
+ return VM_FAULT_RETRY;
+ /* -ENOSPC, -EDQUOT, -EIO ... */
+ return VM_FAULT_SIGBUS;
+}
sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
int block_truncate_page(struct address_space *, loff_t, get_block_t *);
int nobh_write_begin(struct address_space *, loff_t, unsigned, unsigned,
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 5ac7ebc..ab4ac0c 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -467,12 +467,14 @@
int (*pre_destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp);
void (*destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp);
int (*can_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct task_struct *tsk, bool threadgroup);
+ struct task_struct *tsk);
+ int (*can_attach_task)(struct cgroup *cgrp, struct task_struct *tsk);
void (*cancel_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct task_struct *tsk, bool threadgroup);
+ struct task_struct *tsk);
+ void (*pre_attach)(struct cgroup *cgrp);
+ void (*attach_task)(struct cgroup *cgrp, struct task_struct *tsk);
void (*attach)(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct cgroup *old_cgrp, struct task_struct *tsk,
- bool threadgroup);
+ struct cgroup *old_cgrp, struct task_struct *tsk);
void (*fork)(struct cgroup_subsys *ss, struct task_struct *task);
void (*exit)(struct cgroup_subsys *ss, struct cgroup *cgrp,
struct cgroup *old_cgrp, struct task_struct *task);
@@ -553,9 +555,6 @@
return task_subsys_state(task, subsys_id)->cgroup;
}
-int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *ss,
- char *nodename);
-
/* A cgroup_iter should be treated as an opaque object */
struct cgroup_iter {
struct list_head *cg_link;
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index cdbfcb8..ac663c1 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -19,12 +19,6 @@
/* */
-#ifdef CONFIG_CGROUP_NS
-SUBSYS(ns)
-#endif
-
-/* */
-
#ifdef CONFIG_CGROUP_SCHED
SUBSYS(cpu_cgroup)
#endif
diff --git a/include/linux/cleancache.h b/include/linux/cleancache.h
new file mode 100644
index 0000000..04ffb2e
--- /dev/null
+++ b/include/linux/cleancache.h
@@ -0,0 +1,122 @@
+#ifndef _LINUX_CLEANCACHE_H
+#define _LINUX_CLEANCACHE_H
+
+#include <linux/fs.h>
+#include <linux/exportfs.h>
+#include <linux/mm.h>
+
+#define CLEANCACHE_KEY_MAX 6
+
+/*
+ * cleancache requires every file with a page in cleancache to have a
+ * unique key unless/until the file is removed/truncated. For some
+ * filesystems, the inode number is unique, but for "modern" filesystems
+ * an exportable filehandle is required (see exportfs.h)
+ */
+struct cleancache_filekey {
+ union {
+ ino_t ino;
+ __u32 fh[CLEANCACHE_KEY_MAX];
+ u32 key[CLEANCACHE_KEY_MAX];
+ } u;
+};
+
+struct cleancache_ops {
+ int (*init_fs)(size_t);
+ int (*init_shared_fs)(char *uuid, size_t);
+ int (*get_page)(int, struct cleancache_filekey,
+ pgoff_t, struct page *);
+ void (*put_page)(int, struct cleancache_filekey,
+ pgoff_t, struct page *);
+ void (*flush_page)(int, struct cleancache_filekey, pgoff_t);
+ void (*flush_inode)(int, struct cleancache_filekey);
+ void (*flush_fs)(int);
+};
+
+extern struct cleancache_ops
+ cleancache_register_ops(struct cleancache_ops *ops);
+extern void __cleancache_init_fs(struct super_block *);
+extern void __cleancache_init_shared_fs(char *, struct super_block *);
+extern int __cleancache_get_page(struct page *);
+extern void __cleancache_put_page(struct page *);
+extern void __cleancache_flush_page(struct address_space *, struct page *);
+extern void __cleancache_flush_inode(struct address_space *);
+extern void __cleancache_flush_fs(struct super_block *);
+extern int cleancache_enabled;
+
+#ifdef CONFIG_CLEANCACHE
+static inline bool cleancache_fs_enabled(struct page *page)
+{
+ return page->mapping->host->i_sb->cleancache_poolid >= 0;
+}
+static inline bool cleancache_fs_enabled_mapping(struct address_space *mapping)
+{
+ return mapping->host->i_sb->cleancache_poolid >= 0;
+}
+#else
+#define cleancache_enabled (0)
+#define cleancache_fs_enabled(_page) (0)
+#define cleancache_fs_enabled_mapping(_page) (0)
+#endif
+
+/*
+ * The shim layer provided by these inline functions allows the compiler
+ * to reduce all cleancache hooks to nothingness if CONFIG_CLEANCACHE
+ * is disabled, to a single global variable check if CONFIG_CLEANCACHE
+ * is enabled but no cleancache "backend" has dynamically enabled it,
+ * and, for the most frequent cleancache ops, to a single global variable
+ * check plus a superblock element comparison if CONFIG_CLEANCACHE is enabled
+ * and a cleancache backend has dynamically enabled cleancache, but the
+ * filesystem referenced by that cleancache op has not enabled cleancache.
+ * As a result, CONFIG_CLEANCACHE can be enabled by default with essentially
+ * no measurable performance impact.
+ */
+
+static inline void cleancache_init_fs(struct super_block *sb)
+{
+ if (cleancache_enabled)
+ __cleancache_init_fs(sb);
+}
+
+static inline void cleancache_init_shared_fs(char *uuid, struct super_block *sb)
+{
+ if (cleancache_enabled)
+ __cleancache_init_shared_fs(uuid, sb);
+}
+
+static inline int cleancache_get_page(struct page *page)
+{
+ int ret = -1;
+
+ if (cleancache_enabled && cleancache_fs_enabled(page))
+ ret = __cleancache_get_page(page);
+ return ret;
+}
+
+static inline void cleancache_put_page(struct page *page)
+{
+ if (cleancache_enabled && cleancache_fs_enabled(page))
+ __cleancache_put_page(page);
+}
+
+static inline void cleancache_flush_page(struct address_space *mapping,
+ struct page *page)
+{
+ /* careful... page->mapping is NULL sometimes when this is called */
+ if (cleancache_enabled && cleancache_fs_enabled_mapping(mapping))
+ __cleancache_flush_page(mapping, page);
+}
+
+static inline void cleancache_flush_inode(struct address_space *mapping)
+{
+ if (cleancache_enabled && cleancache_fs_enabled_mapping(mapping))
+ __cleancache_flush_inode(mapping);
+}
+
+static inline void cleancache_flush_fs(struct super_block *sb)
+{
+ if (cleancache_enabled)
+ __cleancache_flush_fs(sb);
+}
+
+#endif /* _LINUX_CLEANCACHE_H */
diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h
index 088cd4a..7405407 100644
--- a/include/linux/crash_dump.h
+++ b/include/linux/crash_dump.h
@@ -66,6 +66,11 @@
if (is_kdump_kernel())
elfcorehdr_addr = ELFCORE_ADDR_ERR;
}
+
+#define HAVE_OLDMEM_PFN_IS_RAM 1
+extern int register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn));
+extern void unregister_oldmem_pfn_is_ram(void);
+
#else /* !CONFIG_CRASH_DUMP */
static inline int is_kdump_kernel(void) { return 0; }
#endif /* CONFIG_CRASH_DUMP */
diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h
index ebeb2f3..6843cf1 100644
--- a/include/linux/flex_array.h
+++ b/include/linux/flex_array.h
@@ -21,6 +21,8 @@
struct {
int element_size;
int total_nr_elements;
+ int elems_per_part;
+ u32 reciprocal_elems;
struct flex_array_part *parts[];
};
/*
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3f9d325..2416093 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1428,6 +1428,11 @@
*/
char __rcu *s_options;
const struct dentry_operations *s_d_op; /* default d_op for dentries */
+
+ /*
+ * Saved pool identifier for cleancache (-1 means none)
+ */
+ int cleancache_poolid;
};
extern struct timespec current_fs_time(struct super_block *sb);
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 943c76b..59225ef 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -1,6 +1,7 @@
#ifndef _LINUX_HUGETLB_H
#define _LINUX_HUGETLB_H
+#include <linux/mm_types.h>
#include <linux/fs.h>
#include <linux/hugetlb_inline.h>
@@ -41,7 +42,7 @@
unsigned long address, unsigned int flags);
int hugetlb_reserve_pages(struct inode *inode, long from, long to,
struct vm_area_struct *vma,
- int acctflags);
+ vm_flags_t vm_flags);
void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed);
int dequeue_hwpoisoned_huge_page(struct page *page);
void copy_huge_page(struct page *dst, struct page *src);
@@ -168,7 +169,7 @@
extern const struct file_operations hugetlbfs_file_operations;
extern const struct vm_operations_struct hugetlb_vm_ops;
-struct file *hugetlb_file_setup(const char *name, size_t size, int acct,
+struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
struct user_struct **user, int creat_flags);
int hugetlb_get_quota(struct address_space *mapping, long delta);
void hugetlb_put_quota(struct address_space *mapping, long delta);
@@ -192,7 +193,7 @@
#define is_file_hugepages(file) 0
#define set_file_hugepages(file) BUG()
static inline struct file *hugetlb_file_setup(const char *name, size_t size,
- int acctflag, struct user_struct **user, int creat_flags)
+ vm_flags_t acctflag, struct user_struct **user, int creat_flags)
{
return ERR_PTR(-ENOSYS);
}
diff --git a/include/linux/hugetlb_inline.h b/include/linux/hugetlb_inline.h
index 6931489..2bb681f 100644
--- a/include/linux/hugetlb_inline.h
+++ b/include/linux/hugetlb_inline.h
@@ -7,7 +7,7 @@
static inline int is_vm_hugetlb_page(struct vm_area_struct *vma)
{
- return vma->vm_flags & VM_HUGETLB;
+ return !!(vma->vm_flags & VM_HUGETLB);
}
#else
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index 0c0d1ae..ba4f886 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -91,6 +91,7 @@
#define BCI_INTR_OFFSET 2
#define MADC_INTR_OFFSET 3
#define USB_INTR_OFFSET 4
+#define CHARGERFAULT_INTR_OFFSET 5
#define BCI_PRES_INTR_OFFSET 9
#define USB_PRES_INTR_OFFSET 10
#define RTC_INTR_OFFSET 11
@@ -150,7 +151,12 @@
#define MMC_PU (0x1 << 3)
#define MMC_PD (0x1 << 2)
-
+#define TWL_SIL_TYPE(rev) ((rev) & 0x00FFFFFF)
+#define TWL_SIL_REV(rev) ((rev) >> 24)
+#define TWL_SIL_5030 0x09002F
+#define TWL5030_REV_1_0 0x00
+#define TWL5030_REV_1_1 0x10
+#define TWL5030_REV_1_2 0x30
#define TWL4030_CLASS_ID 0x4030
#define TWL6030_CLASS_ID 0x6030
@@ -165,6 +171,8 @@
TWL_CLASS_IS(4030, TWL4030_CLASS_ID)
TWL_CLASS_IS(6030, TWL6030_CLASS_ID)
+#define TWL6025_SUBCLASS BIT(4) /* TWL6025 has changed registers */
+
/*
* Read and write single 8-bit registers
*/
@@ -180,6 +188,9 @@
int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
+int twl_get_type(void);
+int twl_get_version(void);
+
int twl6030_interrupt_unmask(u8 bit_mask, u8 offset);
int twl6030_interrupt_mask(u8 bit_mask, u8 offset);
@@ -279,7 +290,12 @@
*(Use TWL_4030_MODULE_INTBR)
*/
+#define REG_IDCODE_7_0 0x00
+#define REG_IDCODE_15_8 0x01
+#define REG_IDCODE_16_23 0x02
+#define REG_IDCODE_31_24 0x03
#define REG_GPPUPDCTR1 0x0F
+#define REG_UNLOCK_TEST_REG 0x12
/*I2C1 and I2C4(SR) SDA/SCL pull-up control bits */
@@ -288,6 +304,8 @@
#define SR_I2C_SCL_CTRL_PU BIT(4)
#define SR_I2C_SDA_CTRL_PU BIT(6)
+#define TWL_EEPROM_R_UNLOCK 0x49
+
/*----------------------------------------------------------------------*/
/*
@@ -501,7 +519,7 @@
#define RES_32KCLKOUT 26
#define RES_RESET 27
/* Power Reference */
-#define RES_Main_Ref 28
+#define RES_MAIN_REF 28
#define TOTAL_RESOURCES 28
/*
@@ -593,6 +611,7 @@
struct twl4030_usb_data {
enum twl4030_usb_mode usb_mode;
+ unsigned long features;
int (*phy_init)(struct device *dev);
int (*phy_exit)(struct device *dev);
@@ -699,6 +718,20 @@
struct regulator_init_data *vcxio;
struct regulator_init_data *vusb;
struct regulator_init_data *clk32kg;
+ /* TWL6025 LDO regulators */
+ struct regulator_init_data *ldo1;
+ struct regulator_init_data *ldo2;
+ struct regulator_init_data *ldo3;
+ struct regulator_init_data *ldo4;
+ struct regulator_init_data *ldo5;
+ struct regulator_init_data *ldo6;
+ struct regulator_init_data *ldo7;
+ struct regulator_init_data *ldoln;
+ struct regulator_init_data *ldousb;
+ /* TWL6025 DCDC regulators */
+ struct regulator_init_data *smps3;
+ struct regulator_init_data *smps4;
+ struct regulator_init_data *vio6025;
};
/*----------------------------------------------------------------------*/
@@ -780,4 +813,21 @@
#define TWL6030_REG_VRTC 47
#define TWL6030_REG_CLK32KG 48
+/* LDOs on 6025 have different names */
+#define TWL6025_REG_LDO2 49
+#define TWL6025_REG_LDO4 50
+#define TWL6025_REG_LDO3 51
+#define TWL6025_REG_LDO5 52
+#define TWL6025_REG_LDO1 53
+#define TWL6025_REG_LDO7 54
+#define TWL6025_REG_LDO6 55
+#define TWL6025_REG_LDOLN 56
+#define TWL6025_REG_LDOUSB 57
+
+/* 6025 DCDC supplies */
+#define TWL6025_REG_SMPS3 58
+#define TWL6025_REG_SMPS4 59
+#define TWL6025_REG_VIO 60
+
+
#endif /* End of __TWL4030_H */
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index bafc58c..580f70c 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -22,6 +22,14 @@
extern struct files_struct init_files;
extern struct fs_struct init_fs;
+#ifdef CONFIG_CGROUPS
+#define INIT_THREADGROUP_FORK_LOCK(sig) \
+ .threadgroup_fork_lock = \
+ __RWSEM_INITIALIZER(sig.threadgroup_fork_lock),
+#else
+#define INIT_THREADGROUP_FORK_LOCK(sig)
+#endif
+
#define INIT_SIGNALS(sig) { \
.nr_threads = 1, \
.wait_chldexit = __WAIT_QUEUE_HEAD_INITIALIZER(sig.wait_chldexit),\
@@ -38,6 +46,7 @@
}, \
.cred_guard_mutex = \
__MUTEX_INITIALIZER(sig.cred_guard_mutex), \
+ INIT_THREADGROUP_FORK_LOCK(sig) \
}
extern struct nsproxy init_nsproxy;
diff --git a/include/linux/input/pmic8xxx-keypad.h b/include/linux/input/pmic8xxx-keypad.h
new file mode 100644
index 0000000..5f1e2f9
--- /dev/null
+++ b/include/linux/input/pmic8xxx-keypad.h
@@ -0,0 +1,52 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PMIC8XXX_KEYPAD_H__
+#define __PMIC8XXX_KEYPAD_H__
+
+#include <linux/input/matrix_keypad.h>
+
+#define PM8XXX_KEYPAD_DEV_NAME "pm8xxx-keypad"
+
+/**
+ * struct pm8xxx_keypad_platform_data - platform data for keypad
+ * @keymap_data - matrix keymap data
+ * @input_name - input device name
+ * @input_phys_device - input device name
+ * @num_cols - number of columns of keypad
+ * @num_rows - number of row of keypad
+ * @debounce_ms - debounce period in milliseconds
+ * @scan_delay_ms - scan delay in milliseconds
+ * @row_hold_ns - row hold period in nanoseconds
+ * @wakeup - configure keypad as wakeup
+ * @rep - enable or disable key repeat bit
+ */
+struct pm8xxx_keypad_platform_data {
+ const struct matrix_keymap_data *keymap_data;
+
+ const char *input_name;
+ const char *input_phys_device;
+
+ unsigned int num_cols;
+ unsigned int num_rows;
+ unsigned int rows_gpio_start;
+ unsigned int cols_gpio_start;
+
+ unsigned int debounce_ms;
+ unsigned int scan_delay_ms;
+ unsigned int row_hold_ns;
+
+ bool wakeup;
+ bool rep;
+};
+
+#endif /*__PMIC8XXX_KEYPAD_H__ */
diff --git a/include/linux/input/pmic8xxx-pwrkey.h b/include/linux/input/pmic8xxx-pwrkey.h
new file mode 100644
index 0000000..6d2974e
--- /dev/null
+++ b/include/linux/input/pmic8xxx-pwrkey.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PMIC8XXX_PWRKEY_H__
+#define __PMIC8XXX_PWRKEY_H__
+
+#define PM8XXX_PWRKEY_DEV_NAME "pm8xxx-pwrkey"
+
+/**
+ * struct pm8xxx_pwrkey_platform_data - platform data for pwrkey driver
+ * @pull up: power on register control for pull up/down configuration
+ * @kpd_trigger_delay_us: time delay for power key state change interrupt
+ * trigger.
+ * @wakeup: configure power key as wakeup source
+ */
+struct pm8xxx_pwrkey_platform_data {
+ bool pull_up;
+ u32 kpd_trigger_delay_us;
+ u32 wakeup;
+};
+
+#endif /* __PMIC8XXX_PWRKEY_H__ */
diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h
index 906590a..204f9cd 100644
--- a/include/linux/ipmi_smi.h
+++ b/include/linux/ipmi_smi.h
@@ -236,7 +236,7 @@
directory for this interface. Note that the entry will
automatically be dstroyed when the interface is destroyed. */
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
- read_proc_t *read_proc,
+ const struct file_operations *proc_ops,
void *data);
#endif /* __LINUX_IPMI_SMI_H */
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index a32dcae..4ecb7b1 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -529,9 +529,10 @@
enum {
T_RUNNING,
T_LOCKED,
- T_RUNDOWN,
T_FLUSH,
T_COMMIT,
+ T_COMMIT_DFLUSH,
+ T_COMMIT_JFLUSH,
T_FINISHED
} t_state;
@@ -658,7 +659,9 @@
* waiting for it to finish.
*/
unsigned int t_synchronous_commit:1;
- unsigned int t_flushed_data_blocks:1;
+
+ /* Disk flush needs to be sent to fs partition [no locking] */
+ int t_need_data_flush;
/*
* For use by the filesystem to store fs-specific data
@@ -1228,6 +1231,7 @@
int jbd2_journal_force_commit_nested(journal_t *journal);
int jbd2_log_wait_commit(journal_t *journal, tid_t tid);
int jbd2_log_do_checkpoint(journal_t *journal);
+int jbd2_trans_will_send_data_barrier(journal_t *journal, tid_t tid);
void __jbd2_log_wait_for_space(journal_t *journal);
extern void __jbd2_journal_drop_transaction(journal_t *, transaction_t *);
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 5e9840f5..9724a38 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -20,6 +20,8 @@
#ifndef _LINUX_MEMCONTROL_H
#define _LINUX_MEMCONTROL_H
#include <linux/cgroup.h>
+#include <linux/vm_event_item.h>
+
struct mem_cgroup;
struct page_cgroup;
struct page;
@@ -106,9 +108,10 @@
*/
int mem_cgroup_inactive_anon_is_low(struct mem_cgroup *memcg);
int mem_cgroup_inactive_file_is_low(struct mem_cgroup *memcg);
-unsigned long mem_cgroup_zone_nr_pages(struct mem_cgroup *memcg,
- struct zone *zone,
- enum lru_list lru);
+int mem_cgroup_select_victim_node(struct mem_cgroup *memcg);
+unsigned long mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg,
+ struct zone *zone,
+ enum lru_list lru);
struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
struct zone *zone);
struct zone_reclaim_stat*
@@ -144,9 +147,11 @@
}
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
- gfp_t gfp_mask);
+ gfp_t gfp_mask,
+ unsigned long *total_scanned);
u64 mem_cgroup_get_limit(struct mem_cgroup *mem);
+void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx);
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail);
#endif
@@ -302,8 +307,8 @@
}
static inline unsigned long
-mem_cgroup_zone_nr_pages(struct mem_cgroup *memcg, struct zone *zone,
- enum lru_list lru)
+mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, struct zone *zone,
+ enum lru_list lru)
{
return 0;
}
@@ -338,7 +343,8 @@
static inline
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
- gfp_t gfp_mask)
+ gfp_t gfp_mask,
+ unsigned long *total_scanned)
{
return 0;
}
@@ -354,6 +360,10 @@
{
}
+static inline
+void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
+{
+}
#endif /* CONFIG_CGROUP_MEM_CONT */
#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM)
diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h
index 8fba797..63b4fb8 100644
--- a/include/linux/mfd/88pm860x.h
+++ b/include/linux/mfd/88pm860x.h
@@ -330,6 +330,11 @@
unsigned long flags;
};
+struct pm860x_rtc_pdata {
+ int (*sync)(unsigned int ticks);
+ int vrtc;
+};
+
struct pm860x_touch_pdata {
int gpadc_prebias;
int slot_cycle;
@@ -349,6 +354,7 @@
struct pm860x_platform_data {
struct pm860x_backlight_pdata *backlight;
struct pm860x_led_pdata *led;
+ struct pm860x_rtc_pdata *rtc;
struct pm860x_touch_pdata *touch;
struct pm860x_power_pdata *power;
struct regulator_init_data *regulator;
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 7d9b6ae..896b5e4 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -34,6 +34,13 @@
#define AB5500_2_0 0x21
#define AB5500_2_1 0x22
+/* AB8500 CIDs*/
+#define AB8500_CUTEARLY 0x00
+#define AB8500_CUT1P0 0x10
+#define AB8500_CUT1P1 0x11
+#define AB8500_CUT2P0 0x20
+#define AB8500_CUT3P0 0x30
+
/*
* AB3100, EVENTA1, A2 and A3 event register flags
* these are catenated into a single 32-bit flag in the code
@@ -186,6 +193,7 @@
struct ab3550_platform_data {
struct {unsigned int base; unsigned int count; } irq;
void *dev_data[AB3550_NUM_DEVICES];
+ size_t dev_data_sz[AB3550_NUM_DEVICES];
struct abx500_init_settings *init_settings;
unsigned int init_settings_sz;
};
diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h
index de3c4ad..ed793b7 100644
--- a/include/linux/mfd/asic3.h
+++ b/include/linux/mfd/asic3.h
@@ -16,6 +16,13 @@
#include <linux/types.h>
+struct led_classdev;
+struct asic3_led {
+ const char *name;
+ const char *default_trigger;
+ struct led_classdev *cdev;
+};
+
struct asic3_platform_data {
u16 *gpio_config;
unsigned int gpio_config_num;
@@ -23,6 +30,8 @@
unsigned int irq_base;
unsigned int gpio_base;
+
+ struct asic3_led *leds;
};
#define ASIC3_NUM_GPIO_BANKS 4
@@ -111,9 +120,9 @@
#define ASIC3_GPIOA11_PWM0 ASIC3_CONFIG_GPIO(11, 1, 1, 0)
#define ASIC3_GPIOA12_PWM1 ASIC3_CONFIG_GPIO(12, 1, 1, 0)
#define ASIC3_GPIOA15_CONTROL_CX ASIC3_CONFIG_GPIO(15, 1, 1, 0)
-#define ASIC3_GPIOC0_LED0 ASIC3_CONFIG_GPIO(32, 1, 1, 0)
-#define ASIC3_GPIOC1_LED1 ASIC3_CONFIG_GPIO(33, 1, 1, 0)
-#define ASIC3_GPIOC2_LED2 ASIC3_CONFIG_GPIO(34, 1, 1, 0)
+#define ASIC3_GPIOC0_LED0 ASIC3_CONFIG_GPIO(32, 1, 0, 0)
+#define ASIC3_GPIOC1_LED1 ASIC3_CONFIG_GPIO(33, 1, 0, 0)
+#define ASIC3_GPIOC2_LED2 ASIC3_CONFIG_GPIO(34, 1, 0, 0)
#define ASIC3_GPIOC3_SPI_RXD ASIC3_CONFIG_GPIO(35, 1, 0, 0)
#define ASIC3_GPIOC4_CF_nCD ASIC3_CONFIG_GPIO(36, 1, 0, 0)
#define ASIC3_GPIOC4_SPI_TXD ASIC3_CONFIG_GPIO(36, 1, 1, 0)
@@ -152,6 +161,7 @@
#define PWM_TIMEBASE_VALUE(x) ((x)&0xf) /* Low 4 bits sets time base */
#define PWM_TIMEBASE_ENABLE (1 << 4) /* Enable clock */
+#define ASIC3_NUM_LEDS 3
#define ASIC3_LED_0_Base 0x0700
#define ASIC3_LED_1_Base 0x0800
#define ASIC3_LED_2_Base 0x0900
@@ -287,10 +297,17 @@
*
*****************************************************************************/
#define ASIC3_SD_CONFIG_BASE 0x0400 /* Assumes 32 bit addressing */
+#define ASIC3_SD_CONFIG_SIZE 0x0200 /* Assumes 32 bit addressing */
#define ASIC3_SD_CTRL_BASE 0x1000
#define ASIC3_SDIO_CTRL_BASE 0x1200
#define ASIC3_MAP_SIZE_32BIT 0x2000
#define ASIC3_MAP_SIZE_16BIT 0x1000
+/* Functions needed by leds-asic3 */
+
+struct asic3;
+extern void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 val);
+extern u32 asic3_read_register(struct asic3 *asic, unsigned int reg);
+
#endif /* __ASIC3_H__ */
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index aef23309..4e76163 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -33,8 +33,9 @@
int (*suspend)(struct platform_device *dev);
int (*resume)(struct platform_device *dev);
- /* mfd_data can be used to pass data to client drivers */
- void *mfd_data;
+ /* platform data passed to the sub devices drivers */
+ void *platform_data;
+ size_t pdata_size;
/*
* These resources can be specified relative to the parent device.
@@ -89,24 +90,6 @@
return pdev->mfd_cell;
}
-/*
- * Given a platform device that's been created by mfd_add_devices(), fetch
- * the .mfd_data entry from the mfd_cell that created it.
- * Otherwise just return the platform_data pointer.
- * This maintains compatibility with platform drivers whose devices aren't
- * created by the mfd layer, and expect platform_data to contain what would've
- * otherwise been in mfd_data.
- */
-static inline void *mfd_get_data(struct platform_device *pdev)
-{
- const struct mfd_cell *cell = mfd_get_cell(pdev);
-
- if (cell)
- return cell->mfd_data;
- else
- return pdev->dev.platform_data;
-}
-
extern int mfd_add_devices(struct device *parent, int id,
struct mfd_cell *cells, int n_devs,
struct resource *mem_base,
diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h
index 69d1010..5ff2400 100644
--- a/include/linux/mfd/max8997-private.h
+++ b/include/linux/mfd/max8997-private.h
@@ -311,10 +311,6 @@
MAX8997_IRQ_NR,
};
-#define MAX8997_REG_BUCK1DVS(x) (MAX8997_REG_BUCK1DVS1 + (x) - 1)
-#define MAX8997_REG_BUCK2DVS(x) (MAX8997_REG_BUCK2DVS1 + (x) - 1)
-#define MAX8997_REG_BUCK5DVS(x) (MAX8997_REG_BUCK5DVS1 + (x) - 1)
-
#define MAX8997_NUM_GPIO 12
struct max8997_dev {
struct device *dev;
diff --git a/include/linux/mfd/pm8xxx/core.h b/include/linux/mfd/pm8xxx/core.h
new file mode 100644
index 0000000..bd2f4f6
--- /dev/null
+++ b/include/linux/mfd/pm8xxx/core.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm PMIC 8xxx driver header file
+ *
+ */
+
+#ifndef __MFD_PM8XXX_CORE_H
+#define __MFD_PM8XXX_CORE_H
+
+#include <linux/mfd/core.h>
+
+struct pm8xxx_drvdata {
+ int (*pmic_readb) (const struct device *dev, u16 addr, u8 *val);
+ int (*pmic_writeb) (const struct device *dev, u16 addr, u8 val);
+ int (*pmic_read_buf) (const struct device *dev, u16 addr, u8 *buf,
+ int n);
+ int (*pmic_write_buf) (const struct device *dev, u16 addr, u8 *buf,
+ int n);
+ int (*pmic_read_irq_stat) (const struct device *dev, int irq);
+ void *pm_chip_data;
+};
+
+static inline int pm8xxx_readb(const struct device *dev, u16 addr, u8 *val)
+{
+ struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
+
+ if (!dd)
+ return -EINVAL;
+ return dd->pmic_readb(dev, addr, val);
+}
+
+static inline int pm8xxx_writeb(const struct device *dev, u16 addr, u8 val)
+{
+ struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
+
+ if (!dd)
+ return -EINVAL;
+ return dd->pmic_writeb(dev, addr, val);
+}
+
+static inline int pm8xxx_read_buf(const struct device *dev, u16 addr, u8 *buf,
+ int n)
+{
+ struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
+
+ if (!dd)
+ return -EINVAL;
+ return dd->pmic_read_buf(dev, addr, buf, n);
+}
+
+static inline int pm8xxx_write_buf(const struct device *dev, u16 addr, u8 *buf,
+ int n)
+{
+ struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
+
+ if (!dd)
+ return -EINVAL;
+ return dd->pmic_write_buf(dev, addr, buf, n);
+}
+
+static inline int pm8xxx_read_irq_stat(const struct device *dev, int irq)
+{
+ struct pm8xxx_drvdata *dd = dev_get_drvdata(dev);
+
+ if (!dd)
+ return -EINVAL;
+ return dd->pmic_read_irq_stat(dev, irq);
+}
+
+#endif
diff --git a/include/linux/mfd/pm8xxx/irq.h b/include/linux/mfd/pm8xxx/irq.h
new file mode 100644
index 0000000..4b21769
--- /dev/null
+++ b/include/linux/mfd/pm8xxx/irq.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm PMIC irq 8xxx driver header file
+ *
+ */
+
+#ifndef __MFD_PM8XXX_IRQ_H
+#define __MFD_PM8XXX_IRQ_H
+
+#include <linux/errno.h>
+#include <linux/err.h>
+
+struct pm8xxx_irq_core_data {
+ u32 rev;
+ int nirqs;
+};
+
+struct pm8xxx_irq_platform_data {
+ int irq_base;
+ struct pm8xxx_irq_core_data irq_cdata;
+ int devirq;
+ int irq_trigger_flag;
+};
+
+struct pm_irq_chip;
+
+#ifdef CONFIG_MFD_PM8XXX_IRQ
+int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq);
+struct pm_irq_chip * __devinit pm8xxx_irq_init(struct device *dev,
+ const struct pm8xxx_irq_platform_data *pdata);
+int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip);
+#else
+static inline int pm8xxx_get_irq_stat(struct pm_irq_chip *chip, int irq)
+{
+ return -ENXIO;
+}
+static inline struct pm_irq_chip * __devinit pm8xxx_irq_init(
+ const struct device *dev,
+ const struct pm8xxx_irq_platform_data *pdata)
+{
+ return ERR_PTR(-ENXIO);
+}
+static inline int __devexit pm8xxx_irq_exit(struct pm_irq_chip *chip)
+{
+ return -ENXIO;
+}
+#endif /* CONFIG_MFD_PM8XXX_IRQ */
+#endif /* __MFD_PM8XXX_IRQ_H */
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h
new file mode 100644
index 0000000..d5517fd
--- /dev/null
+++ b/include/linux/mfd/pm8xxx/pm8921.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm PMIC 8921 driver header file
+ *
+ */
+
+#ifndef __MFD_PM8921_H
+#define __MFD_PM8921_H
+
+#include <linux/device.h>
+#include <linux/mfd/pm8xxx/irq.h>
+
+#define PM8921_NR_IRQS 256
+
+struct pm8921_platform_data {
+ int irq_base;
+ struct pm8xxx_irq_platform_data *irq_pdata;
+};
+
+#endif
diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h
new file mode 100644
index 0000000..8bb85b9
--- /dev/null
+++ b/include/linux/mfd/tps65910.h
@@ -0,0 +1,800 @@
+/*
+ * tps65910.h -- TI TPS6591x
+ *
+ * Copyright 2010-2011 Texas Instruments Inc.
+ *
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ * Author: Arnaud Deconinck <a-deconinck@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_TPS65910_H
+#define __LINUX_MFD_TPS65910_H
+
+/* TPS chip id list */
+#define TPS65910 0
+#define TPS65911 1
+
+/* TPS regulator type list */
+#define REGULATOR_LDO 0
+#define REGULATOR_DCDC 1
+
+/*
+ * List of registers for component TPS65910
+ *
+ */
+
+#define TPS65910_SECONDS 0x0
+#define TPS65910_MINUTES 0x1
+#define TPS65910_HOURS 0x2
+#define TPS65910_DAYS 0x3
+#define TPS65910_MONTHS 0x4
+#define TPS65910_YEARS 0x5
+#define TPS65910_WEEKS 0x6
+#define TPS65910_ALARM_SECONDS 0x8
+#define TPS65910_ALARM_MINUTES 0x9
+#define TPS65910_ALARM_HOURS 0xA
+#define TPS65910_ALARM_DAYS 0xB
+#define TPS65910_ALARM_MONTHS 0xC
+#define TPS65910_ALARM_YEARS 0xD
+#define TPS65910_RTC_CTRL 0x10
+#define TPS65910_RTC_STATUS 0x11
+#define TPS65910_RTC_INTERRUPTS 0x12
+#define TPS65910_RTC_COMP_LSB 0x13
+#define TPS65910_RTC_COMP_MSB 0x14
+#define TPS65910_RTC_RES_PROG 0x15
+#define TPS65910_RTC_RESET_STATUS 0x16
+#define TPS65910_BCK1 0x17
+#define TPS65910_BCK2 0x18
+#define TPS65910_BCK3 0x19
+#define TPS65910_BCK4 0x1A
+#define TPS65910_BCK5 0x1B
+#define TPS65910_PUADEN 0x1C
+#define TPS65910_REF 0x1D
+#define TPS65910_VRTC 0x1E
+#define TPS65910_VIO 0x20
+#define TPS65910_VDD1 0x21
+#define TPS65910_VDD1_OP 0x22
+#define TPS65910_VDD1_SR 0x23
+#define TPS65910_VDD2 0x24
+#define TPS65910_VDD2_OP 0x25
+#define TPS65910_VDD2_SR 0x26
+#define TPS65910_VDD3 0x27
+#define TPS65910_VDIG1 0x30
+#define TPS65910_VDIG2 0x31
+#define TPS65910_VAUX1 0x32
+#define TPS65910_VAUX2 0x33
+#define TPS65910_VAUX33 0x34
+#define TPS65910_VMMC 0x35
+#define TPS65910_VPLL 0x36
+#define TPS65910_VDAC 0x37
+#define TPS65910_THERM 0x38
+#define TPS65910_BBCH 0x39
+#define TPS65910_DCDCCTRL 0x3E
+#define TPS65910_DEVCTRL 0x3F
+#define TPS65910_DEVCTRL2 0x40
+#define TPS65910_SLEEP_KEEP_LDO_ON 0x41
+#define TPS65910_SLEEP_KEEP_RES_ON 0x42
+#define TPS65910_SLEEP_SET_LDO_OFF 0x43
+#define TPS65910_SLEEP_SET_RES_OFF 0x44
+#define TPS65910_EN1_LDO_ASS 0x45
+#define TPS65910_EN1_SMPS_ASS 0x46
+#define TPS65910_EN2_LDO_ASS 0x47
+#define TPS65910_EN2_SMPS_ASS 0x48
+#define TPS65910_EN3_LDO_ASS 0x49
+#define TPS65910_SPARE 0x4A
+#define TPS65910_INT_STS 0x50
+#define TPS65910_INT_MSK 0x51
+#define TPS65910_INT_STS2 0x52
+#define TPS65910_INT_MSK2 0x53
+#define TPS65910_INT_STS3 0x54
+#define TPS65910_INT_MSK3 0x55
+#define TPS65910_GPIO0 0x60
+#define TPS65910_GPIO1 0x61
+#define TPS65910_GPIO2 0x62
+#define TPS65910_GPIO3 0x63
+#define TPS65910_GPIO4 0x64
+#define TPS65910_GPIO5 0x65
+#define TPS65910_GPIO6 0x66
+#define TPS65910_GPIO7 0x67
+#define TPS65910_GPIO8 0x68
+#define TPS65910_JTAGVERNUM 0x80
+#define TPS65910_MAX_REGISTER 0x80
+
+/*
+ * List of registers specific to TPS65911
+ */
+#define TPS65911_VDDCTRL 0x27
+#define TPS65911_VDDCTRL_OP 0x28
+#define TPS65911_VDDCTRL_SR 0x29
+#define TPS65911_LDO1 0x30
+#define TPS65911_LDO2 0x31
+#define TPS65911_LDO5 0x32
+#define TPS65911_LDO8 0x33
+#define TPS65911_LDO7 0x34
+#define TPS65911_LDO6 0x35
+#define TPS65911_LDO4 0x36
+#define TPS65911_LDO3 0x37
+#define TPS65911_VMBCH 0x6A
+#define TPS65911_VMBCH2 0x6B
+
+/*
+ * List of register bitfields for component TPS65910
+ *
+ */
+
+
+/*Register BCK1 (0x80) register.RegisterDescription */
+#define BCK1_BCKUP_MASK 0xFF
+#define BCK1_BCKUP_SHIFT 0
+
+
+/*Register BCK2 (0x80) register.RegisterDescription */
+#define BCK2_BCKUP_MASK 0xFF
+#define BCK2_BCKUP_SHIFT 0
+
+
+/*Register BCK3 (0x80) register.RegisterDescription */
+#define BCK3_BCKUP_MASK 0xFF
+#define BCK3_BCKUP_SHIFT 0
+
+
+/*Register BCK4 (0x80) register.RegisterDescription */
+#define BCK4_BCKUP_MASK 0xFF
+#define BCK4_BCKUP_SHIFT 0
+
+
+/*Register BCK5 (0x80) register.RegisterDescription */
+#define BCK5_BCKUP_MASK 0xFF
+#define BCK5_BCKUP_SHIFT 0
+
+
+/*Register PUADEN (0x80) register.RegisterDescription */
+#define PUADEN_EN3P_MASK 0x80
+#define PUADEN_EN3P_SHIFT 7
+#define PUADEN_I2CCTLP_MASK 0x40
+#define PUADEN_I2CCTLP_SHIFT 6
+#define PUADEN_I2CSRP_MASK 0x20
+#define PUADEN_I2CSRP_SHIFT 5
+#define PUADEN_PWRONP_MASK 0x10
+#define PUADEN_PWRONP_SHIFT 4
+#define PUADEN_SLEEPP_MASK 0x08
+#define PUADEN_SLEEPP_SHIFT 3
+#define PUADEN_PWRHOLDP_MASK 0x04
+#define PUADEN_PWRHOLDP_SHIFT 2
+#define PUADEN_BOOT1P_MASK 0x02
+#define PUADEN_BOOT1P_SHIFT 1
+#define PUADEN_BOOT0P_MASK 0x01
+#define PUADEN_BOOT0P_SHIFT 0
+
+
+/*Register REF (0x80) register.RegisterDescription */
+#define REF_VMBCH_SEL_MASK 0x0C
+#define REF_VMBCH_SEL_SHIFT 2
+#define REF_ST_MASK 0x03
+#define REF_ST_SHIFT 0
+
+
+/*Register VRTC (0x80) register.RegisterDescription */
+#define VRTC_VRTC_OFFMASK_MASK 0x08
+#define VRTC_VRTC_OFFMASK_SHIFT 3
+#define VRTC_ST_MASK 0x03
+#define VRTC_ST_SHIFT 0
+
+
+/*Register VIO (0x80) register.RegisterDescription */
+#define VIO_ILMAX_MASK 0xC0
+#define VIO_ILMAX_SHIFT 6
+#define VIO_SEL_MASK 0x0C
+#define VIO_SEL_SHIFT 2
+#define VIO_ST_MASK 0x03
+#define VIO_ST_SHIFT 0
+
+
+/*Register VDD1 (0x80) register.RegisterDescription */
+#define VDD1_VGAIN_SEL_MASK 0xC0
+#define VDD1_VGAIN_SEL_SHIFT 6
+#define VDD1_ILMAX_MASK 0x20
+#define VDD1_ILMAX_SHIFT 5
+#define VDD1_TSTEP_MASK 0x1C
+#define VDD1_TSTEP_SHIFT 2
+#define VDD1_ST_MASK 0x03
+#define VDD1_ST_SHIFT 0
+
+
+/*Register VDD1_OP (0x80) register.RegisterDescription */
+#define VDD1_OP_CMD_MASK 0x80
+#define VDD1_OP_CMD_SHIFT 7
+#define VDD1_OP_SEL_MASK 0x7F
+#define VDD1_OP_SEL_SHIFT 0
+
+
+/*Register VDD1_SR (0x80) register.RegisterDescription */
+#define VDD1_SR_SEL_MASK 0x7F
+#define VDD1_SR_SEL_SHIFT 0
+
+
+/*Register VDD2 (0x80) register.RegisterDescription */
+#define VDD2_VGAIN_SEL_MASK 0xC0
+#define VDD2_VGAIN_SEL_SHIFT 6
+#define VDD2_ILMAX_MASK 0x20
+#define VDD2_ILMAX_SHIFT 5
+#define VDD2_TSTEP_MASK 0x1C
+#define VDD2_TSTEP_SHIFT 2
+#define VDD2_ST_MASK 0x03
+#define VDD2_ST_SHIFT 0
+
+
+/*Register VDD2_OP (0x80) register.RegisterDescription */
+#define VDD2_OP_CMD_MASK 0x80
+#define VDD2_OP_CMD_SHIFT 7
+#define VDD2_OP_SEL_MASK 0x7F
+#define VDD2_OP_SEL_SHIFT 0
+
+/*Register VDD2_SR (0x80) register.RegisterDescription */
+#define VDD2_SR_SEL_MASK 0x7F
+#define VDD2_SR_SEL_SHIFT 0
+
+
+/*Registers VDD1, VDD2 voltage values definitions */
+#define VDD1_2_NUM_VOLTS 73
+#define VDD1_2_MIN_VOLT 6000
+#define VDD1_2_OFFSET 125
+
+
+/*Register VDD3 (0x80) register.RegisterDescription */
+#define VDD3_CKINEN_MASK 0x04
+#define VDD3_CKINEN_SHIFT 2
+#define VDD3_ST_MASK 0x03
+#define VDD3_ST_SHIFT 0
+#define VDDCTRL_MIN_VOLT 6000
+#define VDDCTRL_OFFSET 125
+
+/*Registers VDIG (0x80) to VDAC register.RegisterDescription */
+#define LDO_SEL_MASK 0x0C
+#define LDO_SEL_SHIFT 2
+#define LDO_ST_MASK 0x03
+#define LDO_ST_SHIFT 0
+#define LDO_ST_ON_BIT 0x01
+#define LDO_ST_MODE_BIT 0x02
+
+
+/* Registers LDO1 to LDO8 in tps65910 */
+#define LDO1_SEL_MASK 0xFC
+#define LDO3_SEL_MASK 0x7C
+#define LDO_MIN_VOLT 1000
+#define LDO_MAX_VOLT 3300;
+
+
+/*Register VDIG1 (0x80) register.RegisterDescription */
+#define VDIG1_SEL_MASK 0x0C
+#define VDIG1_SEL_SHIFT 2
+#define VDIG1_ST_MASK 0x03
+#define VDIG1_ST_SHIFT 0
+
+
+/*Register VDIG2 (0x80) register.RegisterDescription */
+#define VDIG2_SEL_MASK 0x0C
+#define VDIG2_SEL_SHIFT 2
+#define VDIG2_ST_MASK 0x03
+#define VDIG2_ST_SHIFT 0
+
+
+/*Register VAUX1 (0x80) register.RegisterDescription */
+#define VAUX1_SEL_MASK 0x0C
+#define VAUX1_SEL_SHIFT 2
+#define VAUX1_ST_MASK 0x03
+#define VAUX1_ST_SHIFT 0
+
+
+/*Register VAUX2 (0x80) register.RegisterDescription */
+#define VAUX2_SEL_MASK 0x0C
+#define VAUX2_SEL_SHIFT 2
+#define VAUX2_ST_MASK 0x03
+#define VAUX2_ST_SHIFT 0
+
+
+/*Register VAUX33 (0x80) register.RegisterDescription */
+#define VAUX33_SEL_MASK 0x0C
+#define VAUX33_SEL_SHIFT 2
+#define VAUX33_ST_MASK 0x03
+#define VAUX33_ST_SHIFT 0
+
+
+/*Register VMMC (0x80) register.RegisterDescription */
+#define VMMC_SEL_MASK 0x0C
+#define VMMC_SEL_SHIFT 2
+#define VMMC_ST_MASK 0x03
+#define VMMC_ST_SHIFT 0
+
+
+/*Register VPLL (0x80) register.RegisterDescription */
+#define VPLL_SEL_MASK 0x0C
+#define VPLL_SEL_SHIFT 2
+#define VPLL_ST_MASK 0x03
+#define VPLL_ST_SHIFT 0
+
+
+/*Register VDAC (0x80) register.RegisterDescription */
+#define VDAC_SEL_MASK 0x0C
+#define VDAC_SEL_SHIFT 2
+#define VDAC_ST_MASK 0x03
+#define VDAC_ST_SHIFT 0
+
+
+/*Register THERM (0x80) register.RegisterDescription */
+#define THERM_THERM_HD_MASK 0x20
+#define THERM_THERM_HD_SHIFT 5
+#define THERM_THERM_TS_MASK 0x10
+#define THERM_THERM_TS_SHIFT 4
+#define THERM_THERM_HDSEL_MASK 0x0C
+#define THERM_THERM_HDSEL_SHIFT 2
+#define THERM_RSVD1_MASK 0x02
+#define THERM_RSVD1_SHIFT 1
+#define THERM_THERM_STATE_MASK 0x01
+#define THERM_THERM_STATE_SHIFT 0
+
+
+/*Register BBCH (0x80) register.RegisterDescription */
+#define BBCH_BBSEL_MASK 0x06
+#define BBCH_BBSEL_SHIFT 1
+#define BBCH_BBCHEN_MASK 0x01
+#define BBCH_BBCHEN_SHIFT 0
+
+
+/*Register DCDCCTRL (0x80) register.RegisterDescription */
+#define DCDCCTRL_VDD2_PSKIP_MASK 0x20
+#define DCDCCTRL_VDD2_PSKIP_SHIFT 5
+#define DCDCCTRL_VDD1_PSKIP_MASK 0x10
+#define DCDCCTRL_VDD1_PSKIP_SHIFT 4
+#define DCDCCTRL_VIO_PSKIP_MASK 0x08
+#define DCDCCTRL_VIO_PSKIP_SHIFT 3
+#define DCDCCTRL_DCDCCKEXT_MASK 0x04
+#define DCDCCTRL_DCDCCKEXT_SHIFT 2
+#define DCDCCTRL_DCDCCKSYNC_MASK 0x03
+#define DCDCCTRL_DCDCCKSYNC_SHIFT 0
+
+
+/*Register DEVCTRL (0x80) register.RegisterDescription */
+#define DEVCTRL_RTC_PWDN_MASK 0x40
+#define DEVCTRL_RTC_PWDN_SHIFT 6
+#define DEVCTRL_CK32K_CTRL_MASK 0x20
+#define DEVCTRL_CK32K_CTRL_SHIFT 5
+#define DEVCTRL_SR_CTL_I2C_SEL_MASK 0x10
+#define DEVCTRL_SR_CTL_I2C_SEL_SHIFT 4
+#define DEVCTRL_DEV_OFF_RST_MASK 0x08
+#define DEVCTRL_DEV_OFF_RST_SHIFT 3
+#define DEVCTRL_DEV_ON_MASK 0x04
+#define DEVCTRL_DEV_ON_SHIFT 2
+#define DEVCTRL_DEV_SLP_MASK 0x02
+#define DEVCTRL_DEV_SLP_SHIFT 1
+#define DEVCTRL_DEV_OFF_MASK 0x01
+#define DEVCTRL_DEV_OFF_SHIFT 0
+
+
+/*Register DEVCTRL2 (0x80) register.RegisterDescription */
+#define DEVCTRL2_TSLOT_LENGTH_MASK 0x30
+#define DEVCTRL2_TSLOT_LENGTH_SHIFT 4
+#define DEVCTRL2_SLEEPSIG_POL_MASK 0x08
+#define DEVCTRL2_SLEEPSIG_POL_SHIFT 3
+#define DEVCTRL2_PWON_LP_OFF_MASK 0x04
+#define DEVCTRL2_PWON_LP_OFF_SHIFT 2
+#define DEVCTRL2_PWON_LP_RST_MASK 0x02
+#define DEVCTRL2_PWON_LP_RST_SHIFT 1
+#define DEVCTRL2_IT_POL_MASK 0x01
+#define DEVCTRL2_IT_POL_SHIFT 0
+
+
+/*Register SLEEP_KEEP_LDO_ON (0x80) register.RegisterDescription */
+#define SLEEP_KEEP_LDO_ON_VDAC_KEEPON_MASK 0x80
+#define SLEEP_KEEP_LDO_ON_VDAC_KEEPON_SHIFT 7
+#define SLEEP_KEEP_LDO_ON_VPLL_KEEPON_MASK 0x40
+#define SLEEP_KEEP_LDO_ON_VPLL_KEEPON_SHIFT 6
+#define SLEEP_KEEP_LDO_ON_VAUX33_KEEPON_MASK 0x20
+#define SLEEP_KEEP_LDO_ON_VAUX33_KEEPON_SHIFT 5
+#define SLEEP_KEEP_LDO_ON_VAUX2_KEEPON_MASK 0x10
+#define SLEEP_KEEP_LDO_ON_VAUX2_KEEPON_SHIFT 4
+#define SLEEP_KEEP_LDO_ON_VAUX1_KEEPON_MASK 0x08
+#define SLEEP_KEEP_LDO_ON_VAUX1_KEEPON_SHIFT 3
+#define SLEEP_KEEP_LDO_ON_VDIG2_KEEPON_MASK 0x04
+#define SLEEP_KEEP_LDO_ON_VDIG2_KEEPON_SHIFT 2
+#define SLEEP_KEEP_LDO_ON_VDIG1_KEEPON_MASK 0x02
+#define SLEEP_KEEP_LDO_ON_VDIG1_KEEPON_SHIFT 1
+#define SLEEP_KEEP_LDO_ON_VMMC_KEEPON_MASK 0x01
+#define SLEEP_KEEP_LDO_ON_VMMC_KEEPON_SHIFT 0
+
+
+/*Register SLEEP_KEEP_RES_ON (0x80) register.RegisterDescription */
+#define SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK 0x80
+#define SLEEP_KEEP_RES_ON_THERM_KEEPON_SHIFT 7
+#define SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK 0x40
+#define SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_SHIFT 6
+#define SLEEP_KEEP_RES_ON_VRTC_KEEPON_MASK 0x20
+#define SLEEP_KEEP_RES_ON_VRTC_KEEPON_SHIFT 5
+#define SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK 0x10
+#define SLEEP_KEEP_RES_ON_I2CHS_KEEPON_SHIFT 4
+#define SLEEP_KEEP_RES_ON_VDD3_KEEPON_MASK 0x08
+#define SLEEP_KEEP_RES_ON_VDD3_KEEPON_SHIFT 3
+#define SLEEP_KEEP_RES_ON_VDD2_KEEPON_MASK 0x04
+#define SLEEP_KEEP_RES_ON_VDD2_KEEPON_SHIFT 2
+#define SLEEP_KEEP_RES_ON_VDD1_KEEPON_MASK 0x02
+#define SLEEP_KEEP_RES_ON_VDD1_KEEPON_SHIFT 1
+#define SLEEP_KEEP_RES_ON_VIO_KEEPON_MASK 0x01
+#define SLEEP_KEEP_RES_ON_VIO_KEEPON_SHIFT 0
+
+
+/*Register SLEEP_SET_LDO_OFF (0x80) register.RegisterDescription */
+#define SLEEP_SET_LDO_OFF_VDAC_SETOFF_MASK 0x80
+#define SLEEP_SET_LDO_OFF_VDAC_SETOFF_SHIFT 7
+#define SLEEP_SET_LDO_OFF_VPLL_SETOFF_MASK 0x40
+#define SLEEP_SET_LDO_OFF_VPLL_SETOFF_SHIFT 6
+#define SLEEP_SET_LDO_OFF_VAUX33_SETOFF_MASK 0x20
+#define SLEEP_SET_LDO_OFF_VAUX33_SETOFF_SHIFT 5
+#define SLEEP_SET_LDO_OFF_VAUX2_SETOFF_MASK 0x10
+#define SLEEP_SET_LDO_OFF_VAUX2_SETOFF_SHIFT 4
+#define SLEEP_SET_LDO_OFF_VAUX1_SETOFF_MASK 0x08
+#define SLEEP_SET_LDO_OFF_VAUX1_SETOFF_SHIFT 3
+#define SLEEP_SET_LDO_OFF_VDIG2_SETOFF_MASK 0x04
+#define SLEEP_SET_LDO_OFF_VDIG2_SETOFF_SHIFT 2
+#define SLEEP_SET_LDO_OFF_VDIG1_SETOFF_MASK 0x02
+#define SLEEP_SET_LDO_OFF_VDIG1_SETOFF_SHIFT 1
+#define SLEEP_SET_LDO_OFF_VMMC_SETOFF_MASK 0x01
+#define SLEEP_SET_LDO_OFF_VMMC_SETOFF_SHIFT 0
+
+
+/*Register SLEEP_SET_RES_OFF (0x80) register.RegisterDescription */
+#define SLEEP_SET_RES_OFF_DEFAULT_VOLT_MASK 0x80
+#define SLEEP_SET_RES_OFF_DEFAULT_VOLT_SHIFT 7
+#define SLEEP_SET_RES_OFF_RSVD_MASK 0x60
+#define SLEEP_SET_RES_OFF_RSVD_SHIFT 5
+#define SLEEP_SET_RES_OFF_SPARE_SETOFF_MASK 0x10
+#define SLEEP_SET_RES_OFF_SPARE_SETOFF_SHIFT 4
+#define SLEEP_SET_RES_OFF_VDD3_SETOFF_MASK 0x08
+#define SLEEP_SET_RES_OFF_VDD3_SETOFF_SHIFT 3
+#define SLEEP_SET_RES_OFF_VDD2_SETOFF_MASK 0x04
+#define SLEEP_SET_RES_OFF_VDD2_SETOFF_SHIFT 2
+#define SLEEP_SET_RES_OFF_VDD1_SETOFF_MASK 0x02
+#define SLEEP_SET_RES_OFF_VDD1_SETOFF_SHIFT 1
+#define SLEEP_SET_RES_OFF_VIO_SETOFF_MASK 0x01
+#define SLEEP_SET_RES_OFF_VIO_SETOFF_SHIFT 0
+
+
+/*Register EN1_LDO_ASS (0x80) register.RegisterDescription */
+#define EN1_LDO_ASS_VDAC_EN1_MASK 0x80
+#define EN1_LDO_ASS_VDAC_EN1_SHIFT 7
+#define EN1_LDO_ASS_VPLL_EN1_MASK 0x40
+#define EN1_LDO_ASS_VPLL_EN1_SHIFT 6
+#define EN1_LDO_ASS_VAUX33_EN1_MASK 0x20
+#define EN1_LDO_ASS_VAUX33_EN1_SHIFT 5
+#define EN1_LDO_ASS_VAUX2_EN1_MASK 0x10
+#define EN1_LDO_ASS_VAUX2_EN1_SHIFT 4
+#define EN1_LDO_ASS_VAUX1_EN1_MASK 0x08
+#define EN1_LDO_ASS_VAUX1_EN1_SHIFT 3
+#define EN1_LDO_ASS_VDIG2_EN1_MASK 0x04
+#define EN1_LDO_ASS_VDIG2_EN1_SHIFT 2
+#define EN1_LDO_ASS_VDIG1_EN1_MASK 0x02
+#define EN1_LDO_ASS_VDIG1_EN1_SHIFT 1
+#define EN1_LDO_ASS_VMMC_EN1_MASK 0x01
+#define EN1_LDO_ASS_VMMC_EN1_SHIFT 0
+
+
+/*Register EN1_SMPS_ASS (0x80) register.RegisterDescription */
+#define EN1_SMPS_ASS_RSVD_MASK 0xE0
+#define EN1_SMPS_ASS_RSVD_SHIFT 5
+#define EN1_SMPS_ASS_SPARE_EN1_MASK 0x10
+#define EN1_SMPS_ASS_SPARE_EN1_SHIFT 4
+#define EN1_SMPS_ASS_VDD3_EN1_MASK 0x08
+#define EN1_SMPS_ASS_VDD3_EN1_SHIFT 3
+#define EN1_SMPS_ASS_VDD2_EN1_MASK 0x04
+#define EN1_SMPS_ASS_VDD2_EN1_SHIFT 2
+#define EN1_SMPS_ASS_VDD1_EN1_MASK 0x02
+#define EN1_SMPS_ASS_VDD1_EN1_SHIFT 1
+#define EN1_SMPS_ASS_VIO_EN1_MASK 0x01
+#define EN1_SMPS_ASS_VIO_EN1_SHIFT 0
+
+
+/*Register EN2_LDO_ASS (0x80) register.RegisterDescription */
+#define EN2_LDO_ASS_VDAC_EN2_MASK 0x80
+#define EN2_LDO_ASS_VDAC_EN2_SHIFT 7
+#define EN2_LDO_ASS_VPLL_EN2_MASK 0x40
+#define EN2_LDO_ASS_VPLL_EN2_SHIFT 6
+#define EN2_LDO_ASS_VAUX33_EN2_MASK 0x20
+#define EN2_LDO_ASS_VAUX33_EN2_SHIFT 5
+#define EN2_LDO_ASS_VAUX2_EN2_MASK 0x10
+#define EN2_LDO_ASS_VAUX2_EN2_SHIFT 4
+#define EN2_LDO_ASS_VAUX1_EN2_MASK 0x08
+#define EN2_LDO_ASS_VAUX1_EN2_SHIFT 3
+#define EN2_LDO_ASS_VDIG2_EN2_MASK 0x04
+#define EN2_LDO_ASS_VDIG2_EN2_SHIFT 2
+#define EN2_LDO_ASS_VDIG1_EN2_MASK 0x02
+#define EN2_LDO_ASS_VDIG1_EN2_SHIFT 1
+#define EN2_LDO_ASS_VMMC_EN2_MASK 0x01
+#define EN2_LDO_ASS_VMMC_EN2_SHIFT 0
+
+
+/*Register EN2_SMPS_ASS (0x80) register.RegisterDescription */
+#define EN2_SMPS_ASS_RSVD_MASK 0xE0
+#define EN2_SMPS_ASS_RSVD_SHIFT 5
+#define EN2_SMPS_ASS_SPARE_EN2_MASK 0x10
+#define EN2_SMPS_ASS_SPARE_EN2_SHIFT 4
+#define EN2_SMPS_ASS_VDD3_EN2_MASK 0x08
+#define EN2_SMPS_ASS_VDD3_EN2_SHIFT 3
+#define EN2_SMPS_ASS_VDD2_EN2_MASK 0x04
+#define EN2_SMPS_ASS_VDD2_EN2_SHIFT 2
+#define EN2_SMPS_ASS_VDD1_EN2_MASK 0x02
+#define EN2_SMPS_ASS_VDD1_EN2_SHIFT 1
+#define EN2_SMPS_ASS_VIO_EN2_MASK 0x01
+#define EN2_SMPS_ASS_VIO_EN2_SHIFT 0
+
+
+/*Register EN3_LDO_ASS (0x80) register.RegisterDescription */
+#define EN3_LDO_ASS_VDAC_EN3_MASK 0x80
+#define EN3_LDO_ASS_VDAC_EN3_SHIFT 7
+#define EN3_LDO_ASS_VPLL_EN3_MASK 0x40
+#define EN3_LDO_ASS_VPLL_EN3_SHIFT 6
+#define EN3_LDO_ASS_VAUX33_EN3_MASK 0x20
+#define EN3_LDO_ASS_VAUX33_EN3_SHIFT 5
+#define EN3_LDO_ASS_VAUX2_EN3_MASK 0x10
+#define EN3_LDO_ASS_VAUX2_EN3_SHIFT 4
+#define EN3_LDO_ASS_VAUX1_EN3_MASK 0x08
+#define EN3_LDO_ASS_VAUX1_EN3_SHIFT 3
+#define EN3_LDO_ASS_VDIG2_EN3_MASK 0x04
+#define EN3_LDO_ASS_VDIG2_EN3_SHIFT 2
+#define EN3_LDO_ASS_VDIG1_EN3_MASK 0x02
+#define EN3_LDO_ASS_VDIG1_EN3_SHIFT 1
+#define EN3_LDO_ASS_VMMC_EN3_MASK 0x01
+#define EN3_LDO_ASS_VMMC_EN3_SHIFT 0
+
+
+/*Register SPARE (0x80) register.RegisterDescription */
+#define SPARE_SPARE_MASK 0xFF
+#define SPARE_SPARE_SHIFT 0
+
+
+/*Register INT_STS (0x80) register.RegisterDescription */
+#define INT_STS_RTC_PERIOD_IT_MASK 0x80
+#define INT_STS_RTC_PERIOD_IT_SHIFT 7
+#define INT_STS_RTC_ALARM_IT_MASK 0x40
+#define INT_STS_RTC_ALARM_IT_SHIFT 6
+#define INT_STS_HOTDIE_IT_MASK 0x20
+#define INT_STS_HOTDIE_IT_SHIFT 5
+#define INT_STS_PWRHOLD_IT_MASK 0x10
+#define INT_STS_PWRHOLD_IT_SHIFT 4
+#define INT_STS_PWRON_LP_IT_MASK 0x08
+#define INT_STS_PWRON_LP_IT_SHIFT 3
+#define INT_STS_PWRON_IT_MASK 0x04
+#define INT_STS_PWRON_IT_SHIFT 2
+#define INT_STS_VMBHI_IT_MASK 0x02
+#define INT_STS_VMBHI_IT_SHIFT 1
+#define INT_STS_VMBDCH_IT_MASK 0x01
+#define INT_STS_VMBDCH_IT_SHIFT 0
+
+
+/*Register INT_MSK (0x80) register.RegisterDescription */
+#define INT_MSK_RTC_PERIOD_IT_MSK_MASK 0x80
+#define INT_MSK_RTC_PERIOD_IT_MSK_SHIFT 7
+#define INT_MSK_RTC_ALARM_IT_MSK_MASK 0x40
+#define INT_MSK_RTC_ALARM_IT_MSK_SHIFT 6
+#define INT_MSK_HOTDIE_IT_MSK_MASK 0x20
+#define INT_MSK_HOTDIE_IT_MSK_SHIFT 5
+#define INT_MSK_PWRHOLD_IT_MSK_MASK 0x10
+#define INT_MSK_PWRHOLD_IT_MSK_SHIFT 4
+#define INT_MSK_PWRON_LP_IT_MSK_MASK 0x08
+#define INT_MSK_PWRON_LP_IT_MSK_SHIFT 3
+#define INT_MSK_PWRON_IT_MSK_MASK 0x04
+#define INT_MSK_PWRON_IT_MSK_SHIFT 2
+#define INT_MSK_VMBHI_IT_MSK_MASK 0x02
+#define INT_MSK_VMBHI_IT_MSK_SHIFT 1
+#define INT_MSK_VMBDCH_IT_MSK_MASK 0x01
+#define INT_MSK_VMBDCH_IT_MSK_SHIFT 0
+
+
+/*Register INT_STS2 (0x80) register.RegisterDescription */
+#define INT_STS2_GPIO3_F_IT_MASK 0x80
+#define INT_STS2_GPIO3_F_IT_SHIFT 7
+#define INT_STS2_GPIO3_R_IT_MASK 0x40
+#define INT_STS2_GPIO3_R_IT_SHIFT 6
+#define INT_STS2_GPIO2_F_IT_MASK 0x20
+#define INT_STS2_GPIO2_F_IT_SHIFT 5
+#define INT_STS2_GPIO2_R_IT_MASK 0x10
+#define INT_STS2_GPIO2_R_IT_SHIFT 4
+#define INT_STS2_GPIO1_F_IT_MASK 0x08
+#define INT_STS2_GPIO1_F_IT_SHIFT 3
+#define INT_STS2_GPIO1_R_IT_MASK 0x04
+#define INT_STS2_GPIO1_R_IT_SHIFT 2
+#define INT_STS2_GPIO0_F_IT_MASK 0x02
+#define INT_STS2_GPIO0_F_IT_SHIFT 1
+#define INT_STS2_GPIO0_R_IT_MASK 0x01
+#define INT_STS2_GPIO0_R_IT_SHIFT 0
+
+
+/*Register INT_MSK2 (0x80) register.RegisterDescription */
+#define INT_MSK2_GPIO3_F_IT_MSK_MASK 0x80
+#define INT_MSK2_GPIO3_F_IT_MSK_SHIFT 7
+#define INT_MSK2_GPIO3_R_IT_MSK_MASK 0x40
+#define INT_MSK2_GPIO3_R_IT_MSK_SHIFT 6
+#define INT_MSK2_GPIO2_F_IT_MSK_MASK 0x20
+#define INT_MSK2_GPIO2_F_IT_MSK_SHIFT 5
+#define INT_MSK2_GPIO2_R_IT_MSK_MASK 0x10
+#define INT_MSK2_GPIO2_R_IT_MSK_SHIFT 4
+#define INT_MSK2_GPIO1_F_IT_MSK_MASK 0x08
+#define INT_MSK2_GPIO1_F_IT_MSK_SHIFT 3
+#define INT_MSK2_GPIO1_R_IT_MSK_MASK 0x04
+#define INT_MSK2_GPIO1_R_IT_MSK_SHIFT 2
+#define INT_MSK2_GPIO0_F_IT_MSK_MASK 0x02
+#define INT_MSK2_GPIO0_F_IT_MSK_SHIFT 1
+#define INT_MSK2_GPIO0_R_IT_MSK_MASK 0x01
+#define INT_MSK2_GPIO0_R_IT_MSK_SHIFT 0
+
+
+/*Register INT_STS3 (0x80) register.RegisterDescription */
+#define INT_STS3_GPIO5_F_IT_MASK 0x08
+#define INT_STS3_GPIO5_F_IT_SHIFT 3
+#define INT_STS3_GPIO5_R_IT_MASK 0x04
+#define INT_STS3_GPIO5_R_IT_SHIFT 2
+#define INT_STS3_GPIO4_F_IT_MASK 0x02
+#define INT_STS3_GPIO4_F_IT_SHIFT 1
+#define INT_STS3_GPIO4_R_IT_MASK 0x01
+#define INT_STS3_GPIO4_R_IT_SHIFT 0
+
+
+/*Register INT_MSK3 (0x80) register.RegisterDescription */
+#define INT_MSK3_GPIO5_F_IT_MSK_MASK 0x08
+#define INT_MSK3_GPIO5_F_IT_MSK_SHIFT 3
+#define INT_MSK3_GPIO5_R_IT_MSK_MASK 0x04
+#define INT_MSK3_GPIO5_R_IT_MSK_SHIFT 2
+#define INT_MSK3_GPIO4_F_IT_MSK_MASK 0x02
+#define INT_MSK3_GPIO4_F_IT_MSK_SHIFT 1
+#define INT_MSK3_GPIO4_R_IT_MSK_MASK 0x01
+#define INT_MSK3_GPIO4_R_IT_MSK_SHIFT 0
+
+
+/*Register GPIO (0x80) register.RegisterDescription */
+#define GPIO_DEB_MASK 0x10
+#define GPIO_DEB_SHIFT 4
+#define GPIO_PUEN_MASK 0x08
+#define GPIO_PUEN_SHIFT 3
+#define GPIO_CFG_MASK 0x04
+#define GPIO_CFG_SHIFT 2
+#define GPIO_STS_MASK 0x02
+#define GPIO_STS_SHIFT 1
+#define GPIO_SET_MASK 0x01
+#define GPIO_SET_SHIFT 0
+
+
+/*Register JTAGVERNUM (0x80) register.RegisterDescription */
+#define JTAGVERNUM_VERNUM_MASK 0x0F
+#define JTAGVERNUM_VERNUM_SHIFT 0
+
+
+/* Register VDDCTRL (0x27) bit definitions */
+#define VDDCTRL_ST_MASK 0x03
+#define VDDCTRL_ST_SHIFT 0
+
+
+/*Register VDDCTRL_OP (0x28) bit definitios */
+#define VDDCTRL_OP_CMD_MASK 0x80
+#define VDDCTRL_OP_CMD_SHIFT 7
+#define VDDCTRL_OP_SEL_MASK 0x7F
+#define VDDCTRL_OP_SEL_SHIFT 0
+
+
+/*Register VDDCTRL_SR (0x29) bit definitions */
+#define VDDCTRL_SR_SEL_MASK 0x7F
+#define VDDCTRL_SR_SEL_SHIFT 0
+
+
+/* IRQ Definitions */
+#define TPS65910_IRQ_VBAT_VMBDCH 0
+#define TPS65910_IRQ_VBAT_VMHI 1
+#define TPS65910_IRQ_PWRON 2
+#define TPS65910_IRQ_PWRON_LP 3
+#define TPS65910_IRQ_PWRHOLD 4
+#define TPS65910_IRQ_HOTDIE 5
+#define TPS65910_IRQ_RTC_ALARM 6
+#define TPS65910_IRQ_RTC_PERIOD 7
+#define TPS65910_IRQ_GPIO_R 8
+#define TPS65910_IRQ_GPIO_F 9
+#define TPS65910_NUM_IRQ 10
+
+#define TPS65911_IRQ_VBAT_VMBDCH 0
+#define TPS65911_IRQ_VBAT_VMBDCH2L 1
+#define TPS65911_IRQ_VBAT_VMBDCH2H 2
+#define TPS65911_IRQ_VBAT_VMHI 3
+#define TPS65911_IRQ_PWRON 4
+#define TPS65911_IRQ_PWRON_LP 5
+#define TPS65911_IRQ_PWRHOLD_F 6
+#define TPS65911_IRQ_PWRHOLD_R 7
+#define TPS65911_IRQ_HOTDIE 8
+#define TPS65911_IRQ_RTC_ALARM 9
+#define TPS65911_IRQ_RTC_PERIOD 10
+#define TPS65911_IRQ_GPIO0_R 11
+#define TPS65911_IRQ_GPIO0_F 12
+#define TPS65911_IRQ_GPIO1_R 13
+#define TPS65911_IRQ_GPIO1_F 14
+#define TPS65911_IRQ_GPIO2_R 15
+#define TPS65911_IRQ_GPIO2_F 16
+#define TPS65911_IRQ_GPIO3_R 17
+#define TPS65911_IRQ_GPIO3_F 18
+#define TPS65911_IRQ_GPIO4_R 19
+#define TPS65911_IRQ_GPIO4_F 20
+#define TPS65911_IRQ_GPIO5_R 21
+#define TPS65911_IRQ_GPIO5_F 22
+#define TPS65911_IRQ_WTCHDG 23
+#define TPS65911_IRQ_PWRDN 24
+
+#define TPS65911_NUM_IRQ 25
+
+
+/* GPIO Register Definitions */
+#define TPS65910_GPIO_DEB BIT(2)
+#define TPS65910_GPIO_PUEN BIT(3)
+#define TPS65910_GPIO_CFG BIT(2)
+#define TPS65910_GPIO_STS BIT(1)
+#define TPS65910_GPIO_SET BIT(0)
+
+/**
+ * struct tps65910_board
+ * Board platform data may be used to initialize regulators.
+ */
+
+struct tps65910_board {
+ int gpio_base;
+ int irq;
+ int irq_base;
+ int vmbch_threshold;
+ int vmbch2_threshold;
+ struct regulator_init_data *tps65910_pmic_init_data;
+};
+
+/**
+ * struct tps65910 - tps65910 sub-driver chip access routines
+ */
+
+struct tps65910 {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct mutex io_mutex;
+ unsigned int id;
+ int (*read)(struct tps65910 *tps65910, u8 reg, int size, void *dest);
+ int (*write)(struct tps65910 *tps65910, u8 reg, int size, void *src);
+
+ /* Client devices */
+ struct tps65910_pmic *pmic;
+ struct tps65910_rtc *rtc;
+ struct tps65910_power *power;
+
+ /* GPIO Handling */
+ struct gpio_chip gpio;
+
+ /* IRQ Handling */
+ struct mutex irq_lock;
+ int chip_irq;
+ int irq_base;
+ int irq_num;
+ u32 irq_mask;
+};
+
+struct tps65910_platform_data {
+ int irq;
+ int irq_base;
+};
+
+int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask);
+int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask);
+void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base);
+int tps65910_irq_init(struct tps65910 *tps65910, int irq,
+ struct tps65910_platform_data *pdata);
+
+static inline int tps65910_chip_id(struct tps65910 *tps65910)
+{
+ return tps65910->id;
+}
+
+#endif /* __LINUX_MFD_TPS65910_H */
diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h
index 2ec317c..5cc16bb 100644
--- a/include/linux/mfd/twl4030-codec.h
+++ b/include/linux/mfd/twl4030-codec.h
@@ -1,7 +1,7 @@
/*
* MFD driver for twl4030 codec submodule
*
- * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* Copyright: (C) 2009 Nokia Corporation
*
diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h
index 903280d..0d515ee 100644
--- a/include/linux/mfd/wm831x/core.h
+++ b/include/linux/mfd/wm831x/core.h
@@ -301,30 +301,4 @@
int wm831x_irq_init(struct wm831x *wm831x, int irq);
void wm831x_irq_exit(struct wm831x *wm831x);
-static inline int __must_check wm831x_request_irq(struct wm831x *wm831x,
- unsigned int irq,
- irq_handler_t handler,
- unsigned long flags,
- const char *name,
- void *dev)
-{
- return request_threaded_irq(irq, NULL, handler, flags, name, dev);
-}
-
-static inline void wm831x_free_irq(struct wm831x *wm831x,
- unsigned int irq, void *dev)
-{
- free_irq(irq, dev);
-}
-
-static inline void wm831x_disable_irq(struct wm831x *wm831x, int irq)
-{
- disable_irq(irq);
-}
-
-static inline void wm831x_enable_irq(struct wm831x *wm831x, int irq)
-{
- enable_irq(irq);
-}
-
#endif
diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h
index 632d156..ff42d70 100644
--- a/include/linux/mfd/wm831x/pdata.h
+++ b/include/linux/mfd/wm831x/pdata.h
@@ -105,6 +105,9 @@
#define WM831X_MAX_LDO 11
#define WM831X_MAX_ISINK 2
+#define WM831X_GPIO_CONFIGURE 0x10000
+#define WM831X_GPIO_NUM 16
+
struct wm831x_pdata {
/** Used to distinguish multiple WM831x chips */
int wm831x_num;
@@ -119,6 +122,7 @@
int irq_base;
int gpio_base;
+ int gpio_defaults[WM831X_GPIO_NUM];
struct wm831x_backlight_pdata *backlight;
struct wm831x_backup_pdata *backup;
struct wm831x_battery_pdata *battery;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 8eb969e..9670f71 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -165,12 +165,12 @@
*/
static inline int is_linear_pfn_mapping(struct vm_area_struct *vma)
{
- return (vma->vm_flags & VM_PFN_AT_MMAP);
+ return !!(vma->vm_flags & VM_PFN_AT_MMAP);
}
static inline int is_pfn_mapping(struct vm_area_struct *vma)
{
- return (vma->vm_flags & VM_PFNMAP);
+ return !!(vma->vm_flags & VM_PFNMAP);
}
/*
@@ -1408,17 +1408,11 @@
extern int mm_take_all_locks(struct mm_struct *mm);
extern void mm_drop_all_locks(struct mm_struct *mm);
-#ifdef CONFIG_PROC_FS
/* From fs/proc/base.c. callers must _not_ hold the mm's exe_file_lock */
extern void added_exe_file_vma(struct mm_struct *mm);
extern void removed_exe_file_vma(struct mm_struct *mm);
-#else
-static inline void added_exe_file_vma(struct mm_struct *mm)
-{}
-
-static inline void removed_exe_file_vma(struct mm_struct *mm)
-{}
-#endif /* CONFIG_PROC_FS */
+extern void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file);
+extern struct file *get_mm_exe_file(struct mm_struct *mm);
extern int may_expand_vm(struct mm_struct *mm, unsigned long npages);
extern int install_special_mapping(struct mm_struct *mm,
@@ -1432,7 +1426,7 @@
unsigned long flag, unsigned long pgoff);
extern unsigned long mmap_region(struct file *file, unsigned long addr,
unsigned long len, unsigned long flags,
- unsigned int vm_flags, unsigned long pgoff);
+ vm_flags_t vm_flags, unsigned long pgoff);
static inline unsigned long do_mmap(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot,
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 071d459..2a78aae 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -102,6 +102,8 @@
#endif
};
+typedef unsigned long __nocast vm_flags_t;
+
/*
* A region containing a mapping of a non-memory backed file under NOMMU
* conditions. These are held in a global tree and are pinned by the VMAs that
@@ -109,7 +111,7 @@
*/
struct vm_region {
struct rb_node vm_rb; /* link in global region tree */
- unsigned long vm_flags; /* VMA vm_flags */
+ vm_flags_t vm_flags; /* VMA vm_flags */
unsigned long vm_start; /* start address of region */
unsigned long vm_end; /* region initialised to here */
unsigned long vm_top; /* region allocated to here */
@@ -300,11 +302,9 @@
struct task_struct __rcu *owner;
#endif
-#ifdef CONFIG_PROC_FS
/* store ref to file /proc/<pid>/exe symlink points to */
struct file *exe_file;
unsigned long num_exe_file_vmas;
-#endif
#ifdef CONFIG_MMU_NOTIFIER
struct mmu_notifier_mm *mmu_notifier_mm;
#endif
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 217bcf6..29312bd 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -273,11 +273,6 @@
*/
unsigned long recent_rotated[2];
unsigned long recent_scanned[2];
-
- /*
- * accumulated for batching
- */
- unsigned long nr_saved_scan[NR_LRU_LISTS];
};
struct zone {
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 4c4ac3f..a9dd895 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -24,6 +24,7 @@
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
#define MAX_LINKS 32
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index 7b370c7..50d20ab 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -81,13 +81,4 @@
atomic_inc(&ns->count);
}
-#ifdef CONFIG_CGROUP_NS
-int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid);
-#else
-static inline int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid)
-{
- return 0;
-}
-#endif
-
#endif
diff --git a/include/linux/pid.h b/include/linux/pid.h
index cdced84..b152d44 100644
--- a/include/linux/pid.h
+++ b/include/linux/pid.h
@@ -105,7 +105,7 @@
* or rcu_read_lock() held.
*
* find_pid_ns() finds the pid in the namespace specified
- * find_vpid() finr the pid by its virtual id, i.e. in the current namespace
+ * find_vpid() finds the pid by its virtual id, i.e. in the current namespace
*
* see also find_task_by_vpid() set in include/linux/sched.h
*/
diff --git a/include/linux/power/isp1704_charger.h b/include/linux/power/isp1704_charger.h
new file mode 100644
index 0000000..68096a6
--- /dev/null
+++ b/include/linux/power/isp1704_charger.h
@@ -0,0 +1,29 @@
+/*
+ * ISP1704 USB Charger Detection driver
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef __ISP1704_CHARGER_H
+#define __ISP1704_CHARGER_H
+
+struct isp1704_charger_data {
+ void (*set_power)(bool on);
+};
+
+#endif
diff --git a/include/linux/power/max8903_charger.h b/include/linux/power/max8903_charger.h
new file mode 100644
index 0000000..24f51db
--- /dev/null
+++ b/include/linux/power/max8903_charger.h
@@ -0,0 +1,57 @@
+/*
+ * max8903_charger.h - Maxim 8903 USB/Adapter Charger Driver
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __MAX8903_CHARGER_H__
+#define __MAX8903_CHARGER_H__
+
+struct max8903_pdata {
+ /*
+ * GPIOs
+ * cen, chg, flt, and usus are optional.
+ * dok, dcm, and uok are not optional depending on the status of
+ * dc_valid and usb_valid.
+ */
+ int cen; /* Charger Enable input */
+ int dok; /* DC(Adapter) Power OK output */
+ int uok; /* USB Power OK output */
+ int chg; /* Charger status output */
+ int flt; /* Fault output */
+ int dcm; /* Current-Limit Mode input (1: DC, 2: USB) */
+ int usus; /* USB Suspend Input (1: suspended) */
+
+ /*
+ * DC(Adapter/TA) is wired
+ * When dc_valid is true,
+ * dok and dcm should be valid.
+ *
+ * At least one of dc_valid or usb_valid should be true.
+ */
+ bool dc_valid;
+ /*
+ * USB is wired
+ * When usb_valid is true,
+ * uok should be valid.
+ */
+ bool usb_valid;
+};
+
+#endif /* __MAX8903_CHARGER_H__ */
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 648c9c58..e7576cf 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -173,12 +173,6 @@
extern struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
struct proc_dir_entry *parent);
-/* While the {get|set|dup}_mm_exe_file functions are for mm_structs, they are
- * only needed to implement /proc/<pid>|self/exe so we define them here. */
-extern void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file);
-extern struct file *get_mm_exe_file(struct mm_struct *mm);
-extern void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm);
-
extern struct file *proc_ns_fget(int fd);
#else
@@ -230,19 +224,6 @@
{
}
-static inline void set_mm_exe_file(struct mm_struct *mm,
- struct file *new_exe_file)
-{}
-
-static inline struct file *get_mm_exe_file(struct mm_struct *mm)
-{
- return NULL;
-}
-
-static inline void dup_mm_exe_file(struct mm_struct *oldmm,
- struct mm_struct *newmm)
-{}
-
static inline struct file *proc_ns_fget(int fd)
{
return ERR_PTR(-EINVAL);
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index c4c4fc4..ce3127a 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -68,6 +68,8 @@
*
* @min_uV: Smallest voltage consumers may set.
* @max_uV: Largest voltage consumers may set.
+ * @uV_offset: Offset applied to voltages from consumer to compensate for
+ * voltage drops.
*
* @min_uA: Smallest consumers consumers may set.
* @max_uA: Largest current consumers may set.
@@ -99,6 +101,8 @@
int min_uV;
int max_uV;
+ int uV_offset;
+
/* current output range (inclusive) - for current control */
int min_uA;
int max_uA;
@@ -160,8 +164,6 @@
* @supply_regulator: Parent regulator. Specified using the regulator name
* as it appears in the name field in sysfs, which can
* be explicitly set using the constraints field 'name'.
- * @supply_regulator_dev: Parent regulator (if any) - DEPRECATED in favour
- * of supply_regulator.
*
* @constraints: Constraints. These must be specified for the regulator to
* be usable.
@@ -173,7 +175,6 @@
*/
struct regulator_init_data {
const char *supply_regulator; /* or NULL for system supply */
- struct device *supply_regulator_dev; /* or NULL for system supply */
struct regulation_constraints constraints;
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index 877ece4..b27ebea 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -92,10 +92,10 @@
#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */
/* interrupt flags */
-#define RTC_IRQF 0x80 /* any of the following is active */
-#define RTC_PF 0x40
-#define RTC_AF 0x20
-#define RTC_UF 0x10
+#define RTC_IRQF 0x80 /* Any of the following is active */
+#define RTC_PF 0x40 /* Periodic interrupt */
+#define RTC_AF 0x20 /* Alarm interrupt */
+#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */
#ifdef __KERNEL__
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f18300e..dc88712 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -513,6 +513,7 @@
spinlock_t lock;
};
+#include <linux/rwsem.h>
struct autogroup;
/*
@@ -632,6 +633,16 @@
unsigned audit_tty;
struct tty_audit_buf *tty_audit_buf;
#endif
+#ifdef CONFIG_CGROUPS
+ /*
+ * The threadgroup_fork_lock prevents threads from forking with
+ * CLONE_THREAD while held for writing. Use this for fork-sensitive
+ * threadgroup-wide operations. It's taken for reading in fork.c in
+ * copy_process().
+ * Currently only needed write-side by cgroups.
+ */
+ struct rw_semaphore threadgroup_fork_lock;
+#endif
int oom_adj; /* OOM kill score adjustment (bit shift) */
int oom_score_adj; /* OOM kill score adjustment */
@@ -2323,6 +2334,31 @@
spin_unlock_irqrestore(&tsk->sighand->siglock, *flags);
}
+/* See the declaration of threadgroup_fork_lock in signal_struct. */
+#ifdef CONFIG_CGROUPS
+static inline void threadgroup_fork_read_lock(struct task_struct *tsk)
+{
+ down_read(&tsk->signal->threadgroup_fork_lock);
+}
+static inline void threadgroup_fork_read_unlock(struct task_struct *tsk)
+{
+ up_read(&tsk->signal->threadgroup_fork_lock);
+}
+static inline void threadgroup_fork_write_lock(struct task_struct *tsk)
+{
+ down_write(&tsk->signal->threadgroup_fork_lock);
+}
+static inline void threadgroup_fork_write_unlock(struct task_struct *tsk)
+{
+ up_write(&tsk->signal->threadgroup_fork_lock);
+}
+#else
+static inline void threadgroup_fork_read_lock(struct task_struct *tsk) {}
+static inline void threadgroup_fork_read_unlock(struct task_struct *tsk) {}
+static inline void threadgroup_fork_write_lock(struct task_struct *tsk) {}
+static inline void threadgroup_fork_write_unlock(struct task_struct *tsk) {}
+#endif
+
#ifndef __HAVE_THREAD_FUNCTIONS
#define task_thread_info(task) ((struct thread_info *)(task)->stack)
diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h
index 06d6964..e981189 100644
--- a/include/linux/seqlock.h
+++ b/include/linux/seqlock.h
@@ -41,9 +41,6 @@
#define __SEQLOCK_UNLOCKED(lockname) \
{ 0, __SPIN_LOCK_UNLOCKED(lockname) }
-#define SEQLOCK_UNLOCKED \
- __SEQLOCK_UNLOCKED(old_style_seqlock_init)
-
#define seqlock_init(x) \
do { \
(x)->sequence = 0; \
diff --git a/include/linux/smp.h b/include/linux/smp.h
index 74243c8..7ad824d 100644
--- a/include/linux/smp.h
+++ b/include/linux/smp.h
@@ -98,16 +98,6 @@
*/
int on_each_cpu(smp_call_func_t func, void *info, int wait);
-#define MSG_ALL_BUT_SELF 0x8000 /* Assume <32768 CPU's */
-#define MSG_ALL 0x8001
-
-#define MSG_INVALIDATE_TLB 0x0001 /* Remote processor TLB invalidate */
-#define MSG_STOP_CPU 0x0002 /* Sent to shut down slave CPU's
- * when rebooting
- */
-#define MSG_RESCHEDULE 0x0003 /* Reschedule request from master CPU*/
-#define MSG_CALL_FUNCTION 0x0004 /* Call function on all other CPUs */
-
/*
* Mark the boot cpu "online" so that it can call console drivers in
* printk() and can access its per-cpu storage.
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index b4d7710..bb4f5fb 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -581,7 +581,7 @@
* Callable only from contexts that can sleep.
*/
static inline int
-spi_write(struct spi_device *spi, const u8 *buf, size_t len)
+spi_write(struct spi_device *spi, const void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
@@ -605,7 +605,7 @@
* Callable only from contexts that can sleep.
*/
static inline int
-spi_read(struct spi_device *spi, u8 *buf, size_t len)
+spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
@@ -620,8 +620,8 @@
/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,
- const u8 *txbuf, unsigned n_tx,
- u8 *rxbuf, unsigned n_rx);
+ const void *txbuf, unsigned n_tx,
+ void *rxbuf, unsigned n_rx);
/**
* spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read
diff --git a/include/linux/swap.h b/include/linux/swap.h
index a5c6da5..384eb5f 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -257,7 +257,8 @@
extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap,
unsigned int swappiness,
- struct zone *zone);
+ struct zone *zone,
+ unsigned long *nr_scanned);
extern int __isolate_lru_page(struct page *page, int mode, int file);
extern unsigned long shrink_all_memory(unsigned long nr_pages);
extern int vm_swappiness;
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
new file mode 100644
index 0000000..03b90cdc
--- /dev/null
+++ b/include/linux/vm_event_item.h
@@ -0,0 +1,64 @@
+#ifndef VM_EVENT_ITEM_H_INCLUDED
+#define VM_EVENT_ITEM_H_INCLUDED
+
+#ifdef CONFIG_ZONE_DMA
+#define DMA_ZONE(xx) xx##_DMA,
+#else
+#define DMA_ZONE(xx)
+#endif
+
+#ifdef CONFIG_ZONE_DMA32
+#define DMA32_ZONE(xx) xx##_DMA32,
+#else
+#define DMA32_ZONE(xx)
+#endif
+
+#ifdef CONFIG_HIGHMEM
+#define HIGHMEM_ZONE(xx) , xx##_HIGH
+#else
+#define HIGHMEM_ZONE(xx)
+#endif
+
+#define FOR_ALL_ZONES(xx) DMA_ZONE(xx) DMA32_ZONE(xx) xx##_NORMAL HIGHMEM_ZONE(xx) , xx##_MOVABLE
+
+enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
+ FOR_ALL_ZONES(PGALLOC),
+ PGFREE, PGACTIVATE, PGDEACTIVATE,
+ PGFAULT, PGMAJFAULT,
+ FOR_ALL_ZONES(PGREFILL),
+ FOR_ALL_ZONES(PGSTEAL),
+ FOR_ALL_ZONES(PGSCAN_KSWAPD),
+ FOR_ALL_ZONES(PGSCAN_DIRECT),
+#ifdef CONFIG_NUMA
+ PGSCAN_ZONE_RECLAIM_FAILED,
+#endif
+ PGINODESTEAL, SLABS_SCANNED, KSWAPD_STEAL, KSWAPD_INODESTEAL,
+ KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY,
+ KSWAPD_SKIP_CONGESTION_WAIT,
+ PAGEOUTRUN, ALLOCSTALL, PGROTATED,
+#ifdef CONFIG_COMPACTION
+ COMPACTBLOCKS, COMPACTPAGES, COMPACTPAGEFAILED,
+ COMPACTSTALL, COMPACTFAIL, COMPACTSUCCESS,
+#endif
+#ifdef CONFIG_HUGETLB_PAGE
+ HTLB_BUDDY_PGALLOC, HTLB_BUDDY_PGALLOC_FAIL,
+#endif
+ UNEVICTABLE_PGCULLED, /* culled to noreclaim list */
+ UNEVICTABLE_PGSCANNED, /* scanned for reclaimability */
+ UNEVICTABLE_PGRESCUED, /* rescued from noreclaim list */
+ UNEVICTABLE_PGMLOCKED,
+ UNEVICTABLE_PGMUNLOCKED,
+ UNEVICTABLE_PGCLEARED, /* on COW, page truncate */
+ UNEVICTABLE_PGSTRANDED, /* unable to isolate on unlock */
+ UNEVICTABLE_MLOCKFREED,
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ THP_FAULT_ALLOC,
+ THP_FAULT_FALLBACK,
+ THP_COLLAPSE_ALLOC,
+ THP_COLLAPSE_ALLOC_FAILED,
+ THP_SPLIT,
+#endif
+ NR_VM_EVENT_ITEMS
+};
+
+#endif /* VM_EVENT_ITEM_H_INCLUDED */
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index 5135983..bcd942f 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -5,69 +5,9 @@
#include <linux/percpu.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
+#include <linux/vm_event_item.h>
#include <asm/atomic.h>
-#ifdef CONFIG_ZONE_DMA
-#define DMA_ZONE(xx) xx##_DMA,
-#else
-#define DMA_ZONE(xx)
-#endif
-
-#ifdef CONFIG_ZONE_DMA32
-#define DMA32_ZONE(xx) xx##_DMA32,
-#else
-#define DMA32_ZONE(xx)
-#endif
-
-#ifdef CONFIG_HIGHMEM
-#define HIGHMEM_ZONE(xx) , xx##_HIGH
-#else
-#define HIGHMEM_ZONE(xx)
-#endif
-
-
-#define FOR_ALL_ZONES(xx) DMA_ZONE(xx) DMA32_ZONE(xx) xx##_NORMAL HIGHMEM_ZONE(xx) , xx##_MOVABLE
-
-enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
- FOR_ALL_ZONES(PGALLOC),
- PGFREE, PGACTIVATE, PGDEACTIVATE,
- PGFAULT, PGMAJFAULT,
- FOR_ALL_ZONES(PGREFILL),
- FOR_ALL_ZONES(PGSTEAL),
- FOR_ALL_ZONES(PGSCAN_KSWAPD),
- FOR_ALL_ZONES(PGSCAN_DIRECT),
-#ifdef CONFIG_NUMA
- PGSCAN_ZONE_RECLAIM_FAILED,
-#endif
- PGINODESTEAL, SLABS_SCANNED, KSWAPD_STEAL, KSWAPD_INODESTEAL,
- KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY,
- KSWAPD_SKIP_CONGESTION_WAIT,
- PAGEOUTRUN, ALLOCSTALL, PGROTATED,
-#ifdef CONFIG_COMPACTION
- COMPACTBLOCKS, COMPACTPAGES, COMPACTPAGEFAILED,
- COMPACTSTALL, COMPACTFAIL, COMPACTSUCCESS,
-#endif
-#ifdef CONFIG_HUGETLB_PAGE
- HTLB_BUDDY_PGALLOC, HTLB_BUDDY_PGALLOC_FAIL,
-#endif
- UNEVICTABLE_PGCULLED, /* culled to noreclaim list */
- UNEVICTABLE_PGSCANNED, /* scanned for reclaimability */
- UNEVICTABLE_PGRESCUED, /* rescued from noreclaim list */
- UNEVICTABLE_PGMLOCKED,
- UNEVICTABLE_PGMUNLOCKED,
- UNEVICTABLE_PGCLEARED, /* on COW, page truncate */
- UNEVICTABLE_PGSTRANDED, /* unable to isolate on unlock */
- UNEVICTABLE_MLOCKFREED,
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
- THP_FAULT_ALLOC,
- THP_FAULT_FALLBACK,
- THP_COLLAPSE_ALLOC,
- THP_COLLAPSE_ALLOC_FAILED,
- THP_SPLIT,
-#endif
- NR_VM_EVENT_ITEMS
-};
-
extern int sysctl_stat_interval;
#ifdef CONFIG_VM_EVENT_COUNTERS
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
new file mode 100644
index 0000000..2d7e7ca
--- /dev/null
+++ b/include/media/m5mols.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef MEDIA_M5MOLS_H
+#define MEDIA_M5MOLS_H
+
+/**
+ * struct m5mols_platform_data - platform data for M-5MOLS driver
+ * @irq: GPIO getting the irq pin of M-5MOLS
+ * @gpio_reset: GPIO driving the reset pin of M-5MOLS
+ * @reset_polarity: active state for gpio_rst pin, 0 or 1
+ * @set_power: an additional callback to the board setup code
+ * to be called after enabling and before disabling
+ * the sensor's supply regulators
+ */
+struct m5mols_platform_data {
+ int irq;
+ int gpio_reset;
+ u8 reset_polarity;
+ int (*set_power)(struct device *dev, int on);
+};
+
+#endif /* MEDIA_M5MOLS_H */
diff --git a/include/media/videobuf-dvb.h b/include/media/videobuf-dvb.h
index 07cf4b9..bf36572 100644
--- a/include/media/videobuf-dvb.h
+++ b/include/media/videobuf-dvb.h
@@ -4,6 +4,9 @@
#include <dvb_net.h>
#include <dvb_frontend.h>
+#ifndef _VIDEOBUF_DVB_H_
+#define _VIDEOBUF_DVB_H_
+
struct videobuf_dvb {
/* filling that the job of the driver */
char *name;
@@ -54,6 +57,7 @@
struct videobuf_dvb_frontend * videobuf_dvb_get_frontend(struct videobuf_dvb_frontends *f, int id);
int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, struct dvb_frontend *p);
+#endif /* _VIDEOBUF_DVB_H_ */
/*
* Local variables:
diff --git a/include/rdma/Kbuild b/include/rdma/Kbuild
index e7c0432..ea56f76 100644
--- a/include/rdma/Kbuild
+++ b/include/rdma/Kbuild
@@ -1 +1,6 @@
+header-y += ib_user_cm.h
header-y += ib_user_mad.h
+header-y += ib_user_sa.h
+header-y += ib_user_verbs.h
+header-y += rdma_netlink.h
+header-y += rdma_user_cm.h
diff --git a/include/rdma/ib_user_cm.h b/include/rdma/ib_user_cm.h
index bd3d380..f79014a 100644
--- a/include/rdma/ib_user_cm.h
+++ b/include/rdma/ib_user_cm.h
@@ -34,6 +34,7 @@
#ifndef IB_USER_CM_H
#define IB_USER_CM_H
+#include <linux/types.h>
#include <rdma/ib_user_sa.h>
#define IB_USER_CM_ABI_VERSION 5
diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h
index 169f7a5..26977c1 100644
--- a/include/rdma/rdma_cm.h
+++ b/include/rdma/rdma_cm.h
@@ -111,6 +111,20 @@
} param;
};
+enum rdma_cm_state {
+ RDMA_CM_IDLE,
+ RDMA_CM_ADDR_QUERY,
+ RDMA_CM_ADDR_RESOLVED,
+ RDMA_CM_ROUTE_QUERY,
+ RDMA_CM_ROUTE_RESOLVED,
+ RDMA_CM_CONNECT,
+ RDMA_CM_DISCONNECT,
+ RDMA_CM_ADDR_BOUND,
+ RDMA_CM_LISTEN,
+ RDMA_CM_DEVICE_REMOVAL,
+ RDMA_CM_DESTROYING
+};
+
struct rdma_cm_id;
/**
@@ -130,6 +144,7 @@
rdma_cm_event_handler event_handler;
struct rdma_route route;
enum rdma_port_space ps;
+ enum ib_qp_type qp_type;
u8 port_num;
};
@@ -140,9 +155,11 @@
* returned rdma_id.
* @context: User specified context associated with the id.
* @ps: RDMA port space.
+ * @qp_type: type of queue pair associated with the id.
*/
struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler,
- void *context, enum rdma_port_space ps);
+ void *context, enum rdma_port_space ps,
+ enum ib_qp_type qp_type);
/**
* rdma_destroy_id - Destroys an RDMA identifier.
diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h
new file mode 100644
index 0000000..3c5363a
--- /dev/null
+++ b/include/rdma/rdma_netlink.h
@@ -0,0 +1,92 @@
+#ifndef _RDMA_NETLINK_H
+#define _RDMA_NETLINK_H
+
+#include <linux/types.h>
+
+enum {
+ RDMA_NL_RDMA_CM = 1
+};
+
+#define RDMA_NL_GET_CLIENT(type) ((type & (((1 << 6) - 1) << 10)) >> 10)
+#define RDMA_NL_GET_OP(type) (type & ((1 << 10) - 1))
+#define RDMA_NL_GET_TYPE(client, op) ((client << 10) + op)
+
+enum {
+ RDMA_NL_RDMA_CM_ID_STATS = 0,
+ RDMA_NL_RDMA_CM_NUM_OPS
+};
+
+enum {
+ RDMA_NL_RDMA_CM_ATTR_SRC_ADDR = 1,
+ RDMA_NL_RDMA_CM_ATTR_DST_ADDR,
+ RDMA_NL_RDMA_CM_NUM_ATTR,
+};
+
+struct rdma_cm_id_stats {
+ __u32 qp_num;
+ __u32 bound_dev_if;
+ __u32 port_space;
+ __s32 pid;
+ __u8 cm_state;
+ __u8 node_type;
+ __u8 port_num;
+ __u8 qp_type;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/netlink.h>
+
+struct ibnl_client_cbs {
+ int (*dump)(struct sk_buff *skb, struct netlink_callback *nlcb);
+};
+
+int ibnl_init(void);
+void ibnl_cleanup(void);
+
+/**
+ * Add a a client to the list of IB netlink exporters.
+ * @index: Index of the added client
+ * @nops: Number of supported ops by the added client.
+ * @cb_table: A table for op->callback
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int ibnl_add_client(int index, int nops,
+ const struct ibnl_client_cbs cb_table[]);
+
+/**
+ * Remove a client from IB netlink.
+ * @index: Index of the removed IB client.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int ibnl_remove_client(int index);
+
+/**
+ * Put a new message in a supplied skb.
+ * @skb: The netlink skb.
+ * @nlh: Pointer to put the header of the new netlink message.
+ * @seq: The message sequence number.
+ * @len: The requested message length to allocate.
+ * @client: Calling IB netlink client.
+ * @op: message content op.
+ * Returns the allocated buffer on success and NULL on failure.
+ */
+void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq,
+ int len, int client, int op);
+/**
+ * Put a new attribute in a supplied skb.
+ * @skb: The netlink skb.
+ * @nlh: Header of the netlink message to append the attribute to.
+ * @len: The length of the attribute data.
+ * @data: The attribute data to put.
+ * @type: The attribute type.
+ * Returns the 0 and a negative error code on failure.
+ */
+int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
+ int len, void *data, int type);
+
+#endif /* __KERNEL__ */
+
+#endif /* _RDMA_NETLINK_H */
diff --git a/include/trace/events/gpio.h b/include/trace/events/gpio.h
new file mode 100644
index 0000000..927a8ad
--- /dev/null
+++ b/include/trace/events/gpio.h
@@ -0,0 +1,56 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM gpio
+
+#if !defined(_TRACE_GPIO_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_GPIO_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(gpio_direction,
+
+ TP_PROTO(unsigned gpio, int in, int err),
+
+ TP_ARGS(gpio, in, err),
+
+ TP_STRUCT__entry(
+ __field(unsigned, gpio)
+ __field(int, in)
+ __field(int, err)
+ ),
+
+ TP_fast_assign(
+ __entry->gpio = gpio;
+ __entry->in = in;
+ __entry->err = err;
+ ),
+
+ TP_printk("%u %3s (%d)", __entry->gpio,
+ __entry->in ? "in" : "out", __entry->err)
+);
+
+TRACE_EVENT(gpio_value,
+
+ TP_PROTO(unsigned gpio, int get, int value),
+
+ TP_ARGS(gpio, get, value),
+
+ TP_STRUCT__entry(
+ __field(unsigned, gpio)
+ __field(int, get)
+ __field(int, value)
+ ),
+
+ TP_fast_assign(
+ __entry->gpio = gpio;
+ __entry->get = get;
+ __entry->value = value;
+ ),
+
+ TP_printk("%u %3s %d", __entry->gpio,
+ __entry->get ? "get" : "set", __entry->value)
+);
+
+#endif /* if !defined(_TRACE_GPIO_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h
index b33257b..70213b4 100644
--- a/include/xen/interface/xen.h
+++ b/include/xen/interface/xen.h
@@ -58,6 +58,7 @@
#define __HYPERVISOR_event_channel_op 32
#define __HYPERVISOR_physdev_op 33
#define __HYPERVISOR_hvm_op 34
+#define __HYPERVISOR_tmem_op 38
/* Architecture-specific hypercall definitions. */
#define __HYPERVISOR_arch_0 48
@@ -461,6 +462,27 @@
#define __mk_unsigned_long(x) x ## UL
#define mk_unsigned_long(x) __mk_unsigned_long(x)
+#define TMEM_SPEC_VERSION 1
+
+struct tmem_op {
+ uint32_t cmd;
+ int32_t pool_id;
+ union {
+ struct { /* for cmd == TMEM_NEW_POOL */
+ uint64_t uuid[2];
+ uint32_t flags;
+ } new;
+ struct {
+ uint64_t oid[3];
+ uint32_t index;
+ uint32_t tmem_offset;
+ uint32_t pfn_offset;
+ uint32_t len;
+ GUEST_HANDLE(void) gmfn; /* guest machine page frame */
+ } gen;
+ } u;
+};
+
#else /* __ASSEMBLY__ */
/* In assembly code we cannot use C numeric constant suffixes. */
diff --git a/init/Kconfig b/init/Kconfig
index 332aac6..ebafac4 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -589,14 +589,6 @@
Say N if unsure.
-config CGROUP_NS
- bool "Namespace cgroup subsystem"
- help
- Provides a simple namespace cgroup subsystem to
- provide hierarchical naming of sets of namespaces,
- for instance virtual servers and checkpoint/restart
- jobs.
-
config CGROUP_FREEZER
bool "Freezer cgroup subsystem"
help
diff --git a/ipc/shm.c b/ipc/shm.c
index 729acb7..ab3385a 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -347,7 +347,7 @@
struct file * file;
char name[13];
int id;
- int acctflag = 0;
+ vm_flags_t acctflag = 0;
if (size < SHMMIN || size > ns->shm_ctlmax)
return -EINVAL;
diff --git a/kernel/Makefile b/kernel/Makefile
index e9cf191..2d64cfc 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -61,7 +61,6 @@
obj-$(CONFIG_CGROUPS) += cgroup.o
obj-$(CONFIG_CGROUP_FREEZER) += cgroup_freezer.o
obj-$(CONFIG_CPUSETS) += cpuset.o
-obj-$(CONFIG_CGROUP_NS) += ns_cgroup.o
obj-$(CONFIG_UTS_NS) += utsname.o
obj-$(CONFIG_USER_NS) += user_namespace.o
obj-$(CONFIG_PID_NS) += pid_namespace.o
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 909a355..2731d11 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -57,6 +57,7 @@
#include <linux/vmalloc.h> /* TODO: replace with more sophisticated array */
#include <linux/eventfd.h>
#include <linux/poll.h>
+#include <linux/flex_array.h> /* used in cgroup_attach_proc */
#include <asm/atomic.h>
@@ -1735,6 +1736,76 @@
}
EXPORT_SYMBOL_GPL(cgroup_path);
+/*
+ * cgroup_task_migrate - move a task from one cgroup to another.
+ *
+ * 'guarantee' is set if the caller promises that a new css_set for the task
+ * will already exist. If not set, this function might sleep, and can fail with
+ * -ENOMEM. Otherwise, it can only fail with -ESRCH.
+ */
+static int cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp,
+ struct task_struct *tsk, bool guarantee)
+{
+ struct css_set *oldcg;
+ struct css_set *newcg;
+
+ /*
+ * get old css_set. we need to take task_lock and refcount it, because
+ * an exiting task can change its css_set to init_css_set and drop its
+ * old one without taking cgroup_mutex.
+ */
+ task_lock(tsk);
+ oldcg = tsk->cgroups;
+ get_css_set(oldcg);
+ task_unlock(tsk);
+
+ /* locate or allocate a new css_set for this task. */
+ if (guarantee) {
+ /* we know the css_set we want already exists. */
+ struct cgroup_subsys_state *template[CGROUP_SUBSYS_COUNT];
+ read_lock(&css_set_lock);
+ newcg = find_existing_css_set(oldcg, cgrp, template);
+ BUG_ON(!newcg);
+ get_css_set(newcg);
+ read_unlock(&css_set_lock);
+ } else {
+ might_sleep();
+ /* find_css_set will give us newcg already referenced. */
+ newcg = find_css_set(oldcg, cgrp);
+ if (!newcg) {
+ put_css_set(oldcg);
+ return -ENOMEM;
+ }
+ }
+ put_css_set(oldcg);
+
+ /* if PF_EXITING is set, the tsk->cgroups pointer is no longer safe. */
+ task_lock(tsk);
+ if (tsk->flags & PF_EXITING) {
+ task_unlock(tsk);
+ put_css_set(newcg);
+ return -ESRCH;
+ }
+ rcu_assign_pointer(tsk->cgroups, newcg);
+ task_unlock(tsk);
+
+ /* Update the css_set linked lists if we're using them */
+ write_lock(&css_set_lock);
+ if (!list_empty(&tsk->cg_list))
+ list_move(&tsk->cg_list, &newcg->tasks);
+ write_unlock(&css_set_lock);
+
+ /*
+ * We just gained a reference on oldcg by taking it from the task. As
+ * trading it for newcg is protected by cgroup_mutex, we're safe to drop
+ * it here; it will be freed under RCU.
+ */
+ put_css_set(oldcg);
+
+ set_bit(CGRP_RELEASABLE, &oldcgrp->flags);
+ return 0;
+}
+
/**
* cgroup_attach_task - attach task 'tsk' to cgroup 'cgrp'
* @cgrp: the cgroup the task is attaching to
@@ -1745,11 +1816,9 @@
*/
int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
{
- int retval = 0;
+ int retval;
struct cgroup_subsys *ss, *failed_ss = NULL;
struct cgroup *oldcgrp;
- struct css_set *cg;
- struct css_set *newcg;
struct cgroupfs_root *root = cgrp->root;
/* Nothing to do if the task is already in that cgroup */
@@ -1759,7 +1828,7 @@
for_each_subsys(root, ss) {
if (ss->can_attach) {
- retval = ss->can_attach(ss, cgrp, tsk, false);
+ retval = ss->can_attach(ss, cgrp, tsk);
if (retval) {
/*
* Remember on which subsystem the can_attach()
@@ -1771,46 +1840,29 @@
goto out;
}
}
+ if (ss->can_attach_task) {
+ retval = ss->can_attach_task(cgrp, tsk);
+ if (retval) {
+ failed_ss = ss;
+ goto out;
+ }
+ }
}
- task_lock(tsk);
- cg = tsk->cgroups;
- get_css_set(cg);
- task_unlock(tsk);
- /*
- * Locate or allocate a new css_set for this task,
- * based on its final set of cgroups
- */
- newcg = find_css_set(cg, cgrp);
- put_css_set(cg);
- if (!newcg) {
- retval = -ENOMEM;
+ retval = cgroup_task_migrate(cgrp, oldcgrp, tsk, false);
+ if (retval)
goto out;
- }
-
- task_lock(tsk);
- if (tsk->flags & PF_EXITING) {
- task_unlock(tsk);
- put_css_set(newcg);
- retval = -ESRCH;
- goto out;
- }
- rcu_assign_pointer(tsk->cgroups, newcg);
- task_unlock(tsk);
-
- /* Update the css_set linked lists if we're using them */
- write_lock(&css_set_lock);
- if (!list_empty(&tsk->cg_list))
- list_move(&tsk->cg_list, &newcg->tasks);
- write_unlock(&css_set_lock);
for_each_subsys(root, ss) {
+ if (ss->pre_attach)
+ ss->pre_attach(cgrp);
+ if (ss->attach_task)
+ ss->attach_task(cgrp, tsk);
if (ss->attach)
- ss->attach(ss, cgrp, oldcgrp, tsk, false);
+ ss->attach(ss, cgrp, oldcgrp, tsk);
}
- set_bit(CGRP_RELEASABLE, &oldcgrp->flags);
+
synchronize_rcu();
- put_css_set(cg);
/*
* wake up rmdir() waiter. the rmdir should fail since the cgroup
@@ -1829,7 +1881,7 @@
*/
break;
if (ss->cancel_attach)
- ss->cancel_attach(ss, cgrp, tsk, false);
+ ss->cancel_attach(ss, cgrp, tsk);
}
}
return retval;
@@ -1860,49 +1912,370 @@
EXPORT_SYMBOL_GPL(cgroup_attach_task_all);
/*
- * Attach task with pid 'pid' to cgroup 'cgrp'. Call with cgroup_mutex
- * held. May take task_lock of task
+ * cgroup_attach_proc works in two stages, the first of which prefetches all
+ * new css_sets needed (to make sure we have enough memory before committing
+ * to the move) and stores them in a list of entries of the following type.
+ * TODO: possible optimization: use css_set->rcu_head for chaining instead
*/
-static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
+struct cg_list_entry {
+ struct css_set *cg;
+ struct list_head links;
+};
+
+static bool css_set_check_fetched(struct cgroup *cgrp,
+ struct task_struct *tsk, struct css_set *cg,
+ struct list_head *newcg_list)
+{
+ struct css_set *newcg;
+ struct cg_list_entry *cg_entry;
+ struct cgroup_subsys_state *template[CGROUP_SUBSYS_COUNT];
+
+ read_lock(&css_set_lock);
+ newcg = find_existing_css_set(cg, cgrp, template);
+ if (newcg)
+ get_css_set(newcg);
+ read_unlock(&css_set_lock);
+
+ /* doesn't exist at all? */
+ if (!newcg)
+ return false;
+ /* see if it's already in the list */
+ list_for_each_entry(cg_entry, newcg_list, links) {
+ if (cg_entry->cg == newcg) {
+ put_css_set(newcg);
+ return true;
+ }
+ }
+
+ /* not found */
+ put_css_set(newcg);
+ return false;
+}
+
+/*
+ * Find the new css_set and store it in the list in preparation for moving the
+ * given task to the given cgroup. Returns 0 or -ENOMEM.
+ */
+static int css_set_prefetch(struct cgroup *cgrp, struct css_set *cg,
+ struct list_head *newcg_list)
+{
+ struct css_set *newcg;
+ struct cg_list_entry *cg_entry;
+
+ /* ensure a new css_set will exist for this thread */
+ newcg = find_css_set(cg, cgrp);
+ if (!newcg)
+ return -ENOMEM;
+ /* add it to the list */
+ cg_entry = kmalloc(sizeof(struct cg_list_entry), GFP_KERNEL);
+ if (!cg_entry) {
+ put_css_set(newcg);
+ return -ENOMEM;
+ }
+ cg_entry->cg = newcg;
+ list_add(&cg_entry->links, newcg_list);
+ return 0;
+}
+
+/**
+ * cgroup_attach_proc - attach all threads in a threadgroup to a cgroup
+ * @cgrp: the cgroup to attach to
+ * @leader: the threadgroup leader task_struct of the group to be attached
+ *
+ * Call holding cgroup_mutex and the threadgroup_fork_lock of the leader. Will
+ * take task_lock of each thread in leader's threadgroup individually in turn.
+ */
+int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader)
+{
+ int retval, i, group_size;
+ struct cgroup_subsys *ss, *failed_ss = NULL;
+ bool cancel_failed_ss = false;
+ /* guaranteed to be initialized later, but the compiler needs this */
+ struct cgroup *oldcgrp = NULL;
+ struct css_set *oldcg;
+ struct cgroupfs_root *root = cgrp->root;
+ /* threadgroup list cursor and array */
+ struct task_struct *tsk;
+ struct flex_array *group;
+ /*
+ * we need to make sure we have css_sets for all the tasks we're
+ * going to move -before- we actually start moving them, so that in
+ * case we get an ENOMEM we can bail out before making any changes.
+ */
+ struct list_head newcg_list;
+ struct cg_list_entry *cg_entry, *temp_nobe;
+
+ /*
+ * step 0: in order to do expensive, possibly blocking operations for
+ * every thread, we cannot iterate the thread group list, since it needs
+ * rcu or tasklist locked. instead, build an array of all threads in the
+ * group - threadgroup_fork_lock prevents new threads from appearing,
+ * and if threads exit, this will just be an over-estimate.
+ */
+ group_size = get_nr_threads(leader);
+ /* flex_array supports very large thread-groups better than kmalloc. */
+ group = flex_array_alloc(sizeof(struct task_struct *), group_size,
+ GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+ /* pre-allocate to guarantee space while iterating in rcu read-side. */
+ retval = flex_array_prealloc(group, 0, group_size - 1, GFP_KERNEL);
+ if (retval)
+ goto out_free_group_list;
+
+ /* prevent changes to the threadgroup list while we take a snapshot. */
+ rcu_read_lock();
+ if (!thread_group_leader(leader)) {
+ /*
+ * a race with de_thread from another thread's exec() may strip
+ * us of our leadership, making while_each_thread unsafe to use
+ * on this task. if this happens, there is no choice but to
+ * throw this task away and try again (from cgroup_procs_write);
+ * this is "double-double-toil-and-trouble-check locking".
+ */
+ rcu_read_unlock();
+ retval = -EAGAIN;
+ goto out_free_group_list;
+ }
+ /* take a reference on each task in the group to go in the array. */
+ tsk = leader;
+ i = 0;
+ do {
+ /* as per above, nr_threads may decrease, but not increase. */
+ BUG_ON(i >= group_size);
+ get_task_struct(tsk);
+ /*
+ * saying GFP_ATOMIC has no effect here because we did prealloc
+ * earlier, but it's good form to communicate our expectations.
+ */
+ retval = flex_array_put_ptr(group, i, tsk, GFP_ATOMIC);
+ BUG_ON(retval != 0);
+ i++;
+ } while_each_thread(leader, tsk);
+ /* remember the number of threads in the array for later. */
+ group_size = i;
+ rcu_read_unlock();
+
+ /*
+ * step 1: check that we can legitimately attach to the cgroup.
+ */
+ for_each_subsys(root, ss) {
+ if (ss->can_attach) {
+ retval = ss->can_attach(ss, cgrp, leader);
+ if (retval) {
+ failed_ss = ss;
+ goto out_cancel_attach;
+ }
+ }
+ /* a callback to be run on every thread in the threadgroup. */
+ if (ss->can_attach_task) {
+ /* run on each task in the threadgroup. */
+ for (i = 0; i < group_size; i++) {
+ tsk = flex_array_get_ptr(group, i);
+ retval = ss->can_attach_task(cgrp, tsk);
+ if (retval) {
+ failed_ss = ss;
+ cancel_failed_ss = true;
+ goto out_cancel_attach;
+ }
+ }
+ }
+ }
+
+ /*
+ * step 2: make sure css_sets exist for all threads to be migrated.
+ * we use find_css_set, which allocates a new one if necessary.
+ */
+ INIT_LIST_HEAD(&newcg_list);
+ for (i = 0; i < group_size; i++) {
+ tsk = flex_array_get_ptr(group, i);
+ /* nothing to do if this task is already in the cgroup */
+ oldcgrp = task_cgroup_from_root(tsk, root);
+ if (cgrp == oldcgrp)
+ continue;
+ /* get old css_set pointer */
+ task_lock(tsk);
+ if (tsk->flags & PF_EXITING) {
+ /* ignore this task if it's going away */
+ task_unlock(tsk);
+ continue;
+ }
+ oldcg = tsk->cgroups;
+ get_css_set(oldcg);
+ task_unlock(tsk);
+ /* see if the new one for us is already in the list? */
+ if (css_set_check_fetched(cgrp, tsk, oldcg, &newcg_list)) {
+ /* was already there, nothing to do. */
+ put_css_set(oldcg);
+ } else {
+ /* we don't already have it. get new one. */
+ retval = css_set_prefetch(cgrp, oldcg, &newcg_list);
+ put_css_set(oldcg);
+ if (retval)
+ goto out_list_teardown;
+ }
+ }
+
+ /*
+ * step 3: now that we're guaranteed success wrt the css_sets, proceed
+ * to move all tasks to the new cgroup, calling ss->attach_task for each
+ * one along the way. there are no failure cases after here, so this is
+ * the commit point.
+ */
+ for_each_subsys(root, ss) {
+ if (ss->pre_attach)
+ ss->pre_attach(cgrp);
+ }
+ for (i = 0; i < group_size; i++) {
+ tsk = flex_array_get_ptr(group, i);
+ /* leave current thread as it is if it's already there */
+ oldcgrp = task_cgroup_from_root(tsk, root);
+ if (cgrp == oldcgrp)
+ continue;
+ /* attach each task to each subsystem */
+ for_each_subsys(root, ss) {
+ if (ss->attach_task)
+ ss->attach_task(cgrp, tsk);
+ }
+ /* if the thread is PF_EXITING, it can just get skipped. */
+ retval = cgroup_task_migrate(cgrp, oldcgrp, tsk, true);
+ BUG_ON(retval != 0 && retval != -ESRCH);
+ }
+ /* nothing is sensitive to fork() after this point. */
+
+ /*
+ * step 4: do expensive, non-thread-specific subsystem callbacks.
+ * TODO: if ever a subsystem needs to know the oldcgrp for each task
+ * being moved, this call will need to be reworked to communicate that.
+ */
+ for_each_subsys(root, ss) {
+ if (ss->attach)
+ ss->attach(ss, cgrp, oldcgrp, leader);
+ }
+
+ /*
+ * step 5: success! and cleanup
+ */
+ synchronize_rcu();
+ cgroup_wakeup_rmdir_waiter(cgrp);
+ retval = 0;
+out_list_teardown:
+ /* clean up the list of prefetched css_sets. */
+ list_for_each_entry_safe(cg_entry, temp_nobe, &newcg_list, links) {
+ list_del(&cg_entry->links);
+ put_css_set(cg_entry->cg);
+ kfree(cg_entry);
+ }
+out_cancel_attach:
+ /* same deal as in cgroup_attach_task */
+ if (retval) {
+ for_each_subsys(root, ss) {
+ if (ss == failed_ss) {
+ if (cancel_failed_ss && ss->cancel_attach)
+ ss->cancel_attach(ss, cgrp, leader);
+ break;
+ }
+ if (ss->cancel_attach)
+ ss->cancel_attach(ss, cgrp, leader);
+ }
+ }
+ /* clean up the array of referenced threads in the group. */
+ for (i = 0; i < group_size; i++) {
+ tsk = flex_array_get_ptr(group, i);
+ put_task_struct(tsk);
+ }
+out_free_group_list:
+ flex_array_free(group);
+ return retval;
+}
+
+/*
+ * Find the task_struct of the task to attach by vpid and pass it along to the
+ * function to attach either it or all tasks in its threadgroup. Will take
+ * cgroup_mutex; may take task_lock of task.
+ */
+static int attach_task_by_pid(struct cgroup *cgrp, u64 pid, bool threadgroup)
{
struct task_struct *tsk;
const struct cred *cred = current_cred(), *tcred;
int ret;
+ if (!cgroup_lock_live_group(cgrp))
+ return -ENODEV;
+
if (pid) {
rcu_read_lock();
tsk = find_task_by_vpid(pid);
- if (!tsk || tsk->flags & PF_EXITING) {
+ if (!tsk) {
rcu_read_unlock();
+ cgroup_unlock();
+ return -ESRCH;
+ }
+ if (threadgroup) {
+ /*
+ * RCU protects this access, since tsk was found in the
+ * tid map. a race with de_thread may cause group_leader
+ * to stop being the leader, but cgroup_attach_proc will
+ * detect it later.
+ */
+ tsk = tsk->group_leader;
+ } else if (tsk->flags & PF_EXITING) {
+ /* optimization for the single-task-only case */
+ rcu_read_unlock();
+ cgroup_unlock();
return -ESRCH;
}
+ /*
+ * even if we're attaching all tasks in the thread group, we
+ * only need to check permissions on one of them.
+ */
tcred = __task_cred(tsk);
if (cred->euid &&
cred->euid != tcred->uid &&
cred->euid != tcred->suid) {
rcu_read_unlock();
+ cgroup_unlock();
return -EACCES;
}
get_task_struct(tsk);
rcu_read_unlock();
} else {
- tsk = current;
+ if (threadgroup)
+ tsk = current->group_leader;
+ else
+ tsk = current;
get_task_struct(tsk);
}
- ret = cgroup_attach_task(cgrp, tsk);
+ if (threadgroup) {
+ threadgroup_fork_write_lock(tsk);
+ ret = cgroup_attach_proc(cgrp, tsk);
+ threadgroup_fork_write_unlock(tsk);
+ } else {
+ ret = cgroup_attach_task(cgrp, tsk);
+ }
put_task_struct(tsk);
+ cgroup_unlock();
return ret;
}
static int cgroup_tasks_write(struct cgroup *cgrp, struct cftype *cft, u64 pid)
{
+ return attach_task_by_pid(cgrp, pid, false);
+}
+
+static int cgroup_procs_write(struct cgroup *cgrp, struct cftype *cft, u64 tgid)
+{
int ret;
- if (!cgroup_lock_live_group(cgrp))
- return -ENODEV;
- ret = attach_task_by_pid(cgrp, pid);
- cgroup_unlock();
+ do {
+ /*
+ * attach_proc fails with -EAGAIN if threadgroup leadership
+ * changes in the middle of the operation, in which case we need
+ * to find the task_struct for the new leader and start over.
+ */
+ ret = attach_task_by_pid(cgrp, tgid, true);
+ } while (ret == -EAGAIN);
return ret;
}
@@ -3259,9 +3632,9 @@
{
.name = CGROUP_FILE_GENERIC_PREFIX "procs",
.open = cgroup_procs_open,
- /* .write_u64 = cgroup_procs_write, TODO */
+ .write_u64 = cgroup_procs_write,
.release = cgroup_pidlist_release,
- .mode = S_IRUGO,
+ .mode = S_IRUGO | S_IWUSR,
},
{
.name = "notify_on_release",
@@ -4257,122 +4630,6 @@
}
/**
- * cgroup_clone - clone the cgroup the given subsystem is attached to
- * @tsk: the task to be moved
- * @subsys: the given subsystem
- * @nodename: the name for the new cgroup
- *
- * Duplicate the current cgroup in the hierarchy that the given
- * subsystem is attached to, and move this task into the new
- * child.
- */
-int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
- char *nodename)
-{
- struct dentry *dentry;
- int ret = 0;
- struct cgroup *parent, *child;
- struct inode *inode;
- struct css_set *cg;
- struct cgroupfs_root *root;
- struct cgroup_subsys *ss;
-
- /* We shouldn't be called by an unregistered subsystem */
- BUG_ON(!subsys->active);
-
- /* First figure out what hierarchy and cgroup we're dealing
- * with, and pin them so we can drop cgroup_mutex */
- mutex_lock(&cgroup_mutex);
- again:
- root = subsys->root;
- if (root == &rootnode) {
- mutex_unlock(&cgroup_mutex);
- return 0;
- }
-
- /* Pin the hierarchy */
- if (!atomic_inc_not_zero(&root->sb->s_active)) {
- /* We race with the final deactivate_super() */
- mutex_unlock(&cgroup_mutex);
- return 0;
- }
-
- /* Keep the cgroup alive */
- task_lock(tsk);
- parent = task_cgroup(tsk, subsys->subsys_id);
- cg = tsk->cgroups;
- get_css_set(cg);
- task_unlock(tsk);
-
- mutex_unlock(&cgroup_mutex);
-
- /* Now do the VFS work to create a cgroup */
- inode = parent->dentry->d_inode;
-
- /* Hold the parent directory mutex across this operation to
- * stop anyone else deleting the new cgroup */
- mutex_lock(&inode->i_mutex);
- dentry = lookup_one_len(nodename, parent->dentry, strlen(nodename));
- if (IS_ERR(dentry)) {
- printk(KERN_INFO
- "cgroup: Couldn't allocate dentry for %s: %ld\n", nodename,
- PTR_ERR(dentry));
- ret = PTR_ERR(dentry);
- goto out_release;
- }
-
- /* Create the cgroup directory, which also creates the cgroup */
- ret = vfs_mkdir(inode, dentry, 0755);
- child = __d_cgrp(dentry);
- dput(dentry);
- if (ret) {
- printk(KERN_INFO
- "Failed to create cgroup %s: %d\n", nodename,
- ret);
- goto out_release;
- }
-
- /* The cgroup now exists. Retake cgroup_mutex and check
- * that we're still in the same state that we thought we
- * were. */
- mutex_lock(&cgroup_mutex);
- if ((root != subsys->root) ||
- (parent != task_cgroup(tsk, subsys->subsys_id))) {
- /* Aargh, we raced ... */
- mutex_unlock(&inode->i_mutex);
- put_css_set(cg);
-
- deactivate_super(root->sb);
- /* The cgroup is still accessible in the VFS, but
- * we're not going to try to rmdir() it at this
- * point. */
- printk(KERN_INFO
- "Race in cgroup_clone() - leaking cgroup %s\n",
- nodename);
- goto again;
- }
-
- /* do any required auto-setup */
- for_each_subsys(root, ss) {
- if (ss->post_clone)
- ss->post_clone(ss, child);
- }
-
- /* All seems fine. Finish by moving the task into the new cgroup */
- ret = cgroup_attach_task(child, tsk);
- mutex_unlock(&cgroup_mutex);
-
- out_release:
- mutex_unlock(&inode->i_mutex);
-
- mutex_lock(&cgroup_mutex);
- put_css_set(cg);
- mutex_unlock(&cgroup_mutex);
- deactivate_super(root->sb);
- return ret;
-}
-
-/**
* cgroup_is_descendant - see if @cgrp is a descendant of @task's cgrp
* @cgrp: the cgroup in question
* @task: the task in question
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c
index e7bebb7..e691818 100644
--- a/kernel/cgroup_freezer.c
+++ b/kernel/cgroup_freezer.c
@@ -160,7 +160,7 @@
*/
static int freezer_can_attach(struct cgroup_subsys *ss,
struct cgroup *new_cgroup,
- struct task_struct *task, bool threadgroup)
+ struct task_struct *task)
{
struct freezer *freezer;
@@ -172,26 +172,17 @@
if (freezer->state != CGROUP_THAWED)
return -EBUSY;
+ return 0;
+}
+
+static int freezer_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
+{
rcu_read_lock();
- if (__cgroup_freezing_or_frozen(task)) {
+ if (__cgroup_freezing_or_frozen(tsk)) {
rcu_read_unlock();
return -EBUSY;
}
rcu_read_unlock();
-
- if (threadgroup) {
- struct task_struct *c;
-
- rcu_read_lock();
- list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
- if (__cgroup_freezing_or_frozen(c)) {
- rcu_read_unlock();
- return -EBUSY;
- }
- }
- rcu_read_unlock();
- }
-
return 0;
}
@@ -390,6 +381,9 @@
.populate = freezer_populate,
.subsys_id = freezer_subsys_id,
.can_attach = freezer_can_attach,
+ .can_attach_task = freezer_can_attach_task,
+ .pre_attach = NULL,
+ .attach_task = NULL,
.attach = NULL,
.fork = freezer_fork,
.exit = NULL,
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 2bb8c2e..1ceeb04 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -1367,14 +1367,10 @@
return val;
}
-/* Protected by cgroup_lock */
-static cpumask_var_t cpus_attach;
-
/* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */
static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cont,
- struct task_struct *tsk, bool threadgroup)
+ struct task_struct *tsk)
{
- int ret;
struct cpuset *cs = cgroup_cs(cont);
if (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
@@ -1391,29 +1387,42 @@
if (tsk->flags & PF_THREAD_BOUND)
return -EINVAL;
- ret = security_task_setscheduler(tsk);
- if (ret)
- return ret;
- if (threadgroup) {
- struct task_struct *c;
-
- rcu_read_lock();
- list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
- ret = security_task_setscheduler(c);
- if (ret) {
- rcu_read_unlock();
- return ret;
- }
- }
- rcu_read_unlock();
- }
return 0;
}
-static void cpuset_attach_task(struct task_struct *tsk, nodemask_t *to,
- struct cpuset *cs)
+static int cpuset_can_attach_task(struct cgroup *cgrp, struct task_struct *task)
+{
+ return security_task_setscheduler(task);
+}
+
+/*
+ * Protected by cgroup_lock. The nodemasks must be stored globally because
+ * dynamically allocating them is not allowed in pre_attach, and they must
+ * persist among pre_attach, attach_task, and attach.
+ */
+static cpumask_var_t cpus_attach;
+static nodemask_t cpuset_attach_nodemask_from;
+static nodemask_t cpuset_attach_nodemask_to;
+
+/* Set-up work for before attaching each task. */
+static void cpuset_pre_attach(struct cgroup *cont)
+{
+ struct cpuset *cs = cgroup_cs(cont);
+
+ if (cs == &top_cpuset)
+ cpumask_copy(cpus_attach, cpu_possible_mask);
+ else
+ guarantee_online_cpus(cs, cpus_attach);
+
+ guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
+}
+
+/* Per-thread attachment work. */
+static void cpuset_attach_task(struct cgroup *cont, struct task_struct *tsk)
{
int err;
+ struct cpuset *cs = cgroup_cs(cont);
+
/*
* can_attach beforehand should guarantee that this doesn't fail.
* TODO: have a better way to handle failure here
@@ -1421,45 +1430,29 @@
err = set_cpus_allowed_ptr(tsk, cpus_attach);
WARN_ON_ONCE(err);
- cpuset_change_task_nodemask(tsk, to);
+ cpuset_change_task_nodemask(tsk, &cpuset_attach_nodemask_to);
cpuset_update_task_spread_flag(cs, tsk);
-
}
static void cpuset_attach(struct cgroup_subsys *ss, struct cgroup *cont,
- struct cgroup *oldcont, struct task_struct *tsk,
- bool threadgroup)
+ struct cgroup *oldcont, struct task_struct *tsk)
{
struct mm_struct *mm;
struct cpuset *cs = cgroup_cs(cont);
struct cpuset *oldcs = cgroup_cs(oldcont);
- static nodemask_t to; /* protected by cgroup_mutex */
- if (cs == &top_cpuset) {
- cpumask_copy(cpus_attach, cpu_possible_mask);
- } else {
- guarantee_online_cpus(cs, cpus_attach);
- }
- guarantee_online_mems(cs, &to);
-
- /* do per-task migration stuff possibly for each in the threadgroup */
- cpuset_attach_task(tsk, &to, cs);
- if (threadgroup) {
- struct task_struct *c;
- rcu_read_lock();
- list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
- cpuset_attach_task(c, &to, cs);
- }
- rcu_read_unlock();
- }
-
- /* change mm; only needs to be done once even if threadgroup */
- to = cs->mems_allowed;
+ /*
+ * Change mm, possibly for multiple threads in a threadgroup. This is
+ * expensive and may sleep.
+ */
+ cpuset_attach_nodemask_from = oldcs->mems_allowed;
+ cpuset_attach_nodemask_to = cs->mems_allowed;
mm = get_task_mm(tsk);
if (mm) {
- mpol_rebind_mm(mm, &to);
+ mpol_rebind_mm(mm, &cpuset_attach_nodemask_to);
if (is_memory_migrate(cs))
- cpuset_migrate_mm(mm, &oldcs->mems_allowed, &to);
+ cpuset_migrate_mm(mm, &cpuset_attach_nodemask_from,
+ &cpuset_attach_nodemask_to);
mmput(mm);
}
}
@@ -1809,10 +1802,9 @@
}
/*
- * post_clone() is called at the end of cgroup_clone().
- * 'cgroup' was just created automatically as a result of
- * a cgroup_clone(), and the current task is about to
- * be moved into 'cgroup'.
+ * post_clone() is called during cgroup_create() when the
+ * clone_children mount argument was specified. The cgroup
+ * can not yet have any tasks.
*
* Currently we refuse to set up the cgroup - thereby
* refusing the task to be entered, and as a result refusing
@@ -1911,6 +1903,9 @@
.create = cpuset_create,
.destroy = cpuset_destroy,
.can_attach = cpuset_can_attach,
+ .can_attach_task = cpuset_can_attach_task,
+ .pre_attach = cpuset_pre_attach,
+ .attach_task = cpuset_attach_task,
.attach = cpuset_attach,
.populate = cpuset_populate,
.post_clone = cpuset_post_clone,
diff --git a/kernel/fork.c b/kernel/fork.c
index 8e7e135..ca406d9 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -59,7 +59,6 @@
#include <linux/taskstats_kern.h>
#include <linux/random.h>
#include <linux/tty.h>
-#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/fs_struct.h>
#include <linux/magic.h>
@@ -597,6 +596,57 @@
}
EXPORT_SYMBOL_GPL(mmput);
+/*
+ * We added or removed a vma mapping the executable. The vmas are only mapped
+ * during exec and are not mapped with the mmap system call.
+ * Callers must hold down_write() on the mm's mmap_sem for these
+ */
+void added_exe_file_vma(struct mm_struct *mm)
+{
+ mm->num_exe_file_vmas++;
+}
+
+void removed_exe_file_vma(struct mm_struct *mm)
+{
+ mm->num_exe_file_vmas--;
+ if ((mm->num_exe_file_vmas == 0) && mm->exe_file){
+ fput(mm->exe_file);
+ mm->exe_file = NULL;
+ }
+
+}
+
+void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
+{
+ if (new_exe_file)
+ get_file(new_exe_file);
+ if (mm->exe_file)
+ fput(mm->exe_file);
+ mm->exe_file = new_exe_file;
+ mm->num_exe_file_vmas = 0;
+}
+
+struct file *get_mm_exe_file(struct mm_struct *mm)
+{
+ struct file *exe_file;
+
+ /* We need mmap_sem to protect against races with removal of
+ * VM_EXECUTABLE vmas */
+ down_read(&mm->mmap_sem);
+ exe_file = mm->exe_file;
+ if (exe_file)
+ get_file(exe_file);
+ up_read(&mm->mmap_sem);
+ return exe_file;
+}
+
+static void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm)
+{
+ /* It's safe to write the exe_file pointer without exe_file_lock because
+ * this is called during fork when the task is not yet in /proc */
+ newmm->exe_file = get_mm_exe_file(oldmm);
+}
+
/**
* get_task_mm - acquire a reference to the task's mm
*
@@ -957,6 +1007,10 @@
tty_audit_fork(sig);
sched_autogroup_fork(sig);
+#ifdef CONFIG_CGROUPS
+ init_rwsem(&sig->threadgroup_fork_lock);
+#endif
+
sig->oom_adj = current->signal->oom_adj;
sig->oom_score_adj = current->signal->oom_score_adj;
sig->oom_score_adj_min = current->signal->oom_score_adj_min;
@@ -1138,6 +1192,8 @@
monotonic_to_bootbased(&p->real_start_time);
p->io_context = NULL;
p->audit_context = NULL;
+ if (clone_flags & CLONE_THREAD)
+ threadgroup_fork_read_lock(current);
cgroup_fork(p);
#ifdef CONFIG_NUMA
p->mempolicy = mpol_dup(p->mempolicy);
@@ -1223,12 +1279,6 @@
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
- if (current->nsproxy != p->nsproxy) {
- retval = ns_cgroup_clone(p, pid);
- if (retval)
- goto bad_fork_free_pid;
- }
-
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
/*
* Clear TID on mm_release()?
@@ -1342,6 +1392,8 @@
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
cgroup_post_fork(p);
+ if (clone_flags & CLONE_THREAD)
+ threadgroup_fork_read_unlock(current);
perf_event_fork(p);
return p;
@@ -1380,6 +1432,8 @@
mpol_put(p->mempolicy);
bad_fork_cleanup_cgroup:
#endif
+ if (clone_flags & CLONE_THREAD)
+ threadgroup_fork_read_unlock(current);
cgroup_exit(p, cgroup_callbacks_done);
delayacct_tsk_free(p);
module_put(task_thread_info(p)->exec_domain->module);
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index 64e3df6..4bd4faa 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -352,6 +352,7 @@
#ifdef CONFIG_SMP
remove_proc_entry("smp_affinity", desc->dir);
remove_proc_entry("affinity_hint", desc->dir);
+ remove_proc_entry("smp_affinity_list", desc->dir);
remove_proc_entry("node", desc->dir);
#endif
remove_proc_entry("spurious", desc->dir);
diff --git a/kernel/ns_cgroup.c b/kernel/ns_cgroup.c
deleted file mode 100644
index 2c98ad9..0000000
--- a/kernel/ns_cgroup.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * ns_cgroup.c - namespace cgroup subsystem
- *
- * Copyright 2006, 2007 IBM Corp
- */
-
-#include <linux/module.h>
-#include <linux/cgroup.h>
-#include <linux/fs.h>
-#include <linux/proc_fs.h>
-#include <linux/slab.h>
-#include <linux/nsproxy.h>
-
-struct ns_cgroup {
- struct cgroup_subsys_state css;
-};
-
-struct cgroup_subsys ns_subsys;
-
-static inline struct ns_cgroup *cgroup_to_ns(
- struct cgroup *cgroup)
-{
- return container_of(cgroup_subsys_state(cgroup, ns_subsys_id),
- struct ns_cgroup, css);
-}
-
-int ns_cgroup_clone(struct task_struct *task, struct pid *pid)
-{
- char name[PROC_NUMBUF];
-
- snprintf(name, PROC_NUMBUF, "%d", pid_vnr(pid));
- return cgroup_clone(task, &ns_subsys, name);
-}
-
-/*
- * Rules:
- * 1. you can only enter a cgroup which is a descendant of your current
- * cgroup
- * 2. you can only place another process into a cgroup if
- * a. you have CAP_SYS_ADMIN
- * b. your cgroup is an ancestor of task's destination cgroup
- * (hence either you are in the same cgroup as task, or in an
- * ancestor cgroup thereof)
- */
-static int ns_can_attach(struct cgroup_subsys *ss, struct cgroup *new_cgroup,
- struct task_struct *task, bool threadgroup)
-{
- if (current != task) {
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (!cgroup_is_descendant(new_cgroup, current))
- return -EPERM;
- }
-
- if (!cgroup_is_descendant(new_cgroup, task))
- return -EPERM;
-
- if (threadgroup) {
- struct task_struct *c;
- rcu_read_lock();
- list_for_each_entry_rcu(c, &task->thread_group, thread_group) {
- if (!cgroup_is_descendant(new_cgroup, c)) {
- rcu_read_unlock();
- return -EPERM;
- }
- }
- rcu_read_unlock();
- }
-
- return 0;
-}
-
-/*
- * Rules: you can only create a cgroup if
- * 1. you are capable(CAP_SYS_ADMIN)
- * 2. the target cgroup is a descendant of your own cgroup
- */
-static struct cgroup_subsys_state *ns_create(struct cgroup_subsys *ss,
- struct cgroup *cgroup)
-{
- struct ns_cgroup *ns_cgroup;
-
- if (!capable(CAP_SYS_ADMIN))
- return ERR_PTR(-EPERM);
- if (!cgroup_is_descendant(cgroup, current))
- return ERR_PTR(-EPERM);
- if (test_bit(CGRP_CLONE_CHILDREN, &cgroup->flags)) {
- printk("ns_cgroup can't be created with parent "
- "'clone_children' set.\n");
- return ERR_PTR(-EINVAL);
- }
-
- printk_once("ns_cgroup deprecated: consider using the "
- "'clone_children' flag without the ns_cgroup.\n");
-
- ns_cgroup = kzalloc(sizeof(*ns_cgroup), GFP_KERNEL);
- if (!ns_cgroup)
- return ERR_PTR(-ENOMEM);
- return &ns_cgroup->css;
-}
-
-static void ns_destroy(struct cgroup_subsys *ss,
- struct cgroup *cgroup)
-{
- struct ns_cgroup *ns_cgroup;
-
- ns_cgroup = cgroup_to_ns(cgroup);
- kfree(ns_cgroup);
-}
-
-struct cgroup_subsys ns_subsys = {
- .name = "ns",
- .can_attach = ns_can_attach,
- .create = ns_create,
- .destroy = ns_destroy,
- .subsys_id = ns_subsys_id,
-};
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 5424e37..d6a00f3 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -201,10 +201,6 @@
goto out;
}
- err = ns_cgroup_clone(current, task_pid(current));
- if (err)
- put_nsproxy(*new_nsp);
-
out:
return err;
}
diff --git a/kernel/profile.c b/kernel/profile.c
index 14c9f87..961b389 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -303,14 +303,12 @@
mutex_unlock(&profile_flip_mutex);
}
-void profile_hits(int type, void *__pc, unsigned int nr_hits)
+static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
{
unsigned long primary, secondary, flags, pc = (unsigned long)__pc;
int i, j, cpu;
struct profile_hit *hits;
- if (prof_on != type || !prof_buffer)
- return;
pc = min((pc - (unsigned long)_stext) >> prof_shift, prof_len - 1);
i = primary = (pc & (NR_PROFILE_GRP - 1)) << PROFILE_GRPSHIFT;
secondary = (~(pc << 1) & (NR_PROFILE_GRP - 1)) << PROFILE_GRPSHIFT;
@@ -417,16 +415,20 @@
#define profile_discard_flip_buffers() do { } while (0)
#define profile_cpu_callback NULL
-void profile_hits(int type, void *__pc, unsigned int nr_hits)
+static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
{
unsigned long pc;
-
- if (prof_on != type || !prof_buffer)
- return;
pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift;
atomic_add(nr_hits, &prof_buffer[min(pc, prof_len - 1)]);
}
#endif /* !CONFIG_SMP */
+
+void profile_hits(int type, void *__pc, unsigned int nr_hits)
+{
+ if (prof_on != type || !prof_buffer)
+ return;
+ do_profile_hits(type, __pc, nr_hits);
+}
EXPORT_SYMBOL_GPL(profile_hits);
void profile_tick(int type)
diff --git a/kernel/sched.c b/kernel/sched.c
index 2d12893..5e43e9d 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -8764,42 +8764,10 @@
return 0;
}
-static int
-cpu_cgroup_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct task_struct *tsk, bool threadgroup)
-{
- int retval = cpu_cgroup_can_attach_task(cgrp, tsk);
- if (retval)
- return retval;
- if (threadgroup) {
- struct task_struct *c;
- rcu_read_lock();
- list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
- retval = cpu_cgroup_can_attach_task(cgrp, c);
- if (retval) {
- rcu_read_unlock();
- return retval;
- }
- }
- rcu_read_unlock();
- }
- return 0;
-}
-
static void
-cpu_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp,
- struct cgroup *old_cont, struct task_struct *tsk,
- bool threadgroup)
+cpu_cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
{
sched_move_task(tsk);
- if (threadgroup) {
- struct task_struct *c;
- rcu_read_lock();
- list_for_each_entry_rcu(c, &tsk->thread_group, thread_group) {
- sched_move_task(c);
- }
- rcu_read_unlock();
- }
}
static void
@@ -8887,8 +8855,8 @@
.name = "cpu",
.create = cpu_cgroup_create,
.destroy = cpu_cgroup_destroy,
- .can_attach = cpu_cgroup_can_attach,
- .attach = cpu_cgroup_attach,
+ .can_attach_task = cpu_cgroup_can_attach_task,
+ .attach_task = cpu_cgroup_attach_task,
.exit = cpu_cgroup_exit,
.populate = cpu_cgroup_populate,
.subsys_id = cpu_cgroup_subsys_id,
diff --git a/lib/Kconfig b/lib/Kconfig
index 9c10e38..830181c 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -19,16 +19,6 @@
config GENERIC_FIND_FIRST_BIT
bool
-config GENERIC_FIND_NEXT_BIT
- bool
-
-config GENERIC_FIND_BIT_LE
- bool
-
-config GENERIC_FIND_LAST_BIT
- bool
- default y
-
config CRC_CCITT
tristate "CRC-CCITT functions"
help
diff --git a/lib/Makefile b/lib/Makefile
index 4b49a24..6b597fd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -12,7 +12,7 @@
idr.o int_sqrt.o extable.o prio_tree.o \
sha1.o irq_regs.o reciprocal_div.o argv_split.o \
proportions.o prio_heap.o ratelimit.o show_mem.o \
- is_single_threaded.o plist.o decompress.o
+ is_single_threaded.o plist.o decompress.o find_next_bit.o
lib-$(CONFIG_MMU) += ioremap.o
lib-$(CONFIG_SMP) += cpumask.o
@@ -22,7 +22,7 @@
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \
- bsearch.o
+ bsearch.o find_last_bit.o
obj-y += kstrtox.o
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
@@ -39,10 +39,6 @@
obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
-lib-$(CONFIG_GENERIC_FIND_FIRST_BIT) += find_next_bit.o
-lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
-lib-$(CONFIG_GENERIC_FIND_BIT_LE) += find_next_bit.o
-obj-$(CONFIG_GENERIC_FIND_LAST_BIT) += find_last_bit.o
CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS))
obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
diff --git a/lib/find_last_bit.c b/lib/find_last_bit.c
index 5d202e3..d903959 100644
--- a/lib/find_last_bit.c
+++ b/lib/find_last_bit.c
@@ -15,6 +15,8 @@
#include <asm/types.h>
#include <asm/byteorder.h>
+#ifndef find_last_bit
+
unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
{
unsigned long words;
@@ -43,3 +45,5 @@
return size;
}
EXPORT_SYMBOL(find_last_bit);
+
+#endif
diff --git a/lib/find_next_bit.c b/lib/find_next_bit.c
index b0a8767..4bd75a7 100644
--- a/lib/find_next_bit.c
+++ b/lib/find_next_bit.c
@@ -16,7 +16,7 @@
#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
-#ifdef CONFIG_GENERIC_FIND_NEXT_BIT
+#ifndef find_next_bit
/*
* Find the next set bit in a memory region.
*/
@@ -59,7 +59,9 @@
return result + __ffs(tmp);
}
EXPORT_SYMBOL(find_next_bit);
+#endif
+#ifndef find_next_zero_bit
/*
* This implementation of find_{first,next}_zero_bit was stolen from
* Linus' asm-alpha/bitops.h.
@@ -103,9 +105,9 @@
return result + ffz(tmp);
}
EXPORT_SYMBOL(find_next_zero_bit);
-#endif /* CONFIG_GENERIC_FIND_NEXT_BIT */
+#endif
-#ifdef CONFIG_GENERIC_FIND_FIRST_BIT
+#ifndef find_first_bit
/*
* Find the first set bit in a memory region.
*/
@@ -131,7 +133,9 @@
return result + __ffs(tmp);
}
EXPORT_SYMBOL(find_first_bit);
+#endif
+#ifndef find_first_zero_bit
/*
* Find the first cleared bit in a memory region.
*/
@@ -157,10 +161,9 @@
return result + ffz(tmp);
}
EXPORT_SYMBOL(find_first_zero_bit);
-#endif /* CONFIG_GENERIC_FIND_FIRST_BIT */
+#endif
#ifdef __BIG_ENDIAN
-#ifdef CONFIG_GENERIC_FIND_BIT_LE
/* include/linux/byteorder does not support "unsigned long" type */
static inline unsigned long ext2_swabp(const unsigned long * x)
@@ -186,6 +189,7 @@
#endif
}
+#ifndef find_next_zero_bit_le
unsigned long find_next_zero_bit_le(const void *addr, unsigned
long size, unsigned long offset)
{
@@ -229,7 +233,9 @@
return result + ffz(ext2_swab(tmp));
}
EXPORT_SYMBOL(find_next_zero_bit_le);
+#endif
+#ifndef find_next_bit_le
unsigned long find_next_bit_le(const void *addr, unsigned
long size, unsigned long offset)
{
@@ -274,6 +280,6 @@
return result + __ffs(ext2_swab(tmp));
}
EXPORT_SYMBOL(find_next_bit_le);
+#endif
-#endif /* CONFIG_GENERIC_FIND_BIT_LE */
#endif /* __BIG_ENDIAN */
diff --git a/lib/flex_array.c b/lib/flex_array.c
index cab7621..9b8b894 100644
--- a/lib/flex_array.c
+++ b/lib/flex_array.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/module.h>
+#include <linux/reciprocal_div.h>
struct flex_array_part {
char elements[FLEX_ARRAY_PART_SIZE];
@@ -70,15 +71,15 @@
* Element size | Objects | Objects |
* PAGE_SIZE=4k | 32-bit | 64-bit |
* ---------------------------------|
- * 1 bytes | 4186112 | 2093056 |
- * 2 bytes | 2093056 | 1046528 |
- * 3 bytes | 1395030 | 697515 |
- * 4 bytes | 1046528 | 523264 |
- * 32 bytes | 130816 | 65408 |
- * 33 bytes | 126728 | 63364 |
- * 2048 bytes | 2044 | 1022 |
- * 2049 bytes | 1022 | 511 |
- * void * | 1046528 | 261632 |
+ * 1 bytes | 4177920 | 2088960 |
+ * 2 bytes | 2088960 | 1044480 |
+ * 3 bytes | 1392300 | 696150 |
+ * 4 bytes | 1044480 | 522240 |
+ * 32 bytes | 130560 | 65408 |
+ * 33 bytes | 126480 | 63240 |
+ * 2048 bytes | 2040 | 1020 |
+ * 2049 bytes | 1020 | 510 |
+ * void * | 1044480 | 261120 |
*
* Since 64-bit pointers are twice the size, we lose half the
* capacity in the base structure. Also note that no effort is made
@@ -88,11 +89,15 @@
gfp_t flags)
{
struct flex_array *ret;
+ int elems_per_part = 0;
+ int reciprocal_elems = 0;
int max_size = 0;
- if (element_size)
- max_size = FLEX_ARRAY_NR_BASE_PTRS *
- FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
+ if (element_size) {
+ elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
+ reciprocal_elems = reciprocal_value(elems_per_part);
+ max_size = FLEX_ARRAY_NR_BASE_PTRS * elems_per_part;
+ }
/* max_size will end up 0 if element_size > PAGE_SIZE */
if (total > max_size)
@@ -102,6 +107,8 @@
return NULL;
ret->element_size = element_size;
ret->total_nr_elements = total;
+ ret->elems_per_part = elems_per_part;
+ ret->reciprocal_elems = reciprocal_elems;
if (elements_fit_in_base(ret) && !(flags & __GFP_ZERO))
memset(&ret->parts[0], FLEX_ARRAY_FREE,
FLEX_ARRAY_BASE_BYTES_LEFT);
@@ -112,7 +119,7 @@
static int fa_element_to_part_nr(struct flex_array *fa,
unsigned int element_nr)
{
- return element_nr / FLEX_ARRAY_ELEMENTS_PER_PART(fa->element_size);
+ return reciprocal_divide(element_nr, fa->reciprocal_elems);
}
/**
@@ -141,12 +148,12 @@
EXPORT_SYMBOL(flex_array_free);
static unsigned int index_inside_part(struct flex_array *fa,
- unsigned int element_nr)
+ unsigned int element_nr,
+ unsigned int part_nr)
{
unsigned int part_offset;
- part_offset = element_nr %
- FLEX_ARRAY_ELEMENTS_PER_PART(fa->element_size);
+ part_offset = element_nr - part_nr * fa->elems_per_part;
return part_offset * fa->element_size;
}
@@ -186,7 +193,7 @@
int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src,
gfp_t flags)
{
- int part_nr;
+ int part_nr = 0;
struct flex_array_part *part;
void *dst;
@@ -202,7 +209,7 @@
if (!part)
return -ENOMEM;
}
- dst = &part->elements[index_inside_part(fa, element_nr)];
+ dst = &part->elements[index_inside_part(fa, element_nr, part_nr)];
memcpy(dst, src, fa->element_size);
return 0;
}
@@ -217,7 +224,7 @@
*/
int flex_array_clear(struct flex_array *fa, unsigned int element_nr)
{
- int part_nr;
+ int part_nr = 0;
struct flex_array_part *part;
void *dst;
@@ -233,7 +240,7 @@
if (!part)
return -EINVAL;
}
- dst = &part->elements[index_inside_part(fa, element_nr)];
+ dst = &part->elements[index_inside_part(fa, element_nr, part_nr)];
memset(dst, FLEX_ARRAY_FREE, fa->element_size);
return 0;
}
@@ -302,7 +309,7 @@
*/
void *flex_array_get(struct flex_array *fa, unsigned int element_nr)
{
- int part_nr;
+ int part_nr = 0;
struct flex_array_part *part;
if (!fa->element_size)
@@ -317,7 +324,7 @@
if (!part)
return NULL;
}
- return &part->elements[index_inside_part(fa, element_nr)];
+ return &part->elements[index_inside_part(fa, element_nr, part_nr)];
}
EXPORT_SYMBOL(flex_array_get);
diff --git a/mm/Kconfig b/mm/Kconfig
index e9c0c61..8ca47a5 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -347,3 +347,26 @@
depends on !SMP
bool
default y
+
+config CLEANCACHE
+ bool "Enable cleancache driver to cache clean pages if tmem is present"
+ default n
+ help
+ Cleancache can be thought of as a page-granularity victim cache
+ for clean pages that the kernel's pageframe replacement algorithm
+ (PFRA) would like to keep around, but can't since there isn't enough
+ memory. So when the PFRA "evicts" a page, it first attempts to use
+ cleancacne code to put the data contained in that page into
+ "transcendent memory", memory that is not directly accessible or
+ addressable by the kernel and is of unknown and possibly
+ time-varying size. And when a cleancache-enabled
+ filesystem wishes to access a page in a file on disk, it first
+ checks cleancache to see if it already contains it; if it does,
+ the page is copied into the kernel and a disk access is avoided.
+ When a transcendent memory driver is available (such as zcache or
+ Xen transcendent memory), a significant I/O reduction
+ may be achieved. When none is available, all cleancache calls
+ are reduced to a single pointer-compare-against-NULL resulting
+ in a negligible performance hit.
+
+ If unsure, say Y to enable cleancache
diff --git a/mm/Makefile b/mm/Makefile
index 42a8326..836e416 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -49,3 +49,4 @@
obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
+obj-$(CONFIG_CLEANCACHE) += cleancache.o
diff --git a/mm/cleancache.c b/mm/cleancache.c
new file mode 100644
index 0000000..bcaae4c
--- /dev/null
+++ b/mm/cleancache.c
@@ -0,0 +1,244 @@
+/*
+ * Cleancache frontend
+ *
+ * This code provides the generic "frontend" layer to call a matching
+ * "backend" driver implementation of cleancache. See
+ * Documentation/vm/cleancache.txt for more information.
+ *
+ * Copyright (C) 2009-2010 Oracle Corp. All rights reserved.
+ * Author: Dan Magenheimer
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/exportfs.h>
+#include <linux/mm.h>
+#include <linux/cleancache.h>
+
+/*
+ * This global enablement flag may be read thousands of times per second
+ * by cleancache_get/put/flush even on systems where cleancache_ops
+ * is not claimed (e.g. cleancache is config'ed on but remains
+ * disabled), so is preferred to the slower alternative: a function
+ * call that checks a non-global.
+ */
+int cleancache_enabled;
+EXPORT_SYMBOL(cleancache_enabled);
+
+/*
+ * cleancache_ops is set by cleancache_ops_register to contain the pointers
+ * to the cleancache "backend" implementation functions.
+ */
+static struct cleancache_ops cleancache_ops;
+
+/* useful stats available in /sys/kernel/mm/cleancache */
+static unsigned long cleancache_succ_gets;
+static unsigned long cleancache_failed_gets;
+static unsigned long cleancache_puts;
+static unsigned long cleancache_flushes;
+
+/*
+ * register operations for cleancache, returning previous thus allowing
+ * detection of multiple backends and possible nesting
+ */
+struct cleancache_ops cleancache_register_ops(struct cleancache_ops *ops)
+{
+ struct cleancache_ops old = cleancache_ops;
+
+ cleancache_ops = *ops;
+ cleancache_enabled = 1;
+ return old;
+}
+EXPORT_SYMBOL(cleancache_register_ops);
+
+/* Called by a cleancache-enabled filesystem at time of mount */
+void __cleancache_init_fs(struct super_block *sb)
+{
+ sb->cleancache_poolid = (*cleancache_ops.init_fs)(PAGE_SIZE);
+}
+EXPORT_SYMBOL(__cleancache_init_fs);
+
+/* Called by a cleancache-enabled clustered filesystem at time of mount */
+void __cleancache_init_shared_fs(char *uuid, struct super_block *sb)
+{
+ sb->cleancache_poolid =
+ (*cleancache_ops.init_shared_fs)(uuid, PAGE_SIZE);
+}
+EXPORT_SYMBOL(__cleancache_init_shared_fs);
+
+/*
+ * If the filesystem uses exportable filehandles, use the filehandle as
+ * the key, else use the inode number.
+ */
+static int cleancache_get_key(struct inode *inode,
+ struct cleancache_filekey *key)
+{
+ int (*fhfn)(struct dentry *, __u32 *fh, int *, int);
+ int len = 0, maxlen = CLEANCACHE_KEY_MAX;
+ struct super_block *sb = inode->i_sb;
+
+ key->u.ino = inode->i_ino;
+ if (sb->s_export_op != NULL) {
+ fhfn = sb->s_export_op->encode_fh;
+ if (fhfn) {
+ struct dentry d;
+ d.d_inode = inode;
+ len = (*fhfn)(&d, &key->u.fh[0], &maxlen, 0);
+ if (len <= 0 || len == 255)
+ return -1;
+ if (maxlen > CLEANCACHE_KEY_MAX)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * "Get" data from cleancache associated with the poolid/inode/index
+ * that were specified when the data was put to cleanache and, if
+ * successful, use it to fill the specified page with data and return 0.
+ * The pageframe is unchanged and returns -1 if the get fails.
+ * Page must be locked by caller.
+ */
+int __cleancache_get_page(struct page *page)
+{
+ int ret = -1;
+ int pool_id;
+ struct cleancache_filekey key = { .u.key = { 0 } };
+
+ VM_BUG_ON(!PageLocked(page));
+ pool_id = page->mapping->host->i_sb->cleancache_poolid;
+ if (pool_id < 0)
+ goto out;
+
+ if (cleancache_get_key(page->mapping->host, &key) < 0)
+ goto out;
+
+ ret = (*cleancache_ops.get_page)(pool_id, key, page->index, page);
+ if (ret == 0)
+ cleancache_succ_gets++;
+ else
+ cleancache_failed_gets++;
+out:
+ return ret;
+}
+EXPORT_SYMBOL(__cleancache_get_page);
+
+/*
+ * "Put" data from a page to cleancache and associate it with the
+ * (previously-obtained per-filesystem) poolid and the page's,
+ * inode and page index. Page must be locked. Note that a put_page
+ * always "succeeds", though a subsequent get_page may succeed or fail.
+ */
+void __cleancache_put_page(struct page *page)
+{
+ int pool_id;
+ struct cleancache_filekey key = { .u.key = { 0 } };
+
+ VM_BUG_ON(!PageLocked(page));
+ pool_id = page->mapping->host->i_sb->cleancache_poolid;
+ if (pool_id >= 0 &&
+ cleancache_get_key(page->mapping->host, &key) >= 0) {
+ (*cleancache_ops.put_page)(pool_id, key, page->index, page);
+ cleancache_puts++;
+ }
+}
+EXPORT_SYMBOL(__cleancache_put_page);
+
+/*
+ * Flush any data from cleancache associated with the poolid and the
+ * page's inode and page index so that a subsequent "get" will fail.
+ */
+void __cleancache_flush_page(struct address_space *mapping, struct page *page)
+{
+ /* careful... page->mapping is NULL sometimes when this is called */
+ int pool_id = mapping->host->i_sb->cleancache_poolid;
+ struct cleancache_filekey key = { .u.key = { 0 } };
+
+ if (pool_id >= 0) {
+ VM_BUG_ON(!PageLocked(page));
+ if (cleancache_get_key(mapping->host, &key) >= 0) {
+ (*cleancache_ops.flush_page)(pool_id, key, page->index);
+ cleancache_flushes++;
+ }
+ }
+}
+EXPORT_SYMBOL(__cleancache_flush_page);
+
+/*
+ * Flush all data from cleancache associated with the poolid and the
+ * mappings's inode so that all subsequent gets to this poolid/inode
+ * will fail.
+ */
+void __cleancache_flush_inode(struct address_space *mapping)
+{
+ int pool_id = mapping->host->i_sb->cleancache_poolid;
+ struct cleancache_filekey key = { .u.key = { 0 } };
+
+ if (pool_id >= 0 && cleancache_get_key(mapping->host, &key) >= 0)
+ (*cleancache_ops.flush_inode)(pool_id, key);
+}
+EXPORT_SYMBOL(__cleancache_flush_inode);
+
+/*
+ * Called by any cleancache-enabled filesystem at time of unmount;
+ * note that pool_id is surrendered and may be reutrned by a subsequent
+ * cleancache_init_fs or cleancache_init_shared_fs
+ */
+void __cleancache_flush_fs(struct super_block *sb)
+{
+ if (sb->cleancache_poolid >= 0) {
+ int old_poolid = sb->cleancache_poolid;
+ sb->cleancache_poolid = -1;
+ (*cleancache_ops.flush_fs)(old_poolid);
+ }
+}
+EXPORT_SYMBOL(__cleancache_flush_fs);
+
+#ifdef CONFIG_SYSFS
+
+/* see Documentation/ABI/xxx/sysfs-kernel-mm-cleancache */
+
+#define CLEANCACHE_SYSFS_RO(_name) \
+ static ssize_t cleancache_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%lu\n", cleancache_##_name); \
+ } \
+ static struct kobj_attribute cleancache_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = cleancache_##_name##_show, \
+ }
+
+CLEANCACHE_SYSFS_RO(succ_gets);
+CLEANCACHE_SYSFS_RO(failed_gets);
+CLEANCACHE_SYSFS_RO(puts);
+CLEANCACHE_SYSFS_RO(flushes);
+
+static struct attribute *cleancache_attrs[] = {
+ &cleancache_succ_gets_attr.attr,
+ &cleancache_failed_gets_attr.attr,
+ &cleancache_puts_attr.attr,
+ &cleancache_flushes_attr.attr,
+ NULL,
+};
+
+static struct attribute_group cleancache_attr_group = {
+ .attrs = cleancache_attrs,
+ .name = "cleancache",
+};
+
+#endif /* CONFIG_SYSFS */
+
+static int __init init_cleancache(void)
+{
+#ifdef CONFIG_SYSFS
+ int err;
+
+ err = sysfs_create_group(mm_kobj, &cleancache_attr_group);
+#endif /* CONFIG_SYSFS */
+ return 0;
+}
+module_init(init_cleancache)
diff --git a/mm/filemap.c b/mm/filemap.c
index 68e782b..bcdc393 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -34,6 +34,7 @@
#include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */
#include <linux/memcontrol.h>
#include <linux/mm_inline.h> /* for page_is_file_cache() */
+#include <linux/cleancache.h>
#include "internal.h"
/*
@@ -118,6 +119,16 @@
{
struct address_space *mapping = page->mapping;
+ /*
+ * if we're uptodate, flush out into the cleancache, otherwise
+ * invalidate any existing cleancache entries. We can't leave
+ * stale data around in the cleancache once our page is gone
+ */
+ if (PageUptodate(page) && PageMappedToDisk(page))
+ cleancache_put_page(page);
+ else
+ cleancache_flush_page(mapping, page);
+
radix_tree_delete(&mapping->page_tree, page->index);
page->mapping = NULL;
mapping->nrpages--;
@@ -1650,6 +1661,7 @@
/* No page in the page cache at all */
do_sync_mmap_readahead(vma, ra, file, offset);
count_vm_event(PGMAJFAULT);
+ mem_cgroup_count_vm_event(vma->vm_mm, PGMAJFAULT);
ret = VM_FAULT_MAJOR;
retry_find:
page = find_get_page(mapping, offset);
diff --git a/mm/fremap.c b/mm/fremap.c
index 7f41230..b8e0e2d 100644
--- a/mm/fremap.c
+++ b/mm/fremap.c
@@ -224,7 +224,7 @@
/*
* drop PG_Mlocked flag for over-mapped range
*/
- unsigned int saved_flags = vma->vm_flags;
+ vm_flags_t saved_flags = vma->vm_flags;
munlock_vma_pages_range(vma, start, start + size);
vma->vm_flags = saved_flags;
}
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5fd68b9..f33bb31 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -2833,7 +2833,7 @@
int hugetlb_reserve_pages(struct inode *inode,
long from, long to,
struct vm_area_struct *vma,
- int acctflag)
+ vm_flags_t vm_flags)
{
long ret, chg;
struct hstate *h = hstate_inode(inode);
@@ -2843,7 +2843,7 @@
* attempt will be made for VM_NORESERVE to allocate a page
* and filesystem quota without using reserves
*/
- if (acctflag & VM_NORESERVE)
+ if (vm_flags & VM_NORESERVE)
return 0;
/*
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index d5fd3dc..bd9052a 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -94,6 +94,8 @@
MEM_CGROUP_EVENTS_PGPGIN, /* # of pages paged in */
MEM_CGROUP_EVENTS_PGPGOUT, /* # of pages paged out */
MEM_CGROUP_EVENTS_COUNT, /* # of pages paged in/out */
+ MEM_CGROUP_EVENTS_PGFAULT, /* # of page-faults */
+ MEM_CGROUP_EVENTS_PGMAJFAULT, /* # of major page-faults */
MEM_CGROUP_EVENTS_NSTATS,
};
/*
@@ -231,6 +233,11 @@
* reclaimed from.
*/
int last_scanned_child;
+ int last_scanned_node;
+#if MAX_NUMNODES > 1
+ nodemask_t scan_nodes;
+ unsigned long next_scan_node_update;
+#endif
/*
* Should the accounting and control be hierarchical, per subtree?
*/
@@ -585,6 +592,16 @@
this_cpu_add(mem->stat->count[MEM_CGROUP_STAT_SWAPOUT], val);
}
+void mem_cgroup_pgfault(struct mem_cgroup *mem, int val)
+{
+ this_cpu_add(mem->stat->events[MEM_CGROUP_EVENTS_PGFAULT], val);
+}
+
+void mem_cgroup_pgmajfault(struct mem_cgroup *mem, int val)
+{
+ this_cpu_add(mem->stat->events[MEM_CGROUP_EVENTS_PGMAJFAULT], val);
+}
+
static unsigned long mem_cgroup_read_events(struct mem_cgroup *mem,
enum mem_cgroup_events_index idx)
{
@@ -624,18 +641,27 @@
preempt_enable();
}
+static unsigned long
+mem_cgroup_get_zonestat_node(struct mem_cgroup *mem, int nid, enum lru_list idx)
+{
+ struct mem_cgroup_per_zone *mz;
+ u64 total = 0;
+ int zid;
+
+ for (zid = 0; zid < MAX_NR_ZONES; zid++) {
+ mz = mem_cgroup_zoneinfo(mem, nid, zid);
+ total += MEM_CGROUP_ZSTAT(mz, idx);
+ }
+ return total;
+}
static unsigned long mem_cgroup_get_local_zonestat(struct mem_cgroup *mem,
enum lru_list idx)
{
- int nid, zid;
- struct mem_cgroup_per_zone *mz;
+ int nid;
u64 total = 0;
for_each_online_node(nid)
- for (zid = 0; zid < MAX_NR_ZONES; zid++) {
- mz = mem_cgroup_zoneinfo(mem, nid, zid);
- total += MEM_CGROUP_ZSTAT(mz, idx);
- }
+ total += mem_cgroup_get_zonestat_node(mem, nid, idx);
return total;
}
@@ -813,6 +839,33 @@
return (mem == root_mem_cgroup);
}
+void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
+{
+ struct mem_cgroup *mem;
+
+ if (!mm)
+ return;
+
+ rcu_read_lock();
+ mem = mem_cgroup_from_task(rcu_dereference(mm->owner));
+ if (unlikely(!mem))
+ goto out;
+
+ switch (idx) {
+ case PGMAJFAULT:
+ mem_cgroup_pgmajfault(mem, 1);
+ break;
+ case PGFAULT:
+ mem_cgroup_pgfault(mem, 1);
+ break;
+ default:
+ BUG();
+ }
+out:
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(mem_cgroup_count_vm_event);
+
/*
* Following LRU functions are allowed to be used without PCG_LOCK.
* Operations are called by routine of global LRU independently from memcg.
@@ -1064,9 +1117,9 @@
return (active > inactive);
}
-unsigned long mem_cgroup_zone_nr_pages(struct mem_cgroup *memcg,
- struct zone *zone,
- enum lru_list lru)
+unsigned long mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg,
+ struct zone *zone,
+ enum lru_list lru)
{
int nid = zone_to_nid(zone);
int zid = zone_idx(zone);
@@ -1075,6 +1128,93 @@
return MEM_CGROUP_ZSTAT(mz, lru);
}
+#ifdef CONFIG_NUMA
+static unsigned long mem_cgroup_node_nr_file_lru_pages(struct mem_cgroup *memcg,
+ int nid)
+{
+ unsigned long ret;
+
+ ret = mem_cgroup_get_zonestat_node(memcg, nid, LRU_INACTIVE_FILE) +
+ mem_cgroup_get_zonestat_node(memcg, nid, LRU_ACTIVE_FILE);
+
+ return ret;
+}
+
+static unsigned long mem_cgroup_nr_file_lru_pages(struct mem_cgroup *memcg)
+{
+ u64 total = 0;
+ int nid;
+
+ for_each_node_state(nid, N_HIGH_MEMORY)
+ total += mem_cgroup_node_nr_file_lru_pages(memcg, nid);
+
+ return total;
+}
+
+static unsigned long mem_cgroup_node_nr_anon_lru_pages(struct mem_cgroup *memcg,
+ int nid)
+{
+ unsigned long ret;
+
+ ret = mem_cgroup_get_zonestat_node(memcg, nid, LRU_INACTIVE_ANON) +
+ mem_cgroup_get_zonestat_node(memcg, nid, LRU_ACTIVE_ANON);
+
+ return ret;
+}
+
+static unsigned long mem_cgroup_nr_anon_lru_pages(struct mem_cgroup *memcg)
+{
+ u64 total = 0;
+ int nid;
+
+ for_each_node_state(nid, N_HIGH_MEMORY)
+ total += mem_cgroup_node_nr_anon_lru_pages(memcg, nid);
+
+ return total;
+}
+
+static unsigned long
+mem_cgroup_node_nr_unevictable_lru_pages(struct mem_cgroup *memcg, int nid)
+{
+ return mem_cgroup_get_zonestat_node(memcg, nid, LRU_UNEVICTABLE);
+}
+
+static unsigned long
+mem_cgroup_nr_unevictable_lru_pages(struct mem_cgroup *memcg)
+{
+ u64 total = 0;
+ int nid;
+
+ for_each_node_state(nid, N_HIGH_MEMORY)
+ total += mem_cgroup_node_nr_unevictable_lru_pages(memcg, nid);
+
+ return total;
+}
+
+static unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
+ int nid)
+{
+ enum lru_list l;
+ u64 total = 0;
+
+ for_each_lru(l)
+ total += mem_cgroup_get_zonestat_node(memcg, nid, l);
+
+ return total;
+}
+
+static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *memcg)
+{
+ u64 total = 0;
+ int nid;
+
+ for_each_node_state(nid, N_HIGH_MEMORY)
+ total += mem_cgroup_node_nr_lru_pages(memcg, nid);
+
+ return total;
+}
+#endif /* CONFIG_NUMA */
+
struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
struct zone *zone)
{
@@ -1418,6 +1558,81 @@
return ret;
}
+#if MAX_NUMNODES > 1
+
+/*
+ * Always updating the nodemask is not very good - even if we have an empty
+ * list or the wrong list here, we can start from some node and traverse all
+ * nodes based on the zonelist. So update the list loosely once per 10 secs.
+ *
+ */
+static void mem_cgroup_may_update_nodemask(struct mem_cgroup *mem)
+{
+ int nid;
+
+ if (time_after(mem->next_scan_node_update, jiffies))
+ return;
+
+ mem->next_scan_node_update = jiffies + 10*HZ;
+ /* make a nodemask where this memcg uses memory from */
+ mem->scan_nodes = node_states[N_HIGH_MEMORY];
+
+ for_each_node_mask(nid, node_states[N_HIGH_MEMORY]) {
+
+ if (mem_cgroup_get_zonestat_node(mem, nid, LRU_INACTIVE_FILE) ||
+ mem_cgroup_get_zonestat_node(mem, nid, LRU_ACTIVE_FILE))
+ continue;
+
+ if (total_swap_pages &&
+ (mem_cgroup_get_zonestat_node(mem, nid, LRU_INACTIVE_ANON) ||
+ mem_cgroup_get_zonestat_node(mem, nid, LRU_ACTIVE_ANON)))
+ continue;
+ node_clear(nid, mem->scan_nodes);
+ }
+}
+
+/*
+ * Selecting a node where we start reclaim from. Because what we need is just
+ * reducing usage counter, start from anywhere is O,K. Considering
+ * memory reclaim from current node, there are pros. and cons.
+ *
+ * Freeing memory from current node means freeing memory from a node which
+ * we'll use or we've used. So, it may make LRU bad. And if several threads
+ * hit limits, it will see a contention on a node. But freeing from remote
+ * node means more costs for memory reclaim because of memory latency.
+ *
+ * Now, we use round-robin. Better algorithm is welcomed.
+ */
+int mem_cgroup_select_victim_node(struct mem_cgroup *mem)
+{
+ int node;
+
+ mem_cgroup_may_update_nodemask(mem);
+ node = mem->last_scanned_node;
+
+ node = next_node(node, mem->scan_nodes);
+ if (node == MAX_NUMNODES)
+ node = first_node(mem->scan_nodes);
+ /*
+ * We call this when we hit limit, not when pages are added to LRU.
+ * No LRU may hold pages because all pages are UNEVICTABLE or
+ * memcg is too small and all pages are not on LRU. In that case,
+ * we use curret node.
+ */
+ if (unlikely(node == MAX_NUMNODES))
+ node = numa_node_id();
+
+ mem->last_scanned_node = node;
+ return node;
+}
+
+#else
+int mem_cgroup_select_victim_node(struct mem_cgroup *mem)
+{
+ return 0;
+}
+#endif
+
/*
* Scan the hierarchy if needed to reclaim memory. We remember the last child
* we reclaimed from, so that we don't end up penalizing one child extensively
@@ -1433,7 +1648,8 @@
static int mem_cgroup_hierarchical_reclaim(struct mem_cgroup *root_mem,
struct zone *zone,
gfp_t gfp_mask,
- unsigned long reclaim_options)
+ unsigned long reclaim_options,
+ unsigned long *total_scanned)
{
struct mem_cgroup *victim;
int ret, total = 0;
@@ -1442,6 +1658,7 @@
bool shrink = reclaim_options & MEM_CGROUP_RECLAIM_SHRINK;
bool check_soft = reclaim_options & MEM_CGROUP_RECLAIM_SOFT;
unsigned long excess;
+ unsigned long nr_scanned;
excess = res_counter_soft_limit_excess(&root_mem->res) >> PAGE_SHIFT;
@@ -1484,10 +1701,12 @@
continue;
}
/* we use swappiness of local cgroup */
- if (check_soft)
+ if (check_soft) {
ret = mem_cgroup_shrink_node_zone(victim, gfp_mask,
- noswap, get_swappiness(victim), zone);
- else
+ noswap, get_swappiness(victim), zone,
+ &nr_scanned);
+ *total_scanned += nr_scanned;
+ } else
ret = try_to_free_mem_cgroup_pages(victim, gfp_mask,
noswap, get_swappiness(victim));
css_put(&victim->css);
@@ -1503,7 +1722,7 @@
if (!res_counter_soft_limit_excess(&root_mem->res))
return total;
} else if (mem_cgroup_margin(root_mem))
- return 1 + total;
+ return total;
}
return total;
}
@@ -1928,7 +2147,7 @@
return CHARGE_WOULDBLOCK;
ret = mem_cgroup_hierarchical_reclaim(mem_over_limit, NULL,
- gfp_mask, flags);
+ gfp_mask, flags, NULL);
if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
return CHARGE_RETRY;
/*
@@ -3211,7 +3430,8 @@
break;
mem_cgroup_hierarchical_reclaim(memcg, NULL, GFP_KERNEL,
- MEM_CGROUP_RECLAIM_SHRINK);
+ MEM_CGROUP_RECLAIM_SHRINK,
+ NULL);
curusage = res_counter_read_u64(&memcg->res, RES_USAGE);
/* Usage is reduced ? */
if (curusage >= oldusage)
@@ -3271,7 +3491,8 @@
mem_cgroup_hierarchical_reclaim(memcg, NULL, GFP_KERNEL,
MEM_CGROUP_RECLAIM_NOSWAP |
- MEM_CGROUP_RECLAIM_SHRINK);
+ MEM_CGROUP_RECLAIM_SHRINK,
+ NULL);
curusage = res_counter_read_u64(&memcg->memsw, RES_USAGE);
/* Usage is reduced ? */
if (curusage >= oldusage)
@@ -3285,7 +3506,8 @@
}
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
- gfp_t gfp_mask)
+ gfp_t gfp_mask,
+ unsigned long *total_scanned)
{
unsigned long nr_reclaimed = 0;
struct mem_cgroup_per_zone *mz, *next_mz = NULL;
@@ -3293,6 +3515,7 @@
int loop = 0;
struct mem_cgroup_tree_per_zone *mctz;
unsigned long long excess;
+ unsigned long nr_scanned;
if (order > 0)
return 0;
@@ -3311,10 +3534,13 @@
if (!mz)
break;
+ nr_scanned = 0;
reclaimed = mem_cgroup_hierarchical_reclaim(mz->mem, zone,
gfp_mask,
- MEM_CGROUP_RECLAIM_SOFT);
+ MEM_CGROUP_RECLAIM_SOFT,
+ &nr_scanned);
nr_reclaimed += reclaimed;
+ *total_scanned += nr_scanned;
spin_lock(&mctz->lock);
/*
@@ -3337,10 +3563,9 @@
*/
next_mz =
__mem_cgroup_largest_soft_limit_node(mctz);
- if (next_mz == mz) {
+ if (next_mz == mz)
css_put(&next_mz->mem->css);
- next_mz = NULL;
- } else /* next_mz == NULL or other memcg */
+ else /* next_mz == NULL or other memcg */
break;
} while (1);
}
@@ -3772,6 +3997,8 @@
MCS_PGPGIN,
MCS_PGPGOUT,
MCS_SWAP,
+ MCS_PGFAULT,
+ MCS_PGMAJFAULT,
MCS_INACTIVE_ANON,
MCS_ACTIVE_ANON,
MCS_INACTIVE_FILE,
@@ -3794,6 +4021,8 @@
{"pgpgin", "total_pgpgin"},
{"pgpgout", "total_pgpgout"},
{"swap", "total_swap"},
+ {"pgfault", "total_pgfault"},
+ {"pgmajfault", "total_pgmajfault"},
{"inactive_anon", "total_inactive_anon"},
{"active_anon", "total_active_anon"},
{"inactive_file", "total_inactive_file"},
@@ -3822,6 +4051,10 @@
val = mem_cgroup_read_stat(mem, MEM_CGROUP_STAT_SWAPOUT);
s->stat[MCS_SWAP] += val * PAGE_SIZE;
}
+ val = mem_cgroup_read_events(mem, MEM_CGROUP_EVENTS_PGFAULT);
+ s->stat[MCS_PGFAULT] += val;
+ val = mem_cgroup_read_events(mem, MEM_CGROUP_EVENTS_PGMAJFAULT);
+ s->stat[MCS_PGMAJFAULT] += val;
/* per zone stat */
val = mem_cgroup_get_local_zonestat(mem, LRU_INACTIVE_ANON);
@@ -3845,6 +4078,51 @@
mem_cgroup_get_local_stat(iter, s);
}
+#ifdef CONFIG_NUMA
+static int mem_control_numa_stat_show(struct seq_file *m, void *arg)
+{
+ int nid;
+ unsigned long total_nr, file_nr, anon_nr, unevictable_nr;
+ unsigned long node_nr;
+ struct cgroup *cont = m->private;
+ struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cont);
+
+ total_nr = mem_cgroup_nr_lru_pages(mem_cont);
+ seq_printf(m, "total=%lu", total_nr);
+ for_each_node_state(nid, N_HIGH_MEMORY) {
+ node_nr = mem_cgroup_node_nr_lru_pages(mem_cont, nid);
+ seq_printf(m, " N%d=%lu", nid, node_nr);
+ }
+ seq_putc(m, '\n');
+
+ file_nr = mem_cgroup_nr_file_lru_pages(mem_cont);
+ seq_printf(m, "file=%lu", file_nr);
+ for_each_node_state(nid, N_HIGH_MEMORY) {
+ node_nr = mem_cgroup_node_nr_file_lru_pages(mem_cont, nid);
+ seq_printf(m, " N%d=%lu", nid, node_nr);
+ }
+ seq_putc(m, '\n');
+
+ anon_nr = mem_cgroup_nr_anon_lru_pages(mem_cont);
+ seq_printf(m, "anon=%lu", anon_nr);
+ for_each_node_state(nid, N_HIGH_MEMORY) {
+ node_nr = mem_cgroup_node_nr_anon_lru_pages(mem_cont, nid);
+ seq_printf(m, " N%d=%lu", nid, node_nr);
+ }
+ seq_putc(m, '\n');
+
+ unevictable_nr = mem_cgroup_nr_unevictable_lru_pages(mem_cont);
+ seq_printf(m, "unevictable=%lu", unevictable_nr);
+ for_each_node_state(nid, N_HIGH_MEMORY) {
+ node_nr = mem_cgroup_node_nr_unevictable_lru_pages(mem_cont,
+ nid);
+ seq_printf(m, " N%d=%lu", nid, node_nr);
+ }
+ seq_putc(m, '\n');
+ return 0;
+}
+#endif /* CONFIG_NUMA */
+
static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
struct cgroup_map_cb *cb)
{
@@ -3855,6 +4133,7 @@
memset(&mystat, 0, sizeof(mystat));
mem_cgroup_get_local_stat(mem_cont, &mystat);
+
for (i = 0; i < NR_MCS_STAT; i++) {
if (i == MCS_SWAP && !do_swap_account)
continue;
@@ -4278,6 +4557,22 @@
return 0;
}
+#ifdef CONFIG_NUMA
+static const struct file_operations mem_control_numa_stat_file_operations = {
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mem_control_numa_stat_open(struct inode *unused, struct file *file)
+{
+ struct cgroup *cont = file->f_dentry->d_parent->d_fsdata;
+
+ file->f_op = &mem_control_numa_stat_file_operations;
+ return single_open(file, mem_control_numa_stat_show, cont);
+}
+#endif /* CONFIG_NUMA */
+
static struct cftype mem_cgroup_files[] = {
{
.name = "usage_in_bytes",
@@ -4341,6 +4636,12 @@
.unregister_event = mem_cgroup_oom_unregister_event,
.private = MEMFILE_PRIVATE(_OOM_TYPE, OOM_CONTROL),
},
+#ifdef CONFIG_NUMA
+ {
+ .name = "numa_stat",
+ .open = mem_control_numa_stat_open,
+ },
+#endif
};
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
@@ -4596,6 +4897,7 @@
res_counter_init(&mem->memsw, NULL);
}
mem->last_scanned_child = 0;
+ mem->last_scanned_node = MAX_NUMNODES;
INIT_LIST_HEAD(&mem->oom_notify);
if (parent)
@@ -4953,8 +5255,7 @@
static int mem_cgroup_can_attach(struct cgroup_subsys *ss,
struct cgroup *cgroup,
- struct task_struct *p,
- bool threadgroup)
+ struct task_struct *p)
{
int ret = 0;
struct mem_cgroup *mem = mem_cgroup_from_cont(cgroup);
@@ -4993,8 +5294,7 @@
static void mem_cgroup_cancel_attach(struct cgroup_subsys *ss,
struct cgroup *cgroup,
- struct task_struct *p,
- bool threadgroup)
+ struct task_struct *p)
{
mem_cgroup_clear_mc();
}
@@ -5112,8 +5412,7 @@
static void mem_cgroup_move_task(struct cgroup_subsys *ss,
struct cgroup *cont,
struct cgroup *old_cont,
- struct task_struct *p,
- bool threadgroup)
+ struct task_struct *p)
{
struct mm_struct *mm;
@@ -5131,22 +5430,19 @@
#else /* !CONFIG_MMU */
static int mem_cgroup_can_attach(struct cgroup_subsys *ss,
struct cgroup *cgroup,
- struct task_struct *p,
- bool threadgroup)
+ struct task_struct *p)
{
return 0;
}
static void mem_cgroup_cancel_attach(struct cgroup_subsys *ss,
struct cgroup *cgroup,
- struct task_struct *p,
- bool threadgroup)
+ struct task_struct *p)
{
}
static void mem_cgroup_move_task(struct cgroup_subsys *ss,
struct cgroup *cont,
struct cgroup *old_cont,
- struct task_struct *p,
- bool threadgroup)
+ struct task_struct *p)
{
}
#endif
diff --git a/mm/memory.c b/mm/memory.c
index b73f677..6953d39 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -730,7 +730,7 @@
add_taint(TAINT_BAD_PAGE);
}
-static inline int is_cow_mapping(unsigned int flags)
+static inline int is_cow_mapping(vm_flags_t flags)
{
return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
}
@@ -2874,6 +2874,7 @@
/* Had to read the page from swap area: Major fault */
ret = VM_FAULT_MAJOR;
count_vm_event(PGMAJFAULT);
+ mem_cgroup_count_vm_event(mm, PGMAJFAULT);
} else if (PageHWPoison(page)) {
/*
* hwpoisoned dirty swapcache pages are kept for killing
@@ -3413,6 +3414,7 @@
__set_current_state(TASK_RUNNING);
count_vm_event(PGFAULT);
+ mem_cgroup_count_vm_event(mm, PGFAULT);
/* do counter updates before entering really critical section. */
check_sync_rss_stat(current);
diff --git a/mm/mlock.c b/mm/mlock.c
index 516b2c2..048260c 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -307,13 +307,13 @@
* For vmas that pass the filters, merge/split as appropriate.
*/
static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
- unsigned long start, unsigned long end, unsigned int newflags)
+ unsigned long start, unsigned long end, vm_flags_t newflags)
{
struct mm_struct *mm = vma->vm_mm;
pgoff_t pgoff;
int nr_pages;
int ret = 0;
- int lock = newflags & VM_LOCKED;
+ int lock = !!(newflags & VM_LOCKED);
if (newflags == vma->vm_flags || (vma->vm_flags & VM_SPECIAL) ||
is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm))
@@ -385,7 +385,7 @@
prev = vma;
for (nstart = start ; ; ) {
- unsigned int newflags;
+ vm_flags_t newflags;
/* Here we know that vma->vm_start <= nstart < vma->vm_end. */
@@ -524,7 +524,7 @@
goto out;
for (vma = current->mm->mmap; vma ; vma = prev->vm_next) {
- unsigned int newflags;
+ vm_flags_t newflags;
newflags = vma->vm_flags | VM_LOCKED;
if (!(flags & MCL_CURRENT))
diff --git a/mm/mmap.c b/mm/mmap.c
index ac2631b..bbdc9af 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -960,7 +960,7 @@
{
struct mm_struct * mm = current->mm;
struct inode *inode;
- unsigned int vm_flags;
+ vm_flags_t vm_flags;
int error;
unsigned long reqprot = prot;
@@ -1165,7 +1165,7 @@
*/
int vma_wants_writenotify(struct vm_area_struct *vma)
{
- unsigned int vm_flags = vma->vm_flags;
+ vm_flags_t vm_flags = vma->vm_flags;
/* If it was private or non-writable, the write bit is already clear */
if ((vm_flags & (VM_WRITE|VM_SHARED)) != ((VM_WRITE|VM_SHARED)))
@@ -1193,7 +1193,7 @@
* We account for memory if it's a private writeable mapping,
* not hugepages and VM_NORESERVE wasn't set.
*/
-static inline int accountable_mapping(struct file *file, unsigned int vm_flags)
+static inline int accountable_mapping(struct file *file, vm_flags_t vm_flags)
{
/*
* hugetlb has its own accounting separate from the core VM
@@ -1207,7 +1207,7 @@
unsigned long mmap_region(struct file *file, unsigned long addr,
unsigned long len, unsigned long flags,
- unsigned int vm_flags, unsigned long pgoff)
+ vm_flags_t vm_flags, unsigned long pgoff)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma, *prev;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 2a00f17..a4e1db3 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4323,10 +4323,8 @@
zone->zone_pgdat = pgdat;
zone_pcp_init(zone);
- for_each_lru(l) {
+ for_each_lru(l)
INIT_LIST_HEAD(&zone->lru[l].list);
- zone->reclaim_stat.nr_saved_scan[l] = 0;
- }
zone->reclaim_stat.recent_rotated[0] = 0;
zone->reclaim_stat.recent_rotated[1] = 0;
zone->reclaim_stat.recent_scanned[0] = 0;
diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c
index 2daadc3..74ccff6 100644
--- a/mm/page_cgroup.c
+++ b/mm/page_cgroup.c
@@ -130,7 +130,7 @@
return page;
}
-static void *__init_refok alloc_page_cgroup(size_t size, int nid)
+static void *__meminit alloc_page_cgroup(size_t size, int nid)
{
void *addr = NULL;
@@ -162,7 +162,7 @@
}
#endif
-static int __init_refok init_section_page_cgroup(unsigned long pfn)
+static int __meminit init_section_page_cgroup(unsigned long pfn)
{
struct page_cgroup *base, *pc;
struct mem_section *section;
@@ -475,7 +475,7 @@
if (!do_swap_account)
return 0;
- length = ((max_pages/SC_PER_PAGE) + 1);
+ length = DIV_ROUND_UP(max_pages, SC_PER_PAGE);
array_size = length * sizeof(void *);
array = vmalloc(array_size);
@@ -492,8 +492,8 @@
/* memory shortage */
ctrl->map = NULL;
ctrl->length = 0;
- vfree(array);
mutex_unlock(&swap_cgroup_mutex);
+ vfree(array);
goto nomem;
}
mutex_unlock(&swap_cgroup_mutex);
@@ -508,7 +508,8 @@
void swap_cgroup_swapoff(int type)
{
- int i;
+ struct page **map;
+ unsigned long i, length;
struct swap_cgroup_ctrl *ctrl;
if (!do_swap_account)
@@ -516,17 +517,20 @@
mutex_lock(&swap_cgroup_mutex);
ctrl = &swap_cgroup_ctrl[type];
- if (ctrl->map) {
- for (i = 0; i < ctrl->length; i++) {
- struct page *page = ctrl->map[i];
+ map = ctrl->map;
+ length = ctrl->length;
+ ctrl->map = NULL;
+ ctrl->length = 0;
+ mutex_unlock(&swap_cgroup_mutex);
+
+ if (map) {
+ for (i = 0; i < length; i++) {
+ struct page *page = map[i];
if (page)
__free_page(page);
}
- vfree(ctrl->map);
- ctrl->map = NULL;
- ctrl->length = 0;
+ vfree(map);
}
- mutex_unlock(&swap_cgroup_mutex);
}
#endif
diff --git a/mm/shmem.c b/mm/shmem.c
index 69edb45..1acfb26 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1305,12 +1305,10 @@
swappage = lookup_swap_cache(swap);
if (!swappage) {
shmem_swp_unmap(entry);
- /* here we actually do the io */
- if (type && !(*type & VM_FAULT_MAJOR)) {
- __count_vm_event(PGMAJFAULT);
- *type |= VM_FAULT_MAJOR;
- }
spin_unlock(&info->lock);
+ /* here we actually do the io */
+ if (type)
+ *type |= VM_FAULT_MAJOR;
swappage = shmem_swapin(swap, gfp, info, idx);
if (!swappage) {
spin_lock(&info->lock);
@@ -1549,7 +1547,10 @@
error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret);
if (error)
return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS);
-
+ if (ret & VM_FAULT_MAJOR) {
+ count_vm_event(PGMAJFAULT);
+ mem_cgroup_count_vm_event(vma->vm_mm, PGMAJFAULT);
+ }
return ret | VM_FAULT_LOCKED;
}
diff --git a/mm/truncate.c b/mm/truncate.c
index a956675..3a29a61 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -19,6 +19,7 @@
#include <linux/task_io_accounting_ops.h>
#include <linux/buffer_head.h> /* grr. try_to_release_page,
do_invalidatepage */
+#include <linux/cleancache.h>
#include "internal.h"
@@ -51,6 +52,7 @@
static inline void truncate_partial_page(struct page *page, unsigned partial)
{
zero_user_segment(page, partial, PAGE_CACHE_SIZE);
+ cleancache_flush_page(page->mapping, page);
if (page_has_private(page))
do_invalidatepage(page, partial);
}
@@ -214,6 +216,7 @@
pgoff_t next;
int i;
+ cleancache_flush_inode(mapping);
if (mapping->nrpages == 0)
return;
@@ -291,6 +294,7 @@
pagevec_release(&pvec);
mem_cgroup_uncharge_end();
}
+ cleancache_flush_inode(mapping);
}
EXPORT_SYMBOL(truncate_inode_pages_range);
@@ -440,6 +444,7 @@
int did_range_unmap = 0;
int wrapped = 0;
+ cleancache_flush_inode(mapping);
pagevec_init(&pvec, 0);
next = start;
while (next <= end && !wrapped &&
@@ -498,6 +503,7 @@
mem_cgroup_uncharge_end();
cond_resched();
}
+ cleancache_flush_inode(mapping);
return ret;
}
EXPORT_SYMBOL_GPL(invalidate_inode_pages2_range);
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index b5ccf31..1d34d75 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -2153,10 +2153,6 @@
return NULL;
}
- /* Make sure the pagetables are constructed in process kernel
- mappings */
- vmalloc_sync_all();
-
return area;
}
EXPORT_SYMBOL_GPL(alloc_vm_area);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 7e01161..faa0a08 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -173,7 +173,7 @@
struct scan_control *sc, enum lru_list lru)
{
if (!scanning_global_lru(sc))
- return mem_cgroup_zone_nr_pages(sc->mem_cgroup, zone, lru);
+ return mem_cgroup_zone_nr_lru_pages(sc->mem_cgroup, zone, lru);
return zone_page_state(zone, NR_LRU_BASE + lru);
}
@@ -1718,26 +1718,6 @@
}
/*
- * Smallish @nr_to_scan's are deposited in @nr_saved_scan,
- * until we collected @swap_cluster_max pages to scan.
- */
-static unsigned long nr_scan_try_batch(unsigned long nr_to_scan,
- unsigned long *nr_saved_scan)
-{
- unsigned long nr;
-
- *nr_saved_scan += nr_to_scan;
- nr = *nr_saved_scan;
-
- if (nr >= SWAP_CLUSTER_MAX)
- *nr_saved_scan = 0;
- else
- nr = 0;
-
- return nr;
-}
-
-/*
* Determine how aggressively the anon and file LRU lists should be
* scanned. The relative value of each set of LRU lists is determined
* by looking at the fraction of the pages scanned we did rotate back
@@ -1755,6 +1735,22 @@
u64 fraction[2], denominator;
enum lru_list l;
int noswap = 0;
+ int force_scan = 0;
+
+
+ anon = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_ANON) +
+ zone_nr_lru_pages(zone, sc, LRU_INACTIVE_ANON);
+ file = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_FILE) +
+ zone_nr_lru_pages(zone, sc, LRU_INACTIVE_FILE);
+
+ if (((anon + file) >> priority) < SWAP_CLUSTER_MAX) {
+ /* kswapd does zone balancing and need to scan this zone */
+ if (scanning_global_lru(sc) && current_is_kswapd())
+ force_scan = 1;
+ /* memcg may have small limit and need to avoid priority drop */
+ if (!scanning_global_lru(sc))
+ force_scan = 1;
+ }
/* If we have no swap space, do not bother scanning anon pages. */
if (!sc->may_swap || (nr_swap_pages <= 0)) {
@@ -1765,11 +1761,6 @@
goto out;
}
- anon = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_ANON) +
- zone_nr_lru_pages(zone, sc, LRU_INACTIVE_ANON);
- file = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_FILE) +
- zone_nr_lru_pages(zone, sc, LRU_INACTIVE_FILE);
-
if (scanning_global_lru(sc)) {
free = zone_page_state(zone, NR_FREE_PAGES);
/* If we have very few page cache pages,
@@ -1836,8 +1827,23 @@
scan >>= priority;
scan = div64_u64(scan * fraction[file], denominator);
}
- nr[l] = nr_scan_try_batch(scan,
- &reclaim_stat->nr_saved_scan[l]);
+
+ /*
+ * If zone is small or memcg is small, nr[l] can be 0.
+ * This results no-scan on this priority and priority drop down.
+ * For global direct reclaim, it can visit next zone and tend
+ * not to have problems. For global kswapd, it's for zone
+ * balancing and it need to scan a small amounts. When using
+ * memcg, priority drop can cause big latency. So, it's better
+ * to scan small amount. See may_noscan above.
+ */
+ if (!scan && force_scan) {
+ if (file)
+ scan = SWAP_CLUSTER_MAX;
+ else if (!noswap)
+ scan = SWAP_CLUSTER_MAX;
+ }
+ nr[l] = scan;
}
}
@@ -1977,11 +1983,14 @@
* If a zone is deemed to be full of pinned pages then just give it a light
* scan then give up on it.
*/
-static void shrink_zones(int priority, struct zonelist *zonelist,
+static unsigned long shrink_zones(int priority, struct zonelist *zonelist,
struct scan_control *sc)
{
struct zoneref *z;
struct zone *zone;
+ unsigned long nr_soft_reclaimed;
+ unsigned long nr_soft_scanned;
+ unsigned long total_scanned = 0;
for_each_zone_zonelist_nodemask(zone, z, zonelist,
gfp_zone(sc->gfp_mask), sc->nodemask) {
@@ -1998,8 +2007,17 @@
continue; /* Let kswapd poll it */
}
+ nr_soft_scanned = 0;
+ nr_soft_reclaimed = mem_cgroup_soft_limit_reclaim(zone,
+ sc->order, sc->gfp_mask,
+ &nr_soft_scanned);
+ sc->nr_reclaimed += nr_soft_reclaimed;
+ total_scanned += nr_soft_scanned;
+
shrink_zone(priority, zone, sc);
}
+
+ return total_scanned;
}
static bool zone_reclaimable(struct zone *zone)
@@ -2064,7 +2082,7 @@
sc->nr_scanned = 0;
if (!priority)
disable_swap_token();
- shrink_zones(priority, zonelist, sc);
+ total_scanned += shrink_zones(priority, zonelist, sc);
/*
* Don't shrink slabs when reclaiming memory from
* over limit cgroups
@@ -2171,9 +2189,11 @@
unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap,
unsigned int swappiness,
- struct zone *zone)
+ struct zone *zone,
+ unsigned long *nr_scanned)
{
struct scan_control sc = {
+ .nr_scanned = 0,
.nr_to_reclaim = SWAP_CLUSTER_MAX,
.may_writepage = !laptop_mode,
.may_unmap = 1,
@@ -2182,6 +2202,7 @@
.order = 0,
.mem_cgroup = mem,
};
+
sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
@@ -2200,6 +2221,7 @@
trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed);
+ *nr_scanned = sc.nr_scanned;
return sc.nr_reclaimed;
}
@@ -2210,6 +2232,7 @@
{
struct zonelist *zonelist;
unsigned long nr_reclaimed;
+ int nid;
struct scan_control sc = {
.may_writepage = !laptop_mode,
.may_unmap = 1,
@@ -2226,7 +2249,14 @@
.gfp_mask = sc.gfp_mask,
};
- zonelist = NODE_DATA(numa_node_id())->node_zonelists;
+ /*
+ * Unlike direct reclaim via alloc_pages(), memcg's reclaim doesn't
+ * take care of from where we get pages. So the node where we start the
+ * scan does not need to be the current node.
+ */
+ nid = mem_cgroup_select_victim_node(mem_cont);
+
+ zonelist = NODE_DATA(nid)->node_zonelists;
trace_mm_vmscan_memcg_reclaim_begin(0,
sc.may_writepage,
@@ -2347,6 +2377,8 @@
int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */
unsigned long total_scanned;
struct reclaim_state *reclaim_state = current->reclaim_state;
+ unsigned long nr_soft_reclaimed;
+ unsigned long nr_soft_scanned;
struct scan_control sc = {
.gfp_mask = GFP_KERNEL,
.may_unmap = 1,
@@ -2439,11 +2471,15 @@
sc.nr_scanned = 0;
+ nr_soft_scanned = 0;
/*
* Call soft limit reclaim before calling shrink_zone.
- * For now we ignore the return value
*/
- mem_cgroup_soft_limit_reclaim(zone, order, sc.gfp_mask);
+ nr_soft_reclaimed = mem_cgroup_soft_limit_reclaim(zone,
+ order, sc.gfp_mask,
+ &nr_soft_scanned);
+ sc.nr_reclaimed += nr_soft_reclaimed;
+ total_scanned += nr_soft_scanned;
/*
* We put equal pressure on every zone, unless
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index 844a7a5..159c50f 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -589,7 +589,8 @@
return -ENOMEM;
/* Create the RDMA CM ID */
- rdma->cm_id = rdma_create_id(p9_cm_event_handler, client, RDMA_PS_TCP);
+ rdma->cm_id = rdma_create_id(p9_cm_event_handler, client, RDMA_PS_TCP,
+ IB_QPT_RC);
if (IS_ERR(rdma->cm_id))
goto error;
diff --git a/net/atm/lec.c b/net/atm/lec.c
index 25073b6..ba48daa 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -1171,7 +1171,7 @@
#endif
register_atm_ioctl(&lane_ioctl_ops);
- pr_info("lec.c: " __DATE__ " " __TIME__ " initialized\n");
+ pr_info("lec.c: initialized\n");
return 0;
}
diff --git a/net/atm/mpc.c b/net/atm/mpc.c
index 644cdf0..3ccca42 100644
--- a/net/atm/mpc.c
+++ b/net/atm/mpc.c
@@ -1482,7 +1482,7 @@
if (mpc_proc_init() != 0)
pr_info("failed to initialize /proc/mpoa\n");
- pr_info("mpc.c: " __DATE__ " " __TIME__ " initialized\n");
+ pr_info("mpc.c: initialized\n");
return 0;
}
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 61fac4c..c14d88a 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -33,7 +33,7 @@
* This struct holds the first and last local port number.
*/
struct local_ports sysctl_local_ports __read_mostly = {
- .lock = SEQLOCK_UNLOCKED,
+ .lock = __SEQLOCK_UNLOCKED(sysctl_local_ports.lock),
.range = { 32768, 61000 },
};
diff --git a/net/rds/ib.c b/net/rds/ib.c
index cce19f95..3b83086 100644
--- a/net/rds/ib.c
+++ b/net/rds/ib.c
@@ -325,7 +325,7 @@
/* Create a CMA ID and try to bind it. This catches both
* IB and iWARP capable NICs.
*/
- cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP);
+ cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index ee369d2..fd453dd 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -587,7 +587,7 @@
/* XXX I wonder what affect the port space has */
/* delegate cm event handler to rdma_transport */
ic->i_cm_id = rdma_create_id(rds_rdma_cm_event_handler, conn,
- RDMA_PS_TCP);
+ RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(ic->i_cm_id)) {
ret = PTR_ERR(ic->i_cm_id);
ic->i_cm_id = NULL;
diff --git a/net/rds/iw.c b/net/rds/iw.c
index 5a9676f..f747484 100644
--- a/net/rds/iw.c
+++ b/net/rds/iw.c
@@ -226,7 +226,7 @@
/* Create a CMA ID and try to bind it. This catches both
* IB and iWARP capable NICs.
*/
- cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP);
+ cm_id = rdma_create_id(NULL, NULL, RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
diff --git a/net/rds/iw_cm.c b/net/rds/iw_cm.c
index 3a60a15..c12db66 100644
--- a/net/rds/iw_cm.c
+++ b/net/rds/iw_cm.c
@@ -522,7 +522,7 @@
/* XXX I wonder what affect the port space has */
/* delegate cm event handler to rdma_transport */
ic->i_cm_id = rdma_create_id(rds_rdma_cm_event_handler, conn,
- RDMA_PS_TCP);
+ RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(ic->i_cm_id)) {
ret = PTR_ERR(ic->i_cm_id);
ic->i_cm_id = NULL;
diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c
index 4195a05..f8760e1 100644
--- a/net/rds/rdma_transport.c
+++ b/net/rds/rdma_transport.c
@@ -158,7 +158,8 @@
struct rdma_cm_id *cm_id;
int ret;
- cm_id = rdma_create_id(rds_rdma_cm_event_handler, NULL, RDMA_PS_TCP);
+ cm_id = rdma_create_id(rds_rdma_cm_event_handler, NULL, RDMA_PS_TCP,
+ IB_QPT_RC);
if (IS_ERR(cm_id)) {
ret = PTR_ERR(cm_id);
printk(KERN_ERR "RDS/RDMA: failed to setup listener, "
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 6c014dd..c3c232a 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -695,7 +695,8 @@
return ERR_PTR(-ENOMEM);
xprt = &cma_xprt->sc_xprt;
- listen_id = rdma_create_id(rdma_listen_handler, cma_xprt, RDMA_PS_TCP);
+ listen_id = rdma_create_id(rdma_listen_handler, cma_xprt, RDMA_PS_TCP,
+ IB_QPT_RC);
if (IS_ERR(listen_id)) {
ret = PTR_ERR(listen_id);
dprintk("svcrdma: rdma_create_id failed = %d\n", ret);
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index d4297dc..80f8da3 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -387,7 +387,7 @@
init_completion(&ia->ri_done);
- id = rdma_create_id(rpcrdma_conn_upcall, xprt, RDMA_PS_TCP);
+ id = rdma_create_id(rpcrdma_conn_upcall, xprt, RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(id)) {
rc = PTR_ERR(id);
dprintk("RPC: %s: rdma_create_id() failed %i\n",
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 8d9c48f..cd1f779 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -62,8 +62,7 @@
struct cgroup_subsys devices_subsys;
static int devcgroup_can_attach(struct cgroup_subsys *ss,
- struct cgroup *new_cgroup, struct task_struct *task,
- bool threadgroup)
+ struct cgroup *new_cgroup, struct task_struct *task)
{
if (current != task && !capable(CAP_SYS_ADMIN))
return -EPERM;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 6c0480d..a3063eb 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -847,6 +847,7 @@
new-> sgid = old-> sgid;
new->fsgid = old->fsgid;
new->user = get_uid(old->user);
+ new->user_ns = new->user->user_ns;
new->group_info = get_group_info(old->group_info);
new->securebits = old->securebits;
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index fcb89cb..d515b21 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -752,10 +752,9 @@
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
- struct av_decision *in_avd)
+ struct av_decision *avd)
{
struct avc_node *node;
- struct av_decision avd_entry, *avd;
int rc = 0;
u32 denied;
@@ -766,18 +765,11 @@
node = avc_lookup(ssid, tsid, tclass);
if (unlikely(!node)) {
rcu_read_unlock();
-
- if (in_avd)
- avd = in_avd;
- else
- avd = &avd_entry;
-
security_compute_av(ssid, tsid, tclass, avd);
rcu_read_lock();
node = avc_insert(ssid, tsid, tclass, avd);
} else {
- if (in_avd)
- memcpy(in_avd, &node->ae.avd, sizeof(*in_avd));
+ memcpy(avd, &node->ae.avd, sizeof(*avd));
avd = &node->ae.avd;
}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index c3e4b52..973e00e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2217,10 +2217,11 @@
goto out;
}
for (i = 0, j = 0; i < mynel; i++) {
+ struct av_decision dummy_avd;
rc = avc_has_perm_noaudit(fromsid, mysids[i],
SECCLASS_PROCESS, /* kernel value */
PROCESS__TRANSITION, AVC_STRICT,
- NULL);
+ &dummy_avd);
if (!rc)
mysids2[j++] = mysids[i];
cond_resched();
diff --git a/sound/core/control.c b/sound/core/control.c
index 5d98194..f8c5be4 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -704,13 +704,12 @@
struct snd_ctl_elem_list list;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id *dst, *id;
- unsigned int offset, space, first, jidx;
+ unsigned int offset, space, jidx;
if (copy_from_user(&list, _list, sizeof(list)))
return -EFAULT;
offset = list.offset;
space = list.space;
- first = 0;
/* try limit maximum space */
if (space > 16384)
return -ENOMEM;
diff --git a/sound/core/init.c b/sound/core/init.c
index 30ecad4..2c041bb 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -342,7 +342,6 @@
int snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
- struct file *file;
int err;
if (!card)
@@ -366,8 +365,6 @@
spin_lock(&card->files_lock);
list_for_each_entry(mfile, &card->files_list, list) {
- file = mfile->file;
-
/* it's critical part, use endless loop */
/* we have no room to fail */
mfile->disconnected_f_op = mfile->file->f_op;
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 13b3f6f..2045697 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -90,11 +90,8 @@
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- struct linear_priv *data;
-
if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
return -ENXIO;
- data = (struct linear_priv *)plugin->extra_data;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index abfeff16..f134130 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1756,8 +1756,18 @@
wait_queue_t wait;
int err = 0;
snd_pcm_uframes_t avail = 0;
- long tout;
+ long wait_time, tout;
+ if (runtime->no_period_wakeup)
+ wait_time = MAX_SCHEDULE_TIMEOUT;
+ else {
+ wait_time = 10;
+ if (runtime->rate) {
+ long t = runtime->period_size * 2 / runtime->rate;
+ wait_time = max(t, wait_time);
+ }
+ wait_time = msecs_to_jiffies(wait_time * 1000);
+ }
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->tsleep, &wait);
for (;;) {
@@ -1765,9 +1775,8 @@
err = -ERESTARTSYS;
break;
}
- set_current_state(TASK_INTERRUPTIBLE);
snd_pcm_stream_unlock_irq(substream);
- tout = schedule_timeout(msecs_to_jiffies(10000));
+ tout = schedule_timeout_interruptible(wait_time);
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
case SNDRV_PCM_STATE_SUSPENDED:
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1a07750..1c6be91 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1481,11 +1481,20 @@
break; /* all drained */
init_waitqueue_entry(&wait, current);
add_wait_queue(&to_check->sleep, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
snd_pcm_stream_unlock_irq(substream);
up_read(&snd_pcm_link_rwsem);
snd_power_unlock(card);
- tout = schedule_timeout(10 * HZ);
+ if (runtime->no_period_wakeup)
+ tout = MAX_SCHEDULE_TIMEOUT;
+ else {
+ tout = 10;
+ if (runtime->rate) {
+ long t = runtime->period_size * 2 / runtime->rate;
+ tout = max(t, tout);
+ }
+ tout = msecs_to_jiffies(tout * 1000);
+ }
+ tout = schedule_timeout_interruptible(tout);
snd_power_lock(card);
down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
@@ -1518,13 +1527,11 @@
static int snd_pcm_drop(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- struct snd_card *card;
int result = 0;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- card = substream->pcm->card;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED ||
@@ -2056,7 +2063,6 @@
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
- struct snd_pcm_str *str;
int err;
if (rpcm_file)
@@ -2073,7 +2079,6 @@
}
pcm_file->substream = substream;
if (substream->ref_count == 1) {
- str = substream->pstr;
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
}
@@ -3015,11 +3020,9 @@
static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
return -EINVAL;
@@ -3054,11 +3057,9 @@
static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
return -EINVAL;
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index e7a8e9e..f907736 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -467,13 +467,11 @@
int snd_seq_queue_timer_close(int queueid)
{
struct snd_seq_queue *queue;
- struct snd_seq_timer *tmr;
int result = 0;
queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
- tmr = queue->timer;
snd_seq_timer_close(queue);
queuefree(queue);
return result;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8edd998..45b4a8d 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -4719,7 +4719,7 @@
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
snd_printd(" inputs:");
for (i = 0; i < cfg->num_inputs; i++) {
- snd_printdd(" %s=0x%x",
+ snd_printd(" %s=0x%x",
hda_get_autocfg_input_label(codec, cfg, i),
cfg->inputs[i].pin);
}
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 74b0560..b05f7be 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -312,23 +312,6 @@
return -EINVAL;
}
-static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
-{
- int eldv;
- int present;
-
- present = snd_hda_pin_sense(codec, nid);
- eldv = (present & AC_PINSENSE_ELDV);
- present = (present & AC_PINSENSE_PRESENCE);
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
- !!present, !!eldv);
-#endif
-
- return eldv && present;
-}
-
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
@@ -343,7 +326,7 @@
int size;
unsigned char *buf;
- if (!hdmi_eld_valid(codec, nid))
+ if (!eld->eld_valid)
return -ENOENT;
size = snd_hdmi_get_eld_size(codec, nid);
@@ -477,6 +460,8 @@
snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
+ if (!e->eld_valid)
+ return;
snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
snd_iprintf(buffer, "connection_type\t\t%s\n",
eld_connection_type_names[e->conn_type]);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 43a0367..3487056 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -391,6 +391,7 @@
/* chip type specific */
int driver_type;
+ unsigned int driver_caps;
int playback_streams;
int playback_index_offset;
int capture_streams;
@@ -464,6 +465,34 @@
AZX_NUM_DRIVERS, /* keep this as last entry */
};
+/* driver quirks (capabilities) */
+/* bits 0-7 are used for indicating driver type */
+#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */
+#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
+#define AZX_DCAPS_ATI_SNOOP (1 << 10) /* ATI snoop enable */
+#define AZX_DCAPS_NVIDIA_SNOOP (1 << 11) /* Nvidia snoop enable */
+#define AZX_DCAPS_SCH_SNOOP (1 << 12) /* SCH/PCH snoop enable */
+#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */
+#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */
+#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
+#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
+#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */
+#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
+#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
+
+/* quirks for ATI SB / AMD Hudson */
+#define AZX_DCAPS_PRESET_ATI_SB \
+ (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \
+ AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+
+/* quirks for ATI/AMD HDMI */
+#define AZX_DCAPS_PRESET_ATI_HDMI \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+
+/* quirks for Nvidia */
+#define AZX_DCAPS_PRESET_NVIDIA \
+ (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI)
+
static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -566,7 +595,7 @@
/* reset the rirb hw write pointer */
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
/* set N=1, get RIRB response interrupt for new entry */
- if (chip->driver_type == AZX_DRIVER_CTX)
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
azx_writew(chip, RINTCNT, 0xc0);
else
azx_writew(chip, RINTCNT, 1);
@@ -1056,19 +1085,24 @@
* codecs.
* The PCI register TCSEL is defined in the Intel manuals.
*/
- if (chip->driver_type != AZX_DRIVER_ATI &&
- chip->driver_type != AZX_DRIVER_ATIHDMI)
+ if (chip->driver_caps & AZX_DCAPS_NO_TCSEL) {
+ snd_printdd(SFX "Clearing TCSEL\n");
update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
+ }
- switch (chip->driver_type) {
- case AZX_DRIVER_ATI:
- /* For ATI SB450 azalia HD audio, we need to enable snoop */
+ /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio,
+ * we need to enable snoop.
+ */
+ if (chip->driver_caps & AZX_DCAPS_ATI_SNOOP) {
+ snd_printdd(SFX "Enabling ATI snoop\n");
update_pci_byte(chip->pci,
ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
- break;
- case AZX_DRIVER_NVIDIA:
- /* For NVIDIA HDA, enable snoop */
+ }
+
+ /* For NVIDIA HDA, enable snoop */
+ if (chip->driver_caps & AZX_DCAPS_NVIDIA_SNOOP) {
+ snd_printdd(SFX "Enabling Nvidia snoop\n");
update_pci_byte(chip->pci,
NVIDIA_HDA_TRANSREG_ADDR,
0x0f, NVIDIA_HDA_ENABLE_COHBITS);
@@ -1078,9 +1112,10 @@
update_pci_byte(chip->pci,
NVIDIA_HDA_OSTRM_COH,
0x01, NVIDIA_HDA_ENABLE_COHBIT);
- break;
- case AZX_DRIVER_SCH:
- case AZX_DRIVER_PCH:
+ }
+
+ /* Enable SCH/PCH snoop if needed */
+ if (chip->driver_caps & AZX_DCAPS_SCH_SNOOP) {
pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
@@ -1091,14 +1126,6 @@
(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
? "Failed" : "OK");
}
- break;
- default:
- /* AMD Hudson needs the similar snoop, as it seems... */
- if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
- update_pci_byte(chip->pci,
- ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
- 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
- break;
}
}
@@ -1152,7 +1179,7 @@
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
if (status & RIRB_INT_RESPONSE) {
- if (chip->driver_type == AZX_DRIVER_CTX)
+ if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
udelay(80);
azx_update_rirb(chip);
}
@@ -1421,8 +1448,10 @@
if (err < 0)
return err;
- if (chip->driver_type == AZX_DRIVER_NVIDIA)
+ if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
+ snd_printd(SFX "Enable delay in RIRB handling\n");
chip->bus->needs_damn_long_delay = 1;
+ }
codecs = 0;
max_slots = azx_max_codecs[chip->driver_type];
@@ -1457,9 +1486,8 @@
* sequence like the pin-detection. It seems that forcing the synced
* access works around the stall. Grrr...
*/
- if (chip->pci->vendor == PCI_VENDOR_ID_AMD ||
- chip->pci->vendor == PCI_VENDOR_ID_ATI) {
- snd_printk(KERN_INFO SFX "Enable sync_write for AMD chipset\n");
+ if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) {
+ snd_printd(SFX "Enable sync_write for stable communication\n");
chip->bus->sync_write = 1;
chip->bus->allow_bus_reset = 1;
}
@@ -1720,7 +1748,7 @@
stream_tag = azx_dev->stream_tag;
/* CA-IBG chips need the playback stream starting from 1 */
- if (chip->driver_type == AZX_DRIVER_CTX &&
+ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
stream_tag > chip->capture_streams)
stream_tag -= chip->capture_streams;
return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
@@ -2365,20 +2393,14 @@
}
/* Check VIA/ATI HD Audio Controller exist */
- switch (chip->driver_type) {
- case AZX_DRIVER_VIA:
- /* Use link position directly, avoid any transfer problem. */
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_VIA) {
+ snd_printd(SFX "Using VIACOMBO position fix\n");
return POS_FIX_VIACOMBO;
- case AZX_DRIVER_ATI:
- /* ATI chipsets don't work well with position-buffer */
- return POS_FIX_LPIB;
- case AZX_DRIVER_GENERIC:
- /* AMD chipsets also don't work with position-buffer */
- if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
- return POS_FIX_LPIB;
- break;
}
-
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
+ snd_printd(SFX "Using LPIB position fix\n");
+ return POS_FIX_LPIB;
+ }
return POS_FIX_AUTO;
}
@@ -2460,8 +2482,8 @@
}
/* NVidia chipsets seem to cause troubles with MSI */
- if (chip->driver_type == AZX_DRIVER_NVIDIA) {
- printk(KERN_INFO "hda_intel: Disable MSI for Nvidia chipset\n");
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI) {
+ printk(KERN_INFO "hda_intel: Disabling MSI\n");
chip->msi = 0;
}
}
@@ -2471,7 +2493,7 @@
* constructor
*/
static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int dev, int driver_type,
+ int dev, unsigned int driver_caps,
struct azx **rchip)
{
struct azx *chip;
@@ -2499,7 +2521,8 @@
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- chip->driver_type = driver_type;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
check_msi(chip);
chip->dev_index = dev;
INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
@@ -2563,8 +2586,7 @@
snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
/* disable SB600 64bit support for safety */
- if ((chip->driver_type == AZX_DRIVER_ATI) ||
- (chip->driver_type == AZX_DRIVER_ATIHDMI)) {
+ if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
struct pci_dev *p_smbus;
p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS,
@@ -2574,19 +2596,13 @@
gcap &= ~ICH6_GCAP_64OK;
pci_dev_put(p_smbus);
}
- } else {
- /* FIXME: not sure whether this is really needed, but
- * Hudson isn't stable enough for allowing everything...
- * let's check later again.
- */
- if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
- gcap &= ~ICH6_GCAP_64OK;
}
- /* disable 64bit DMA address for Teradici */
- /* it does not work with device 6549:1200 subsys e4a2:040b */
- if (chip->driver_type == AZX_DRIVER_TERA)
+ /* disable 64bit DMA address on some devices */
+ if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
+ snd_printd(SFX "Disabling 64bit DMA\n");
gcap &= ~ICH6_GCAP_64OK;
+ }
/* allow 64bit DMA address if supported by H/W */
if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
@@ -2788,38 +2804,62 @@
/* PCI IDs */
static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* CPT */
- { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
+ { PCI_DEVICE(0x8086, 0x1c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
/* PBG */
- { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
+ { PCI_DEVICE(0x8086, 0x1d20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
/* Panther Point */
- { PCI_DEVICE(0x8086, 0x1e20), .driver_data = AZX_DRIVER_PCH },
+ { PCI_DEVICE(0x8086, 0x1e20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
/* SCH */
- { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+ { PCI_DEVICE(0x8086, 0x811b),
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP },
/* Generic Intel */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_ICH },
- /* ATI SB 450/600 */
- { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
- { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
+ /* ATI SB 450/600/700/800/900 */
+ { PCI_DEVICE(0x1002, 0x437b),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ { PCI_DEVICE(0x1002, 0x4383),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD Hudson */
+ { PCI_DEVICE(0x1022, 0x780d),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
/* ATI HDMI */
- { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x970f), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI },
+ { PCI_DEVICE(0x1002, 0x793b),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x7919),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x960f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0x970f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa00),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa08),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa10),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa18),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa20),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa28),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa30),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa38),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa40),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(0x1002, 0xaa48),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
/* VIA VT8251/VT8237A */
- { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
+ { PCI_DEVICE(0x1106, 0x3288),
+ .driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA },
/* SIS966 */
{ PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
/* ULI M5461 */
@@ -2828,9 +2868,10 @@
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_NVIDIA },
+ .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA },
/* Teradici */
- { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+ { PCI_DEVICE(0x6549, 0x1200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
/* Creative X-Fi (CA0110-IBG) */
#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
/* the following entry conflicts with snd-ctxfi driver,
@@ -2840,10 +2881,13 @@
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_CTX },
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_RIRB_PRE_DELAY },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
+ { PCI_DEVICE(0x1102, 0x0009),
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_RIRB_PRE_DELAY },
#endif
/* Vortex86MX */
{ PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
@@ -2853,11 +2897,11 @@
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index f1b3875..696ac25 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -3159,6 +3159,7 @@
SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
+ SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
{}
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 4f37477..3e6b9a8 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -3098,7 +3098,9 @@
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
+ SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO),
SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */
{}
};
@@ -3433,7 +3435,9 @@
break;
}
}
- if (spec->auto_mute && cfg->line_out_pins[0] &&
+ if (spec->auto_mute &&
+ cfg->line_out_pins[0] &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT &&
cfg->line_out_pins[0] != cfg->hp_pins[0] &&
cfg->line_out_pins[0] != cfg->speaker_pins[0]) {
for (i = 0; i < cfg->line_outs; i++) {
@@ -3481,25 +3485,32 @@
{
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- int on;
+ int on = 1;
- if (!spec->auto_mute)
- on = 0;
- else
- on = spec->hp_present | spec->line_present;
+ /* turn on HP EAPD when HP jacks are present */
+ if (spec->auto_mute)
+ on = spec->hp_present;
cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on);
- do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, !on);
+ /* mute speakers in auto-mode if HP or LO jacks are plugged */
+ if (spec->auto_mute)
+ on = !(spec->hp_present ||
+ (spec->detect_line && spec->line_present));
+ do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on);
/* toggle line-out mutes if needed, too */
/* if LO is a copy of either HP or Speaker, don't need to handle it */
if (cfg->line_out_pins[0] == cfg->hp_pins[0] ||
cfg->line_out_pins[0] == cfg->speaker_pins[0])
return;
- if (!spec->automute_lines || !spec->auto_mute)
- on = 0;
- else
- on = spec->hp_present;
- do_automute(codec, cfg->line_outs, cfg->line_out_pins, !on);
+ if (spec->auto_mute) {
+ /* mute LO in auto-mode when HP jack is present */
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ||
+ spec->automute_lines)
+ on = !spec->hp_present;
+ else
+ on = 1;
+ }
+ do_automute(codec, cfg->line_outs, cfg->line_out_pins, on);
}
static void cx_auto_hp_automute(struct hda_codec *codec)
@@ -3696,13 +3707,14 @@
{
struct conexant_spec *spec = codec->spec;
hda_nid_t adc;
+ int changed = 1;
if (!imux->num_items)
return 0;
if (idx >= imux->num_items)
idx = imux->num_items - 1;
if (spec->cur_mux[0] == idx)
- return 0;
+ changed = 0;
adc = spec->imux_info[idx].adc;
select_input_connection(codec, spec->imux_info[idx].adc,
spec->imux_info[idx].pin);
@@ -3715,7 +3727,7 @@
spec->cur_adc_format);
}
spec->cur_mux[0] = idx;
- return 1;
+ return changed;
}
static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol,
@@ -3789,7 +3801,7 @@
int pset[INPUT_PIN_ATTR_NORMAL + 1];
int i;
- for (i = 0; i < INPUT_PIN_ATTR_NORMAL; i++)
+ for (i = 0; i < ARRAY_SIZE(pset); i++)
pset[i] = -1;
for (i = 0; i < spec->private_imux.num_items; i++) {
hda_nid_t pin = spec->imux_info[i].pin;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 3229018..bd0ae69 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -48,8 +48,8 @@
*
* The HDA correspondence of pipes/ports are converter/pin nodes.
*/
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
+#define MAX_HDMI_CVTS 4
+#define MAX_HDMI_PINS 4
struct hdmi_spec {
int num_cvts;
@@ -78,10 +78,6 @@
*/
struct hda_multi_out multiout;
const struct hda_pcm_stream *pcm_playback;
-
- /* misc flags */
- /* PD bit indicates only the update, not the current state */
- unsigned int old_pin_detect:1;
};
@@ -300,13 +296,6 @@
return -EINVAL;
}
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
-{
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
-}
-
#ifdef BE_PARANOID
static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
int *packet_index, int *byte_index)
@@ -694,35 +683,20 @@
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
struct hdmi_spec *spec = codec->spec;
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int pind = !!(res & AC_UNSOL_RES_PD);
+ int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int pd = !!(res & AC_UNSOL_RES_PD);
int eldv = !!(res & AC_UNSOL_RES_ELDV);
int index;
printk(KERN_INFO
"HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- tag, pind, eldv);
+ pin_nid, pd, eldv);
- index = hda_node_index(spec->pin, tag);
+ index = hda_node_index(spec->pin, pin_nid);
if (index < 0)
return;
- if (spec->old_pin_detect) {
- if (pind)
- hdmi_present_sense(codec, tag, &spec->sink_eld[index]);
- pind = spec->sink_eld[index].monitor_present;
- }
-
- spec->sink_eld[index].monitor_present = pind;
- spec->sink_eld[index].eld_valid = eldv;
-
- if (pind && eldv) {
- hdmi_get_show_eld(codec, spec->pin[index],
- &spec->sink_eld[index]);
- /* TODO: do real things about ELD */
- }
-
- snd_hda_input_jack_report(codec, tag);
+ hdmi_present_sense(codec, pin_nid, &spec->sink_eld[index]);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
@@ -903,13 +877,33 @@
static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
struct hdmi_eld *eld)
{
+ /*
+ * Always execute a GetPinSense verb here, even when called from
+ * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
+ * response's PD bit is not the real PD value, but indicates that
+ * the real PD value changed. An older version of the HD-audio
+ * specification worked this way. Hence, we just ignore the data in
+ * the unsolicited response to avoid custom WARs.
+ */
int present = snd_hda_pin_sense(codec, pin_nid);
- eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
- eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ memset(eld, 0, sizeof(*eld));
- if (present & AC_PINSENSE_ELDV)
- hdmi_get_show_eld(codec, pin_nid, eld);
+ eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ if (eld->monitor_present)
+ eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ else
+ eld->eld_valid = 0;
+
+ printk(KERN_INFO
+ "HDMI status: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+ pin_nid, eld->monitor_present, eld->eld_valid);
+
+ if (eld->eld_valid)
+ if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+ snd_hdmi_show_eld(eld);
+
+ snd_hda_input_jack_report(codec, pin_nid);
}
static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
@@ -927,7 +921,6 @@
SND_JACK_VIDEOOUT, NULL);
if (err < 0)
return err;
- snd_hda_input_jack_report(codec, pin_nid);
hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
@@ -1034,6 +1027,7 @@
"HDMI 0",
"HDMI 1",
"HDMI 2",
+ "HDMI 3",
};
/*
@@ -1490,18 +1484,6 @@
.free = generic_hdmi_free,
};
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int err = patch_generic_hdmi(codec);
-
- if (err < 0)
- return err;
- spec = codec->spec;
- spec->old_pin_detect = 1;
- return 0;
-}
-
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
@@ -1515,7 +1497,6 @@
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->old_pin_detect = 1;
spec->num_cvts = 1;
spec->cvt[0] = nvhdmi_master_con_nid_7x;
spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
@@ -1658,28 +1639,28 @@
{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi },
/* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 28afbbf..95572d2 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -146,7 +146,7 @@
"at91sam9g20ek_wm8731 "
": at91sam9g20ek_wm8731_init() called\n");
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index b8066ef..46dbfd0 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -153,8 +153,7 @@
static int cq93vc_probe(struct snd_soc_codec *codec)
{
- struct davinci_vc *davinci_vc =
- mfd_get_data(to_platform_device(codec->dev));
+ struct davinci_vc *davinci_vc = codec->dev->platform_data;
davinci_vc->cq93vc.codec = codec;
codec->control_data = davinci_vc;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 575238d..bec788b 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -26,7 +26,6 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
-#include <linux/mfd/core.h>
#include <linux/i2c/twl.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -733,8 +732,7 @@
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
- struct twl4030_codec_audio_data *pdata =
- mfd_get_data(to_platform_device(codec->dev));
+ struct twl4030_codec_audio_data *pdata = codec->dev->platform_data;
unsigned char hs_gain, hs_pop;
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
/* Base values for ramp delay calculation: 2^19 - 2^26 */
@@ -2299,7 +2297,7 @@
static int __devinit twl4030_codec_probe(struct platform_device *pdev)
{
- struct twl4030_codec_audio_data *pdata = mfd_get_data(pdev);
+ struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "platform_data is missing\n");
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index c8a874d..5836201 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -441,8 +441,7 @@
static int wl1273_probe(struct snd_soc_codec *codec)
{
- struct wl1273_core **core =
- mfd_get_data(to_platform_device(codec->dev));
+ struct wl1273_core **core = codec->dev->platform_data;
struct wl1273_priv *wl1273;
int r;
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index 14d0716..bcc2089 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -22,7 +22,7 @@
SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_INPUT("WM1250 Input"),
-SND_SOC_DAPM_INPUT("WM1250 Output"),
+SND_SOC_DAPM_OUTPUT("WM1250 Output"),
};
static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = {
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 736b785..fbee556c 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1378,7 +1378,7 @@
static int wm8400_codec_probe(struct snd_soc_codec *codec)
{
- struct wm8400 *wm8400 = mfd_get_data(to_platform_device(codec->dev));
+ struct wm8400 *wm8400 = dev_get_platdata(codec->dev);
struct wm8400_priv *priv;
int ret;
u16 reg;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 6dec7ce..2dc964b 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -198,7 +198,7 @@
{
struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(source->codec);
- return wm8731->sysclk_type == WM8731_SYSCLK_MCLK;
+ return wm8731->sysclk_type == WM8731_SYSCLK_XTAL;
}
static const struct snd_soc_dapm_route wm8731_intercon[] = {
diff --git a/sound/soc/codecs/wm8915.c b/sound/soc/codecs/wm8915.c
index ccc9bd8..a0b1a72 100644
--- a/sound/soc/codecs/wm8915.c
+++ b/sound/soc/codecs/wm8915.c
@@ -19,7 +19,6 @@
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
-#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c
index 13e05a3..9259f1f 100644
--- a/sound/soc/davinci/davinci-vcif.c
+++ b/sound/soc/davinci/davinci-vcif.c
@@ -205,7 +205,7 @@
static int davinci_vcif_probe(struct platform_device *pdev)
{
- struct davinci_vc *davinci_vc = mfd_get_data(pdev);
+ struct davinci_vc *davinci_vc = pdev->dev.platform_data;
struct davinci_vcif_dev *davinci_vcif_dev;
int ret;
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index b592298..99054cf 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -65,14 +65,6 @@
Say Y if you want to add support for SoC audio on the
Gumstix Overo or CompuLab CM-T35
-config SND_OMAP_SOC_OMAP2EVM
- tristate "SoC Audio support for OMAP2EVM board"
- depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP2EVM
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on the omap2evm board.
-
config SND_OMAP_SOC_OMAP3EVM
tristate "SoC Audio support for OMAP3EVM board"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3EVM
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index ba9fc65..6c2c87e 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -13,7 +13,6 @@
snd-soc-ams-delta-objs := ams-delta.o
snd-soc-osk5912-objs := osk5912.o
snd-soc-overo-objs := overo.o
-snd-soc-omap2evm-objs := omap2evm.o
snd-soc-omap3evm-objs := omap3evm.o
snd-soc-am3517evm-objs := am3517evm.o
snd-soc-sdp3430-objs := sdp3430.o
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
deleted file mode 100644
index 29b60d6..0000000
--- a/sound/soc/omap/omap2evm.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * omap2evm.c -- SoC audio machine driver for omap2evm board
- *
- * Author: Arun KS <arunks@mistralsolutions.com>
- *
- * Based on sound/soc/omap/overo.c by Steve Sakoman
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-#include <plat/mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int omap2evm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
-
- /* Set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0) {
- printk(KERN_ERR "can't set codec DAI configuration\n");
- return ret;
- }
-
- /* Set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0) {
- printk(KERN_ERR "can't set cpu DAI configuration\n");
- return ret;
- }
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set codec system clock\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_ops omap2evm_ops = {
- .hw_params = omap2evm_hw_params,
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link omap2evm_dai = {
- .name = "TWL4030",
- .stream_name = "TWL4030",
- .cpu_dai_name = "omap-mcbsp-dai.1",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-pcm-audio",
- .codec_name = "twl4030-codec",
- .ops = &omap2evm_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_omap2evm = {
- .name = "omap2evm",
- .dai_link = &omap2evm_dai,
- .num_links = 1,
-};
-
-static struct platform_device *omap2evm_snd_device;
-
-static int __init omap2evm_soc_init(void)
-{
- int ret;
-
- if (!machine_is_omap2evm())
- return -ENODEV;
- printk(KERN_INFO "omap2evm SoC init\n");
-
- omap2evm_snd_device = platform_device_alloc("soc-audio", -1);
- if (!omap2evm_snd_device) {
- printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(omap2evm_snd_device, &snd_soc_omap2evm);
-
- ret = platform_device_add(omap2evm_snd_device);
- if (ret)
- goto err1;
-
- return 0;
-
-err1:
- printk(KERN_ERR "Unable to add platform device\n");
- platform_device_put(omap2evm_snd_device);
-
- return ret;
-}
-module_init(omap2evm_soc_init);
-
-static void __exit omap2evm_soc_exit(void)
-{
- platform_device_unregister(omap2evm_snd_device);
-}
-module_exit(omap2evm_soc_exit);
-
-MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
-MODULE_DESCRIPTION("ALSA SoC omap2evm");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
index 2afabaf..1a591f1 100644
--- a/sound/soc/pxa/raumfeld.c
+++ b/sound/soc/pxa/raumfeld.c
@@ -151,13 +151,13 @@
.hw_params = raumfeld_cs4270_hw_params,
};
-static int raumfeld_line_suspend(struct snd_soc_card *card)
+static int raumfeld_analog_suspend(struct snd_soc_card *card)
{
raumfeld_enable_audio(false);
return 0;
}
-static int raumfeld_line_resume(struct snd_soc_card *card)
+static int raumfeld_analog_resume(struct snd_soc_card *card)
{
raumfeld_enable_audio(true);
return 0;
@@ -225,32 +225,53 @@
.hw_params = raumfeld_ak4104_hw_params,
};
-static struct snd_soc_dai_link raumfeld_dai[] = {
-{
- .name = "ak4104",
- .stream_name = "Playback",
- .cpu_dai_name = "pxa-ssp-dai.1",
- .codec_dai_name = "ak4104-hifi",
- .platform_name = "pxa-pcm-audio",
- .ops = &raumfeld_ak4104_ops,
- .codec_name = "ak4104-codec.0",
-},
-{
- .name = "CS4270",
- .stream_name = "CS4270",
- .cpu_dai_name = "pxa-ssp-dai.0",
- .platform_name = "pxa-pcm-audio",
- .codec_dai_name = "cs4270-hifi",
- .codec_name = "cs4270-codec.0-0048",
- .ops = &raumfeld_cs4270_ops,
-},};
+#define DAI_LINK_CS4270 \
+{ \
+ .name = "CS4270", \
+ .stream_name = "CS4270", \
+ .cpu_dai_name = "pxa-ssp-dai.0", \
+ .platform_name = "pxa-pcm-audio", \
+ .codec_dai_name = "cs4270-hifi", \
+ .codec_name = "cs4270-codec.0-0048", \
+ .ops = &raumfeld_cs4270_ops, \
+}
-static struct snd_soc_card snd_soc_raumfeld = {
- .name = "Raumfeld",
- .dai_link = raumfeld_dai,
- .suspend_post = raumfeld_line_suspend,
- .resume_pre = raumfeld_line_resume,
- .num_links = ARRAY_SIZE(raumfeld_dai),
+#define DAI_LINK_AK4104 \
+{ \
+ .name = "ak4104", \
+ .stream_name = "Playback", \
+ .cpu_dai_name = "pxa-ssp-dai.1", \
+ .codec_dai_name = "ak4104-hifi", \
+ .platform_name = "pxa-pcm-audio", \
+ .ops = &raumfeld_ak4104_ops, \
+ .codec_name = "spi0.0", \
+}
+
+static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] =
+{
+ DAI_LINK_CS4270,
+ DAI_LINK_AK4104,
+};
+
+static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] =
+{
+ DAI_LINK_CS4270,
+};
+
+static struct snd_soc_card snd_soc_raumfeld_connector = {
+ .name = "Raumfeld Connector",
+ .dai_link = snd_soc_raumfeld_connector_dai,
+ .num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai),
+ .suspend_post = raumfeld_analog_suspend,
+ .resume_pre = raumfeld_analog_resume,
+};
+
+static struct snd_soc_card snd_soc_raumfeld_speaker = {
+ .name = "Raumfeld Speaker",
+ .dai_link = snd_soc_raumfeld_speaker_dai,
+ .num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai),
+ .suspend_post = raumfeld_analog_suspend,
+ .resume_pre = raumfeld_analog_resume,
};
static struct platform_device *raumfeld_audio_device;
@@ -271,22 +292,25 @@
set_max9485_clk(MAX9485_MCLK_FREQ_122880);
- /* Register LINE and SPDIF */
+ /* Register analog device */
raumfeld_audio_device = platform_device_alloc("soc-audio", 0);
if (!raumfeld_audio_device)
return -ENOMEM;
- platform_set_drvdata(raumfeld_audio_device,
- &snd_soc_raumfeld);
- ret = platform_device_add(raumfeld_audio_device);
-
- /* no S/PDIF on Speakers */
if (machine_is_raumfeld_speaker())
+ platform_set_drvdata(raumfeld_audio_device,
+ &snd_soc_raumfeld_speaker);
+
+ if (machine_is_raumfeld_connector())
+ platform_set_drvdata(raumfeld_audio_device,
+ &snd_soc_raumfeld_connector);
+
+ ret = platform_device_add(raumfeld_audio_device);
+ if (ret < 0)
return ret;
raumfeld_enable_audio(true);
-
- return ret;
+ return 0;
}
static void __exit raumfeld_audio_exit(void)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index bb7cd58..d75043e 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1306,10 +1306,6 @@
/* no, then find CPU DAI from registered DAIs*/
list_for_each_entry(cpu_dai, &dai_list, list) {
if (!strcmp(cpu_dai->name, dai_link->cpu_dai_name)) {
-
- if (!try_module_get(cpu_dai->dev->driver->owner))
- return -ENODEV;
-
rtd->cpu_dai = cpu_dai;
goto find_codec;
}
@@ -1622,11 +1618,15 @@
/* probe the cpu_dai */
if (!cpu_dai->probed) {
+ if (!try_module_get(cpu_dai->dev->driver->owner))
+ return -ENODEV;
+
if (cpu_dai->driver->probe) {
ret = cpu_dai->driver->probe(cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",
cpu_dai->name);
+ module_put(cpu_dai->dev->driver->owner);
return ret;
}
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 456617e..999bb08 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1110,7 +1110,7 @@
trace_snd_soc_dapm_start(card);
list_for_each_entry(d, &card->dapm_list, list)
- if (d->n_widgets)
+ if (d->n_widgets || d->codec == NULL)
d->dev_power = 0;
/* Check which widgets we need to power and store them in
diff --git a/sound/usb/card.c b/sound/usb/card.c
index a90662a..220c616 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -48,6 +48,7 @@
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
+#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
@@ -492,14 +493,6 @@
}
}
- chip->txfr_quirk = 0;
- err = 1; /* continue */
- if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
- /* need some special handlings */
- if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0)
- goto __error;
- }
-
/*
* For devices with more than one control interface, we assume the
* first contains the audio controls. We might need a more specific
@@ -508,6 +501,14 @@
if (!chip->ctrl_intf)
chip->ctrl_intf = alts;
+ chip->txfr_quirk = 0;
+ err = 1; /* continue */
+ if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
+ /* need some special handlings */
+ if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0)
+ goto __error;
+ }
+
if (err > 0) {
/* create normal USB audio interfaces */
if (snd_usb_create_streams(chip, ifnum) < 0 ||
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index eab06ed..c22fa76 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -86,16 +86,6 @@
const struct usbmix_selector_map *selector_map;
};
-enum {
- USB_MIXER_BOOLEAN,
- USB_MIXER_INV_BOOLEAN,
- USB_MIXER_S8,
- USB_MIXER_U8,
- USB_MIXER_S16,
- USB_MIXER_U16,
-};
-
-
/*E-mu 0202/0404/0204 eXtension Unit(XU) control*/
enum {
USB_XU_CLOCK_RATE = 0xe301,
@@ -535,20 +525,21 @@
* if failed, give up and free the control instance.
*/
-static int add_control_to_empty(struct mixer_build *state, struct snd_kcontrol *kctl)
+int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
{
struct usb_mixer_elem_info *cval = kctl->private_data;
int err;
- while (snd_ctl_find_id(state->chip->card, &kctl->id))
+ while (snd_ctl_find_id(mixer->chip->card, &kctl->id))
kctl->id.index++;
- if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) {
+ if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) {
snd_printd(KERN_ERR "cannot add control (err = %d)\n", err);
return err;
}
cval->elem_id = &kctl->id;
- cval->next_id_elem = state->mixer->id_elems[cval->id];
- state->mixer->id_elems[cval->id] = cval;
+ cval->next_id_elem = mixer->id_elems[cval->id];
+ mixer->id_elems[cval->id] = cval;
return 0;
}
@@ -984,6 +975,9 @@
.put = NULL,
};
+/* This symbol is exported in order to allow the mixer quirks to
+ * hook up to the standard feature unit control mechanism */
+struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl;
/*
* build a feature control
@@ -1176,7 +1170,7 @@
snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res);
- add_control_to_empty(state, kctl);
+ snd_usb_mixer_add_control(state->mixer, kctl);
}
@@ -1340,7 +1334,7 @@
snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n",
cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
- add_control_to_empty(state, kctl);
+ snd_usb_mixer_add_control(state->mixer, kctl);
}
@@ -1641,7 +1635,7 @@
snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
- if ((err = add_control_to_empty(state, kctl)) < 0)
+ if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0)
return err;
}
return 0;
@@ -1858,7 +1852,7 @@
snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
cval->id, kctl->id.name, desc->bNrInPins);
- if ((err = add_control_to_empty(state, kctl)) < 0)
+ if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0)
return err;
return 0;
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index b4a2c81..ae1a14d 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -24,7 +24,16 @@
u8 xonar_u1_status;
};
-#define MAX_CHANNELS 10 /* max logical channels */
+#define MAX_CHANNELS 16 /* max logical channels */
+
+enum {
+ USB_MIXER_BOOLEAN,
+ USB_MIXER_INV_BOOLEAN,
+ USB_MIXER_S8,
+ USB_MIXER_U8,
+ USB_MIXER_S16,
+ USB_MIXER_U16,
+};
struct usb_mixer_elem_info {
struct usb_mixer_interface *mixer;
@@ -55,4 +64,7 @@
void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer);
int snd_usb_mixer_activate(struct usb_mixer_interface *mixer);
+int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl);
+
#endif /* __USBMIXER_H */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 9146cff..3d0f487 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -40,6 +40,8 @@
#include "mixer_quirks.h"
#include "helper.h"
+extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
+
/*
* Sound Blaster remote control configuration
*
@@ -492,6 +494,69 @@
return err;
}
+/* M-Audio FastTrack Ultra quirks */
+
+/* private_free callback */
+static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+{
+ kfree(kctl->private_data);
+ kctl->private_data = NULL;
+}
+
+static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer,
+ int in, int out, const char *name)
+{
+ struct usb_mixer_elem_info *cval;
+ struct snd_kcontrol *kctl;
+
+ cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+ if (!cval)
+ return -ENOMEM;
+
+ cval->id = 5;
+ cval->mixer = mixer;
+ cval->val_type = USB_MIXER_S16;
+ cval->channels = 1;
+ cval->control = out + 1;
+ cval->cmask = 1 << in;
+
+ kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+ if (!kctl) {
+ kfree(cval);
+ return -ENOMEM;
+ }
+
+ snprintf(kctl->id.name, sizeof(kctl->id.name), name);
+ kctl->private_free = usb_mixer_elem_free;
+ return snd_usb_mixer_add_control(mixer, kctl);
+}
+
+static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
+{
+ char name[64];
+ int in, out, err;
+
+ for (out = 0; out < 8; out++) {
+ for (in = 0; in < 8; in++) {
+ snprintf(name, sizeof(name),
+ "AIn%d - Out%d Capture Volume", in + 1, out + 1);
+ err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+ if (err < 0)
+ return err;
+ }
+
+ for (in = 8; in < 16; in++) {
+ snprintf(name, sizeof(name),
+ "DIn%d - Out%d Playback Volume", in - 7, out + 1);
+ err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
unsigned char samplerate_id)
{
@@ -533,6 +598,11 @@
snd_audigy2nx_proc_read);
break;
+ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
+ case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+ err = snd_maudio_ftu_create_mixer(mixer);
+ break;
+
case USB_ID(0x0b05, 0x1739):
case USB_ID(0x0b05, 0x1743):
err = snd_xonar_u1_controls_create(mixer);
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 78792a8..0b2ae8e 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -1988,7 +1988,7 @@
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
},
{
.ifnum = 1,
@@ -2055,7 +2055,7 @@
.data = & (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
- .type = QUIRK_IGNORE_INTERFACE
+ .type = QUIRK_AUDIO_STANDARD_MIXER,
},
{
.ifnum = 1,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index bd13d72..2e969cb 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -19,6 +19,7 @@
#include <linux/usb.h>
#include <linux/usb/audio.h>
+#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
@@ -263,6 +264,20 @@
}
/*
+ * Create a standard mixer for the specified interface.
+ */
+static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ struct usb_driver *driver,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ if (quirk->ifnum < 0)
+ return 0;
+
+ return snd_usb_create_mixer(chip, quirk->ifnum, 0);
+}
+
+/*
* audio-interface quirks
*
* returns zero if no standard audio/MIDI parsing is needed.
@@ -294,7 +309,8 @@
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
- [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
+ [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk,
+ [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
};
if (quirk->type < QUIRK_TYPE_COUNT) {
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 32f2a97..1e79986 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -84,6 +84,7 @@
QUIRK_AUDIO_FIXED_ENDPOINT,
QUIRK_AUDIO_EDIROL_UAXX,
QUIRK_AUDIO_ALIGN_TRANSFER,
+ QUIRK_AUDIO_STANDARD_MIXER,
QUIRK_TYPE_COUNT
};