Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Pull networking fixes from David Miller:
"Just a bunch of small fixes and tidy ups:
1) Finish the "busy_poll" renames, from Eliezer Tamir.
2) Fix RCU stalls in IFB driver, from Ding Tianhong.
3) Linearize buffers properly in tun/macvtap zerocopy code.
4) Don't crash on rmmod in vxlan, from Pravin B Shelar.
5) Spinlock used before init in alx driver, from Maarten Lankhorst.
6) A sparse warning fix in bnx2x broke TSO checksums, fix from Dmitry
Kravkov.
7) Dummy and ifb driver load failure paths can oops, fixes from Tan
Xiaojun and Ding Tianhong.
8) Correct MTU calculations in IP tunnels, from Alexander Duyck.
9) Account all TCP retransmits in SNMP stats properly, from Yuchung
Cheng.
10) atl1e and via-rhine do not handle DMA mapping failures properly,
from Neil Horman.
11) Various equal-cost multipath route fixes in ipv6 from Hannes
Frederic Sowa"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (36 commits)
ipv6: only static routes qualify for equal cost multipathing
via-rhine: fix dma mapping errors
atl1e: fix dma mapping warnings
tcp: account all retransmit failures
usb/net/r815x: fix cast to restricted __le32
usb/net/r8152: fix integer overflow in expression
net: access page->private by using page_private
net: strict_strtoul is obsolete, use kstrtoul instead
drivers/net/ieee802154: don't use devm_pinctrl_get_select_default() in probe
drivers/net/ethernet/cadence: don't use devm_pinctrl_get_select_default() in probe
drivers/net/can/c_can: don't use devm_pinctrl_get_select_default() in probe
net/usb: add relative mii functions for r815x
net/tipc: use %*phC to dump small buffers in hex form
qlcnic: Adding Maintainers.
gre: Fix MTU sizing check for gretap tunnels
pkt_sched: sch_qfq: remove forward declaration of qfq_update_agg_ts
pkt_sched: sch_qfq: improve efficiency of make_eligible
gso: Update tunnel segmentation to support Tx checksum offload
inet: fix spacing in assignment
ifb: fix oops when loading the ifb failed
...
diff --git a/Documentation/ABI/stable/sysfs-driver-ib_srp b/Documentation/ABI/stable/sysfs-driver-ib_srp
index 481aae9..5c53d28 100644
--- a/Documentation/ABI/stable/sysfs-driver-ib_srp
+++ b/Documentation/ABI/stable/sysfs-driver-ib_srp
@@ -54,6 +54,13 @@
ib_srp. Specifying a value that exceeds cmd_sg_entries is
only safe with partial memory descriptor list support enabled
(allow_ext_sg=1).
+ * comp_vector, a number in the range 0..n-1 specifying the
+ MSI-X completion vector. Some HCA's allocate multiple (n)
+ MSI-X vectors per HCA port. If the IRQ affinity masks of
+ these interrupts have been configured such that each MSI-X
+ interrupt is handled by a different CPU then the comp_vector
+ parameter can be used to spread the SRP completion workload
+ over multiple CPU's.
What: /sys/class/infiniband_srp/srp-<hca>-<port_number>/ibdev
Date: January 2, 2006
diff --git a/Documentation/ABI/stable/sysfs-module b/Documentation/ABI/stable/sysfs-module
index a0dd21c..6272ae5 100644
--- a/Documentation/ABI/stable/sysfs-module
+++ b/Documentation/ABI/stable/sysfs-module
@@ -4,9 +4,13 @@
/sys/module/MODULENAME
The name of the module that is in the kernel. This
- module name will show up either if the module is built
- directly into the kernel, or if it is loaded as a
- dynamic module.
+ module name will always show up if the module is loaded as a
+ dynamic module. If it is built directly into the kernel, it
+ will only show up if it has a version or at least one
+ parameter.
+
+ Note: The conditions of creation in the built-in case are not
+ by design and may be removed in the future.
/sys/module/MODULENAME/parameters
This directory contains individual files that are each
diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
index 8b25ffb..3c1cc24 100644
--- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
+++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events
@@ -29,7 +29,7 @@
What: /sys/devices/cpu/events/PM_1PLUS_PPC_CMPL
/sys/devices/cpu/events/PM_BRU_FIN
- /sys/devices/cpu/events/PM_BRU_MPRED
+ /sys/devices/cpu/events/PM_BR_MPRED
/sys/devices/cpu/events/PM_CMPLU_STALL
/sys/devices/cpu/events/PM_CMPLU_STALL_BRU
/sys/devices/cpu/events/PM_CMPLU_STALL_DCACHE_MISS
diff --git a/Documentation/ABI/testing/sysfs-devices-edac b/Documentation/ABI/testing/sysfs-devices-edac
index 30ee78a..6568e00 100644
--- a/Documentation/ABI/testing/sysfs-devices-edac
+++ b/Documentation/ABI/testing/sysfs-devices-edac
@@ -77,7 +77,7 @@
What: /sys/devices/system/edac/mc/mc*/max_location
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This attribute file displays the information about the last
available memory slot in this memory controller. It is used by
@@ -85,7 +85,7 @@
What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/size
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This attribute file will display the size of dimm or rank.
For dimm*/size, this is the size, in MB of the DIMM memory
@@ -96,14 +96,14 @@
What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_dev_type
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This attribute file will display what type of DRAM device is
being utilized on this DIMM (x1, x2, x4, x8, ...).
What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_edac_mode
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This attribute file will display what type of Error detection
and correction is being utilized. For example: S4ECD4ED would
@@ -111,7 +111,7 @@
What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_label
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This control file allows this DIMM to have a label assigned
to it. With this label in the module, when errors occur
@@ -126,14 +126,14 @@
What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_location
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This attribute file will display the location (csrow/channel,
branch/channel/slot or channel/slot) of the dimm or rank.
What: /sys/devices/system/edac/mc/mc*/(dimm|rank)*/dimm_mem_type
Date: April 2012
-Contact: Mauro Carvalho Chehab <mchehab@redhat.com>
+Contact: Mauro Carvalho Chehab <m.chehab@samsung.com>
linux-edac@vger.kernel.org
Description: This attribute file will display what type of memory is
currently on this csrow. Normally, either buffered or
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index f43542a..0c7195e 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -2254,7 +2254,7 @@
<orderedlist>
<listitem>
<para>The <constant>VIDIOC_G_CHIP_IDENT</constant> ioctl was renamed
-to <constant>VIDIOC_G_CHIP_IDENT_OLD</constant> and &VIDIOC-DBG-G-CHIP-IDENT;
+to <constant>VIDIOC_G_CHIP_IDENT_OLD</constant> and <constant>VIDIOC_DBG_G_CHIP_IDENT</constant>
was introduced in its place. The old struct <structname>v4l2_chip_ident</structname>
was renamed to <structname id="v4l2-chip-ident-old">v4l2_chip_ident_old</structname>.</para>
</listitem>
@@ -2513,6 +2513,16 @@
</orderedlist>
</section>
+ <section>
+ <title>V4L2 in Linux 3.11</title>
+ <orderedlist>
+ <listitem>
+ <para>Remove obsolete <constant>VIDIOC_DBG_G_CHIP_IDENT</constant> ioctl.
+ </para>
+ </listitem>
+ </orderedlist>
+ </section>
+
<section id="other">
<title>Relation of V4L2 to other Linux multimedia APIs</title>
@@ -2596,7 +2606,7 @@
ioctls.</para>
</listitem>
<listitem>
- <para>&VIDIOC-DBG-G-CHIP-IDENT; ioctl.</para>
+ <para>&VIDIOC-DBG-G-CHIP-INFO; ioctl.</para>
</listitem>
<listitem>
<para>&VIDIOC-ENUM-DV-TIMINGS;, &VIDIOC-QUERY-DV-TIMINGS; and
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index bfe823d..8469fe1 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -141,6 +141,14 @@
applications. -->
<revision>
+ <revnumber>3.11</revnumber>
+ <date>2013-05-26</date>
+ <authorinitials>hv</authorinitials>
+ <revremark>Remove obsolete VIDIOC_DBG_G_CHIP_IDENT ioctl.
+ </revremark>
+ </revision>
+
+ <revision>
<revnumber>3.10</revnumber>
<date>2013-03-25</date>
<authorinitials>hv</authorinitials>
@@ -493,7 +501,7 @@
</partinfo>
<title>Video for Linux Two API Specification</title>
- <subtitle>Revision 3.10</subtitle>
+ <subtitle>Revision 3.11</subtitle>
<chapter id="common">
&sub-common;
@@ -547,7 +555,6 @@
<!-- All ioctls go here. -->
&sub-create-bufs;
&sub-cropcap;
- &sub-dbg-g-chip-ident;
&sub-dbg-g-chip-info;
&sub-dbg-g-register;
&sub-decoder-cmd;
diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml
deleted file mode 100644
index 921e185..0000000
--- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml
+++ /dev/null
@@ -1,271 +0,0 @@
-<refentry id="vidioc-dbg-g-chip-ident">
- <refmeta>
- <refentrytitle>ioctl VIDIOC_DBG_G_CHIP_IDENT</refentrytitle>
- &manvol;
- </refmeta>
-
- <refnamediv>
- <refname>VIDIOC_DBG_G_CHIP_IDENT</refname>
- <refpurpose>Identify the chips on a TV card</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <funcsynopsis>
- <funcprototype>
- <funcdef>int <function>ioctl</function></funcdef>
- <paramdef>int <parameter>fd</parameter></paramdef>
- <paramdef>int <parameter>request</parameter></paramdef>
- <paramdef>struct v4l2_dbg_chip_ident
-*<parameter>argp</parameter></paramdef>
- </funcprototype>
- </funcsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Arguments</title>
-
- <variablelist>
- <varlistentry>
- <term><parameter>fd</parameter></term>
- <listitem>
- <para>&fd;</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><parameter>request</parameter></term>
- <listitem>
- <para>VIDIOC_DBG_G_CHIP_IDENT</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><parameter>argp</parameter></term>
- <listitem>
- <para></para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Description</title>
-
- <note>
- <title>Experimental</title>
-
- <para>This is an <link
-linkend="experimental">experimental</link> interface and may change in
-the future.</para>
- </note>
-
- <para>For driver debugging purposes this ioctl allows test
-applications to query the driver about the chips present on the TV
-card. Regular applications must not use it. When you found a chip
-specific bug, please contact the linux-media mailing list (&v4l-ml;)
-so it can be fixed.</para>
-
- <para>To query the driver applications must initialize the
-<structfield>match.type</structfield> and
-<structfield>match.addr</structfield> or <structfield>match.name</structfield>
-fields of a &v4l2-dbg-chip-ident;
-and call <constant>VIDIOC_DBG_G_CHIP_IDENT</constant> with a pointer to
-this structure. On success the driver stores information about the
-selected chip in the <structfield>ident</structfield> and
-<structfield>revision</structfield> fields. On failure the structure
-remains unchanged.</para>
-
- <para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_HOST</constant>,
-<structfield>match.addr</structfield> selects the nth non-&i2c; chip
-on the TV card. You can enumerate all chips by starting at zero and
-incrementing <structfield>match.addr</structfield> by one until
-<constant>VIDIOC_DBG_G_CHIP_IDENT</constant> fails with an &EINVAL;.
-The number zero always selects the host chip, ⪚ the chip connected
-to the PCI or USB bus.</para>
-
- <para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_I2C_DRIVER</constant>,
-<structfield>match.name</structfield> contains the I2C driver name.
-For instance
-<constant>"saa7127"</constant> will match any chip
-supported by the saa7127 driver, regardless of its &i2c; bus address.
-When multiple chips supported by the same driver are present, the
-ioctl will return <constant>V4L2_IDENT_AMBIGUOUS</constant> in the
-<structfield>ident</structfield> field.</para>
-
- <para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_I2C_ADDR</constant>,
-<structfield>match.addr</structfield> selects a chip by its 7 bit
-&i2c; bus address.</para>
-
- <para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_AC97</constant>,
-<structfield>match.addr</structfield> selects the nth AC97 chip
-on the TV card. You can enumerate all chips by starting at zero and
-incrementing <structfield>match.addr</structfield> by one until
-<constant>VIDIOC_DBG_G_CHIP_IDENT</constant> fails with an &EINVAL;.</para>
-
- <para>On success, the <structfield>ident</structfield> field will
-contain a chip ID from the Linux
-<filename>media/v4l2-chip-ident.h</filename> header file, and the
-<structfield>revision</structfield> field will contain a driver
-specific value, or zero if no particular revision is associated with
-this chip.</para>
-
- <para>When the driver could not identify the selected chip,
-<structfield>ident</structfield> will contain
-<constant>V4L2_IDENT_UNKNOWN</constant>. When no chip matched
-the ioctl will succeed but the
-<structfield>ident</structfield> field will contain
-<constant>V4L2_IDENT_NONE</constant>. If multiple chips matched,
-<structfield>ident</structfield> will contain
-<constant>V4L2_IDENT_AMBIGUOUS</constant>. In all these cases the
-<structfield>revision</structfield> field remains unchanged.</para>
-
- <para>This ioctl is optional, not all drivers may support it. It
-was introduced in Linux 2.6.21, but the API was changed to the
-one described here in 2.6.29.</para>
-
- <para>We recommended the <application>v4l2-dbg</application>
-utility over calling this ioctl directly. It is available from the
-LinuxTV v4l-dvb repository; see <ulink
-url="http://linuxtv.org/repo/">http://linuxtv.org/repo/</ulink> for
-access instructions.</para>
-
- <!-- Note for convenience vidioc-dbg-g-register.sgml
- contains a duplicate of this table. -->
- <table pgwide="1" frame="none" id="ident-v4l2-dbg-match">
- <title>struct <structname>v4l2_dbg_match</structname></title>
- <tgroup cols="4">
- &cs-ustr;
- <tbody valign="top">
- <row>
- <entry>__u32</entry>
- <entry><structfield>type</structfield></entry>
- <entry>See <xref linkend="ident-chip-match-types" /> for a list of
-possible types.</entry>
- </row>
- <row>
- <entry>union</entry>
- <entry>(anonymous)</entry>
- </row>
- <row>
- <entry></entry>
- <entry>__u32</entry>
- <entry><structfield>addr</structfield></entry>
- <entry>Match a chip by this number, interpreted according
-to the <structfield>type</structfield> field.</entry>
- </row>
- <row>
- <entry></entry>
- <entry>char</entry>
- <entry><structfield>name[32]</structfield></entry>
- <entry>Match a chip by this name, interpreted according
-to the <structfield>type</structfield> field.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <table pgwide="1" frame="none" id="v4l2-dbg-chip-ident">
- <title>struct <structname>v4l2_dbg_chip_ident</structname></title>
- <tgroup cols="3">
- &cs-str;
- <tbody valign="top">
- <row>
- <entry>struct v4l2_dbg_match</entry>
- <entry><structfield>match</structfield></entry>
- <entry>How to match the chip, see <xref linkend="ident-v4l2-dbg-match" />.</entry>
- </row>
- <row>
- <entry>__u32</entry>
- <entry><structfield>ident</structfield></entry>
- <entry>A chip identifier as defined in the Linux
-<filename>media/v4l2-chip-ident.h</filename> header file, or one of
-the values from <xref linkend="chip-ids" />.</entry>
- </row>
- <row>
- <entry>__u32</entry>
- <entry><structfield>revision</structfield></entry>
- <entry>A chip revision, chip and driver specific.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <!-- Note for convenience vidioc-dbg-g-register.sgml
- contains a duplicate of this table. -->
- <table pgwide="1" frame="none" id="ident-chip-match-types">
- <title>Chip Match Types</title>
- <tgroup cols="3">
- &cs-def;
- <tbody valign="top">
- <row>
- <entry><constant>V4L2_CHIP_MATCH_BRIDGE</constant></entry>
- <entry>0</entry>
- <entry>Match the nth chip on the card, zero for the
- bridge chip. Does not match sub-devices.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_I2C_DRIVER</constant></entry>
- <entry>1</entry>
- <entry>Match an &i2c; chip by its driver name.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_I2C_ADDR</constant></entry>
- <entry>2</entry>
- <entry>Match a chip by its 7 bit &i2c; bus address.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_AC97</constant></entry>
- <entry>3</entry>
- <entry>Match the nth anciliary AC97 chip.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_SUBDEV</constant></entry>
- <entry>4</entry>
- <entry>Match the nth sub-device. Can't be used with this ioctl.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <!-- This is an anonymous enum in media/v4l2-chip-ident.h. -->
- <table pgwide="1" frame="none" id="chip-ids">
- <title>Chip Identifiers</title>
- <tgroup cols="3">
- &cs-def;
- <tbody valign="top">
- <row>
- <entry><constant>V4L2_IDENT_NONE</constant></entry>
- <entry>0</entry>
- <entry>No chip matched.</entry>
- </row>
- <row>
- <entry><constant>V4L2_IDENT_AMBIGUOUS</constant></entry>
- <entry>1</entry>
- <entry>Multiple chips matched.</entry>
- </row>
- <row>
- <entry><constant>V4L2_IDENT_UNKNOWN</constant></entry>
- <entry>2</entry>
- <entry>A chip is present at this address, but the driver
-could not identify it.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </refsect1>
-
- <refsect1>
- &return-value;
-
- <variablelist>
- <varlistentry>
- <term><errorcode>EINVAL</errorcode></term>
- <listitem>
- <para>The <structfield>match_type</structfield> is invalid.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml
index e1cece6..4c4603c 100644
--- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml
@@ -73,8 +73,7 @@
and call <constant>VIDIOC_DBG_G_CHIP_INFO</constant> with a pointer to
this structure. On success the driver stores information about the
selected chip in the <structfield>name</structfield> and
-<structfield>flags</structfield> fields. On failure the structure
-remains unchanged.</para>
+<structfield>flags</structfield> fields.</para>
<para>When <structfield>match.type</structfield> is
<constant>V4L2_CHIP_MATCH_BRIDGE</constant>,
@@ -132,7 +131,7 @@
<entry>char</entry>
<entry><structfield>name[32]</structfield></entry>
<entry>Match a chip by this name, interpreted according
-to the <structfield>type</structfield> field.</entry>
+to the <structfield>type</structfield> field. Currently unused.</entry>
</row>
</tbody>
</tgroup>
@@ -183,21 +182,6 @@
bridge chip. Does not match sub-devices.</entry>
</row>
<row>
- <entry><constant>V4L2_CHIP_MATCH_I2C_DRIVER</constant></entry>
- <entry>1</entry>
- <entry>Match an &i2c; chip by its driver name. Can't be used with this ioctl.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_I2C_ADDR</constant></entry>
- <entry>2</entry>
- <entry>Match a chip by its 7 bit &i2c; bus address. Can't be used with this ioctl.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_AC97</constant></entry>
- <entry>3</entry>
- <entry>Match the nth anciliary AC97 chip. Can't be used with this ioctl.</entry>
- </row>
- <row>
<entry><constant>V4L2_CHIP_MATCH_SUBDEV</constant></entry>
<entry>4</entry>
<entry>Match the nth sub-device.</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
index d13bac9..3d038e7 100644
--- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
@@ -76,7 +76,7 @@
to enable these ioctls.</para>
<para>To write a register applications must initialize all fields
-of a &v4l2-dbg-register; and call
+of a &v4l2-dbg-register; except for <structfield>size</structfield> and call
<constant>VIDIOC_DBG_S_REGISTER</constant> with a pointer to this
structure. The <structfield>match.type</structfield> and
<structfield>match.addr</structfield> or <structfield>match.name</structfield>
@@ -91,8 +91,8 @@
<structfield>reg</structfield> fields, and call
<constant>VIDIOC_DBG_G_REGISTER</constant> with a pointer to this
structure. On success the driver stores the register value in the
-<structfield>val</structfield> field. On failure the structure remains
-unchanged.</para>
+<structfield>val</structfield> field and the size (in bytes) of the
+value in <structfield>size</structfield>.</para>
<para>When <structfield>match.type</structfield> is
<constant>V4L2_CHIP_MATCH_BRIDGE</constant>,
@@ -102,39 +102,9 @@
present with the &VIDIOC-DBG-G-CHIP-INFO; ioctl.</para>
<para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_I2C_DRIVER</constant>,
-<structfield>match.name</structfield> contains the I2C driver name.
-For instance
-<constant>"saa7127"</constant> will match any chip
-supported by the saa7127 driver, regardless of its &i2c; bus address.
-When multiple chips supported by the same driver are present, the
-effect of these ioctls is undefined. Again with the
-&VIDIOC-DBG-G-CHIP-INFO; ioctl you can find out which &i2c; chips are
-present.</para>
-
- <para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_I2C_ADDR</constant>,
-<structfield>match.addr</structfield> selects a chip by its 7 bit &i2c;
-bus address.</para>
-
- <para>When <structfield>match.type</structfield> is
-<constant>V4L2_CHIP_MATCH_AC97</constant>,
-<structfield>match.addr</structfield> selects the nth AC97 chip
-on the TV card.</para>
-
- <para>When <structfield>match.type</structfield> is
<constant>V4L2_CHIP_MATCH_SUBDEV</constant>,
<structfield>match.addr</structfield> selects the nth sub-device.</para>
- <note>
- <title>Success not guaranteed</title>
-
- <para>Due to a flaw in the Linux &i2c; bus driver these ioctls may
-return successfully without actually reading or writing a register. To
-catch the most likely failure we recommend a &VIDIOC-DBG-G-CHIP-INFO;
-call confirming the presence of the selected &i2c; chip.</para>
- </note>
-
<para>These ioctls are optional, not all drivers may support them.
However when a driver supports these ioctls it must also support
&VIDIOC-DBG-G-CHIP-INFO;. Conversely it may support
@@ -150,7 +120,7 @@
url="http://linuxtv.org/repo/">http://linuxtv.org/repo/</ulink> for
access instructions.</para>
- <!-- Note for convenience vidioc-dbg-g-chip-ident.sgml
+ <!-- Note for convenience vidioc-dbg-g-chip-info.sgml
contains a duplicate of this table. -->
<table pgwide="1" frame="none" id="v4l2-dbg-match">
<title>struct <structname>v4l2_dbg_match</structname></title>
@@ -160,7 +130,7 @@
<row>
<entry>__u32</entry>
<entry><structfield>type</structfield></entry>
- <entry>See <xref linkend="ident-chip-match-types" /> for a list of
+ <entry>See <xref linkend="chip-match-types" /> for a list of
possible types.</entry>
</row>
<row>
@@ -179,7 +149,7 @@
<entry>char</entry>
<entry><structfield>name[32]</structfield></entry>
<entry>Match a chip by this name, interpreted according
-to the <structfield>type</structfield> field.</entry>
+to the <structfield>type</structfield> field. Currently unused.</entry>
</row>
</tbody>
</tgroup>
@@ -199,6 +169,11 @@
<entry>How to match the chip, see <xref linkend="v4l2-dbg-match" />.</entry>
</row>
<row>
+ <entry>__u32</entry>
+ <entry><structfield>size</structfield></entry>
+ <entry>The register size in bytes.</entry>
+ </row>
+ <row>
<entry>__u64</entry>
<entry><structfield>reg</structfield></entry>
<entry>A register number.</entry>
@@ -213,7 +188,7 @@
</tgroup>
</table>
- <!-- Note for convenience vidioc-dbg-g-chip-ident.sgml
+ <!-- Note for convenience vidioc-dbg-g-chip-info.sgml
contains a duplicate of this table. -->
<table pgwide="1" frame="none" id="chip-match-types">
<title>Chip Match Types</title>
@@ -227,21 +202,6 @@
bridge chip. Does not match sub-devices.</entry>
</row>
<row>
- <entry><constant>V4L2_CHIP_MATCH_I2C_DRIVER</constant></entry>
- <entry>1</entry>
- <entry>Match an &i2c; chip by its driver name.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_I2C_ADDR</constant></entry>
- <entry>2</entry>
- <entry>Match a chip by its 7 bit &i2c; bus address.</entry>
- </row>
- <row>
- <entry><constant>V4L2_CHIP_MATCH_AC97</constant></entry>
- <entry>3</entry>
- <entry>Match the nth anciliary AC97 chip.</entry>
- </row>
- <row>
<entry><constant>V4L2_CHIP_MATCH_SUBDEV</constant></entry>
<entry>4</entry>
<entry>Match the nth sub-device.</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-querystd.xml b/Documentation/DocBook/media/v4l/vidioc-querystd.xml
index fe80a18..2223485 100644
--- a/Documentation/DocBook/media/v4l/vidioc-querystd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querystd.xml
@@ -54,7 +54,8 @@
VIDIOC_QUERYSTD</constant> with a pointer to a &v4l2-std-id; type. The
driver stores here a set of candidates, this can be a single flag or a
set of supported standards if for example the hardware can only
-distinguish between 50 and 60 Hz systems. When detection is not
+distinguish between 50 and 60 Hz systems. If no signal was detected,
+then the driver will return V4L2_STD_UNKNOWN. When detection is not
possible or fails, the set must contain all standards supported by the
current video input or output.</para>
diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroups/blkio-controller.txt
index da272c8..cd556b9 100644
--- a/Documentation/cgroups/blkio-controller.txt
+++ b/Documentation/cgroups/blkio-controller.txt
@@ -94,11 +94,13 @@
Hierarchical Cgroups
====================
-- Currently only CFQ supports hierarchical groups. For throttling,
- cgroup interface does allow creation of hierarchical cgroups and
- internally it treats them as flat hierarchy.
- If somebody created a hierarchy like as follows.
+Both CFQ and throttling implement hierarchy support; however,
+throttling's hierarchy support is enabled iff "sane_behavior" is
+enabled from cgroup side, which currently is a development option and
+not publicly available.
+
+If somebody created a hierarchy like as follows.
root
/ \
@@ -106,21 +108,20 @@
|
test3
- CFQ will handle the hierarchy correctly but and throttling will
- practically treat all groups at same level. For details on CFQ
- hierarchy support, refer to Documentation/block/cfq-iosched.txt.
- Throttling will treat the hierarchy as if it looks like the
- following.
+CFQ by default and throttling with "sane_behavior" will handle the
+hierarchy correctly. For details on CFQ hierarchy support, refer to
+Documentation/block/cfq-iosched.txt. For throttling, all limits apply
+to the whole subtree while all statistics are local to the IOs
+directly generated by tasks in that cgroup.
+
+Throttling without "sane_behavior" enabled from cgroup side will
+practically treat all groups at same level as if it looks like the
+following.
pivot
/ / \ \
root test1 test2 test3
- Nesting cgroups, while allowed, isn't officially supported and blkio
- genereates warning when cgroups nest. Once throttling implements
- hierarchy support, hierarchy will be supported and the warning will
- be removed.
-
Various user visible config options
===================================
CONFIG_BLK_CGROUP
diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt
index 18de785..7f773d5 100644
--- a/Documentation/coccinelle.txt
+++ b/Documentation/coccinelle.txt
@@ -6,15 +6,17 @@
Getting Coccinelle
~~~~~~~~~~~~~~~~~~~~
-The semantic patches included in the kernel use the 'virtual rule'
-feature which was introduced in Coccinelle version 0.1.11.
+The semantic patches included in the kernel use features and options
+which are provided by Coccinelle version 1.0.0-rc11 and above.
+Using earlier versions will fail as the option names used by
+the Coccinelle files and coccicheck have been updated.
-Coccinelle (>=0.2.0) is available through the package manager
+Coccinelle is available through the package manager
of many distributions, e.g. :
- - Debian (>=squeeze)
- - Fedora (>=13)
- - Ubuntu (>=10.04 Lucid Lynx)
+ - Debian
+ - Fedora
+ - Ubuntu
- OpenSUSE
- Arch Linux
- NetBSD
@@ -36,11 +38,6 @@
sudo make install
-The semantic patches in the kernel will work best with Coccinelle version
-0.2.4 or later. Using earlier versions may incur some parse errors in the
-semantic patch code, but any results that are obtained should still be
-correct.
-
Using Coccinelle on the Linux kernel
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -48,7 +45,7 @@
Makefile. This target is named 'coccicheck' and calls the 'coccicheck'
front-end in the 'scripts' directory.
-Four modes are defined: patch, report, context, and org. The mode to
+Four basic modes are defined: patch, report, context, and org. The mode to
use is specified by setting the MODE variable with 'MODE=<mode>'.
'patch' proposes a fix, when possible.
@@ -62,18 +59,24 @@
'org' generates a report in the Org mode format of Emacs.
Note that not all semantic patches implement all modes. For easy use
-of Coccinelle, the default mode is "chain" which tries the previous
-modes in the order above until one succeeds.
+of Coccinelle, the default mode is "report".
-To make a report for every semantic patch, run the following command:
+Two other modes provide some common combinations of these modes.
- make coccicheck MODE=report
+'chain' tries the previous modes in the order above until one succeeds.
-NB: The 'report' mode is the default one.
+'rep+ctxt' runs successively the report mode and the context mode.
+ It should be used with the C option (described later)
+ which checks the code on a file basis.
-To produce patches, run:
+Examples:
+ To make a report for every semantic patch, run the following command:
- make coccicheck MODE=patch
+ make coccicheck MODE=report
+
+ To produce patches, run:
+
+ make coccicheck MODE=patch
The coccicheck target applies every semantic patch available in the
@@ -91,6 +94,11 @@
make coccicheck MODE=report V=1
+By default, coccicheck tries to run as parallel as possible. To change
+the parallelism, set the J= variable. For example, to run across 4 CPUs:
+
+ make coccicheck MODE=report J=4
+
Using Coccinelle with a single semantic patch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -124,26 +132,33 @@
make C=2 CHECK="scripts/coccicheck"
+In these modes, which works on a file basis, there is no information
+about semantic patches displayed, and no commit message proposed.
+
This runs every semantic patch in scripts/coccinelle by default. The
COCCI variable may additionally be used to only apply a single
semantic patch as shown in the previous section.
-The "chain" mode is the default. You can select another one with the
+The "report" mode is the default. You can select another one with the
MODE variable explained above.
-In this mode, there is no information about semantic patches
-displayed, and no commit message proposed.
-
Additional flags
~~~~~~~~~~~~~~~~~~
Additional flags can be passed to spatch through the SPFLAGS
variable.
- make SPFLAGS=--use_glimpse coccicheck
+ make SPFLAGS=--use-glimpse coccicheck
+ make SPFLAGS=--use-idutils coccicheck
See spatch --help to learn more about spatch options.
+Note that the '--use-glimpse' and '--use-idutils' options
+require external tools for indexing the code. None of them is
+thus active by default. However, by indexing the code with
+one of these tools, and according to the cocci file used,
+spatch could proceed the entire code base more quickly.
+
Proposing new semantic patches
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/device-mapper/switch.txt b/Documentation/device-mapper/switch.txt
new file mode 100644
index 0000000..2fa7493
--- /dev/null
+++ b/Documentation/device-mapper/switch.txt
@@ -0,0 +1,126 @@
+dm-switch
+=========
+
+The device-mapper switch target creates a device that supports an
+arbitrary mapping of fixed-size regions of I/O across a fixed set of
+paths. The path used for any specific region can be switched
+dynamically by sending the target a message.
+
+It maps I/O to underlying block devices efficiently when there is a large
+number of fixed-sized address regions but there is no simple pattern
+that would allow for a compact representation of the mapping such as
+dm-stripe.
+
+Background
+----------
+
+Dell EqualLogic and some other iSCSI storage arrays use a distributed
+frameless architecture. In this architecture, the storage group
+consists of a number of distinct storage arrays ("members") each having
+independent controllers, disk storage and network adapters. When a LUN
+is created it is spread across multiple members. The details of the
+spreading are hidden from initiators connected to this storage system.
+The storage group exposes a single target discovery portal, no matter
+how many members are being used. When iSCSI sessions are created, each
+session is connected to an eth port on a single member. Data to a LUN
+can be sent on any iSCSI session, and if the blocks being accessed are
+stored on another member the I/O will be forwarded as required. This
+forwarding is invisible to the initiator. The storage layout is also
+dynamic, and the blocks stored on disk may be moved from member to
+member as needed to balance the load.
+
+This architecture simplifies the management and configuration of both
+the storage group and initiators. In a multipathing configuration, it
+is possible to set up multiple iSCSI sessions to use multiple network
+interfaces on both the host and target to take advantage of the
+increased network bandwidth. An initiator could use a simple round
+robin algorithm to send I/O across all paths and let the storage array
+members forward it as necessary, but there is a performance advantage to
+sending data directly to the correct member.
+
+A device-mapper table already lets you map different regions of a
+device onto different targets. However in this architecture the LUN is
+spread with an address region size on the order of 10s of MBs, which
+means the resulting table could have more than a million entries and
+consume far too much memory.
+
+Using this device-mapper switch target we can now build a two-layer
+device hierarchy:
+
+ Upper Tier – Determine which array member the I/O should be sent to.
+ Lower Tier – Load balance amongst paths to a particular member.
+
+The lower tier consists of a single dm multipath device for each member.
+Each of these multipath devices contains the set of paths directly to
+the array member in one priority group, and leverages existing path
+selectors to load balance amongst these paths. We also build a
+non-preferred priority group containing paths to other array members for
+failover reasons.
+
+The upper tier consists of a single dm-switch device. This device uses
+a bitmap to look up the location of the I/O and choose the appropriate
+lower tier device to route the I/O. By using a bitmap we are able to
+use 4 bits for each address range in a 16 member group (which is very
+large for us). This is a much denser representation than the dm table
+b-tree can achieve.
+
+Construction Parameters
+=======================
+
+ <num_paths> <region_size> <num_optional_args> [<optional_args>...]
+ [<dev_path> <offset>]+
+
+<num_paths>
+ The number of paths across which to distribute the I/O.
+
+<region_size>
+ The number of 512-byte sectors in a region. Each region can be redirected
+ to any of the available paths.
+
+<num_optional_args>
+ The number of optional arguments. Currently, no optional arguments
+ are supported and so this must be zero.
+
+<dev_path>
+ The block device that represents a specific path to the device.
+
+<offset>
+ The offset of the start of data on the specific <dev_path> (in units
+ of 512-byte sectors). This number is added to the sector number when
+ forwarding the request to the specific path. Typically it is zero.
+
+Messages
+========
+
+set_region_mappings <index>:<path_nr> [<index>]:<path_nr> [<index>]:<path_nr>...
+
+Modify the region table by specifying which regions are redirected to
+which paths.
+
+<index>
+ The region number (region size was specified in constructor parameters).
+ If index is omitted, the next region (previous index + 1) is used.
+ Expressed in hexadecimal (WITHOUT any prefix like 0x).
+
+<path_nr>
+ The path number in the range 0 ... (<num_paths> - 1).
+ Expressed in hexadecimal (WITHOUT any prefix like 0x).
+
+Status
+======
+
+No status line is reported.
+
+Example
+=======
+
+Assume that you have volumes vg1/switch0 vg1/switch1 vg1/switch2 with
+the same size.
+
+Create a switch device with 64kB region size:
+ dmsetup create switch --table "0 `blockdev --getsize /dev/vg1/switch0`
+ switch 3 128 0 /dev/vg1/switch0 0 /dev/vg1/switch1 0 /dev/vg1/switch2 0"
+
+Set mappings for the first 7 entries to point to devices switch0, switch1,
+switch2, switch0, switch1, switch2, switch1:
+ dmsetup message switch 0 set_region_mappings 0:0 :1 :2 :0 :1 :2 :1
diff --git a/Documentation/devicetree/bindings/arm/global_timer.txt b/Documentation/devicetree/bindings/arm/global_timer.txt
new file mode 100644
index 0000000..1e54898
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/global_timer.txt
@@ -0,0 +1,24 @@
+
+* ARM Global Timer
+ Cortex-A9 are often associated with a per-core Global timer.
+
+** Timer node required properties:
+
+- compatible : Should be "arm,cortex-a9-global-timer"
+ Driver supports versions r2p0 and above.
+
+- interrupts : One interrupt to each core
+
+- reg : Specify the base address and the size of the GT timer
+ register window.
+
+- clocks : Should be phandle to a clock.
+
+Example:
+
+ timer@2c000600 {
+ compatible = "arm,cortex-a9-global-timer";
+ reg = <0x2c000600 0x20>;
+ interrupts = <1 13 0xf01>;
+ clocks = <&arm_periph_clk>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/men-a021-wdt.txt b/Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
new file mode 100644
index 0000000..370dee3
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/men-a021-wdt.txt
@@ -0,0 +1,25 @@
+Bindings for MEN A21 Watchdog device connected to GPIO lines
+
+Required properties:
+- compatible: "men,a021-wdt"
+- gpios: Specifies the pins that control the Watchdog, order:
+ 1: Watchdog enable
+ 2: Watchdog fast-mode
+ 3: Watchdog trigger
+ 4: Watchdog reset cause bit 0
+ 5: Watchdog reset cause bit 1
+ 6: Watchdog reset cause bit 2
+
+Optional properties:
+- None
+
+Example:
+ watchdog {
+ compatible ="men,a021-wdt";
+ gpios = <&gpio3 9 1 /* WD_EN */
+ &gpio3 10 1 /* WD_FAST */
+ &gpio3 11 1 /* WD_TRIG */
+ &gpio3 6 1 /* RST_CAUSE[0] */
+ &gpio3 7 1 /* RST_CAUSE[1] */
+ &gpio3 8 1>; /* RST_CAUSE[2] */
+ };
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
new file mode 100644
index 0000000..e34c6cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -0,0 +1,70 @@
+* ARM System MMU Architecture Implementation
+
+ARM SoCs may contain an implementation of the ARM System Memory
+Management Unit Architecture, which can be used to provide 1 or 2 stages
+of address translation to bus masters external to the CPU.
+
+The SMMU may also raise interrupts in response to various fault
+conditions.
+
+** System MMU required properties:
+
+- compatible : Should be one of:
+
+ "arm,smmu-v1"
+ "arm,smmu-v2"
+ "arm,mmu-400"
+ "arm,mmu-500"
+
+ depending on the particular implementation and/or the
+ version of the architecture implemented.
+
+- reg : Base address and size of the SMMU.
+
+- #global-interrupts : The number of global interrupts exposed by the
+ device.
+
+- interrupts : Interrupt list, with the first #global-irqs entries
+ corresponding to the global interrupts and any
+ following entries corresponding to context interrupts,
+ specified in order of their indexing by the SMMU.
+
+ For SMMUv2 implementations, there must be exactly one
+ interrupt per context bank. In the case of a single,
+ combined interrupt, it must be listed multiple times.
+
+- mmu-masters : A list of phandles to device nodes representing bus
+ masters for which the SMMU can provide a translation
+ and their corresponding StreamIDs (see example below).
+ Each device node linked from this list must have a
+ "#stream-id-cells" property, indicating the number of
+ StreamIDs associated with it.
+
+** System MMU optional properties:
+
+- smmu-parent : When multiple SMMUs are chained together, this
+ property can be used to provide a phandle to the
+ parent SMMU (that is the next SMMU on the path going
+ from the mmu-masters towards memory) node for this
+ SMMU.
+
+Example:
+
+ smmu {
+ compatible = "arm,smmu-v1";
+ reg = <0xba5e0000 0x10000>;
+ #global-interrupts = <2>;
+ interrupts = <0 32 4>,
+ <0 33 4>,
+ <0 34 4>, /* This is the first context interrupt */
+ <0 35 4>,
+ <0 36 4>,
+ <0 37 4>;
+
+ /*
+ * Two DMA controllers, the first with two StreamIDs (0xd01d
+ * and 0xd01e) and the second with only one (0xd11c).
+ */
+ mmu-masters = <&dma0 0xd01d 0xd01e>,
+ <&dma1 0xd11c>;
+ };
diff --git a/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt b/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt
index de9f6b7..0bf6fb7 100644
--- a/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt
+++ b/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt
@@ -2,8 +2,10 @@
Required properties:
-- compatible : should be "samsung,exynos4212-fimc-lite" for Exynos4212 and
- Exynos4412 SoCs;
+- compatible : should be one of:
+ "samsung,exynos4212-fimc-lite" for Exynos4212/4412 SoCs,
+ "samsung,exynos5250-fimc-lite" for Exynos5250 compatible
+ devices;
- reg : physical base address and size of the device memory mapped
registers;
- interrupts : should contain FIMC-LITE interrupt;
diff --git a/Documentation/devicetree/bindings/media/i2c/mt9p031.txt b/Documentation/devicetree/bindings/media/i2c/mt9p031.txt
new file mode 100644
index 0000000..cb60443
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/mt9p031.txt
@@ -0,0 +1,40 @@
+* Aptina 1/2.5-Inch 5Mp CMOS Digital Image Sensor
+
+The Aptina MT9P031 is a 1/2.5-inch CMOS active pixel digital image sensor with
+an active array size of 2592H x 1944V. It is programmable through a simple
+two-wire serial interface.
+
+Required Properties:
+- compatible: value should be either one among the following
+ (a) "aptina,mt9p031" for mt9p031 sensor
+ (b) "aptina,mt9p031m" for mt9p031m sensor
+
+- input-clock-frequency: Input clock frequency.
+
+- pixel-clock-frequency: Pixel clock frequency.
+
+Optional Properties:
+- reset-gpios: Chip reset GPIO
+
+For further reading on port node refer to
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+
+ i2c0@1c22000 {
+ ...
+ ...
+ mt9p031@5d {
+ compatible = "aptina,mt9p031";
+ reg = <0x5d>;
+ reset-gpios = <&gpio3 30 0>;
+
+ port {
+ mt9p031_1: endpoint {
+ input-clock-frequency = <6000000>;
+ pixel-clock-frequency = <96000000>;
+ };
+ };
+ };
+ ...
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/tvp514x.txt b/Documentation/devicetree/bindings/media/i2c/tvp514x.txt
new file mode 100644
index 0000000..46752cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/tvp514x.txt
@@ -0,0 +1,44 @@
+* Texas Instruments TVP514x video decoder
+
+The TVP5146/TVP5146m2/TVP5147/TVP5147m1 device is high quality, single-chip
+digital video decoder that digitizes and decodes all popular baseband analog
+video formats into digital video component. The tvp514x decoder supports analog-
+to-digital (A/D) conversion of component RGB and YPbPr signals as well as A/D
+conversion and decoding of NTSC, PAL and SECAM composite and S-video into
+component YCbCr.
+
+Required Properties :
+- compatible : value should be either one among the following
+ (a) "ti,tvp5146" for tvp5146 decoder.
+ (b) "ti,tvp5146m2" for tvp5146m2 decoder.
+ (c) "ti,tvp5147" for tvp5147 decoder.
+ (d) "ti,tvp5147m1" for tvp5147m1 decoder.
+
+- hsync-active: HSYNC Polarity configuration for endpoint.
+
+- vsync-active: VSYNC Polarity configuration for endpoint.
+
+- pclk-sample: Clock polarity of the endpoint.
+
+For further reading on port node refer to Documentation/devicetree/bindings/
+media/video-interfaces.txt.
+
+Example:
+
+ i2c0@1c22000 {
+ ...
+ ...
+ tvp514x@5c {
+ compatible = "ti,tvp5146";
+ reg = <0x5c>;
+
+ port {
+ tvp514x_1: endpoint {
+ hsync-active = <1>;
+ vsync-active = <1>;
+ pclk-sample = <0>;
+ };
+ };
+ };
+ ...
+ };
diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 51c776b..96312f6 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -127,22 +127,22 @@
};
};
};
- };
- /* MIPI CSI-2 bus IF sensor */
- s5c73m3: sensor@0x1a {
- compatible = "samsung,s5c73m3";
- reg = <0x1a>;
- vddio-supply = <...>;
+ /* MIPI CSI-2 bus IF sensor */
+ s5c73m3: sensor@0x1a {
+ compatible = "samsung,s5c73m3";
+ reg = <0x1a>;
+ vddio-supply = <...>;
- clock-frequency = <24000000>;
- clocks = <...>;
- clock-names = "mclk";
+ clock-frequency = <24000000>;
+ clocks = <...>;
+ clock-names = "mclk";
- port {
- s5c73m3_1: endpoint {
- data-lanes = <1 2 3 4>;
- remote-endpoint = <&csis0_ep>;
+ port {
+ s5c73m3_1: endpoint {
+ data-lanes = <1 2 3 4>;
+ remote-endpoint = <&csis0_ep>;
+ };
};
};
};
diff --git a/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt b/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt
index 5f8e28e..be45f0b 100644
--- a/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt
+++ b/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt
@@ -5,8 +5,8 @@
- compatible : "samsung,s5pv210-csis" for S5PV210 (S5PC110),
"samsung,exynos4210-csis" for Exynos4210 (S5PC210),
- "samsung,exynos4212-csis" for Exynos4212/Exynos4412
- SoC series;
+ "samsung,exynos4212-csis" for Exynos4212/Exynos4412,
+ "samsung,exynos5250-csis" for Exynos5250;
- reg : offset and length of the register set for the device;
- interrupts : should contain MIPI CSIS interrupt; the format of the
interrupt specifier depends on the interrupt controller;
diff --git a/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt b/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
new file mode 100644
index 0000000..1ce4e46
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sh_mobile_ceu.txt
@@ -0,0 +1,18 @@
+Bindings, specific for the sh_mobile_ceu_camera.c driver:
+ - compatible: Should be "renesas,sh-mobile-ceu"
+ - reg: register base and size
+ - interrupts: the interrupt number
+ - interrupt-parent: the interrupt controller
+ - renesas,max-width: maximum image width, supported on this SoC
+ - renesas,max-height: maximum image height, supported on this SoC
+
+Example:
+
+ceu0: ceu@0xfe910000 {
+ compatible = "renesas,sh-mobile-ceu";
+ reg = <0xfe910000 0xa0>;
+ interrupt-parent = <&intcs>;
+ interrupts = <0x880>;
+ renesas,max-width = <8188>;
+ renesas,max-height = <8188>;
+};
diff --git a/drivers/staging/ti-soc-thermal/ti_soc_thermal.txt b/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt
similarity index 78%
rename from drivers/staging/ti-soc-thermal/ti_soc_thermal.txt
rename to Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt
index 1629652..0c9222d 100644
--- a/drivers/staging/ti-soc-thermal/ti_soc_thermal.txt
+++ b/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt
@@ -17,8 +17,9 @@
- interrupts : this entry should indicate which interrupt line
the talert signal is routed to;
Specific:
-- ti,tshut-gpio : this entry should be used to inform which GPIO
-line the tshut signal is routed to;
+- gpios : this entry should be used to inform which GPIO
+line the tshut signal is routed to. The informed GPIO will
+be treated as an IRQ;
- regs : this entry must also be specified and it is specific
to each bandgap version, because the mapping may change from
soc to soc, apart of depending on available features.
@@ -37,7 +38,7 @@
0x4a002378 0x18>;
compatible = "ti,omap4460-bandgap";
interrupts = <0 126 4>; /* talert */
- ti,tshut-gpio = <86>;
+ gpios = <&gpio3 22 0>; /* tshut */
};
OMAP4470:
@@ -47,7 +48,7 @@
0x4a002378 0x18>;
compatible = "ti,omap4470-bandgap";
interrupts = <0 126 4>; /* talert */
- ti,tshut-gpio = <86>;
+ gpios = <&gpio3 22 0>; /* tshut */
};
OMAP5430:
@@ -59,3 +60,15 @@
compatible = "ti,omap5430-bandgap";
interrupts = <0 126 4>; /* talert */
};
+
+DRA752:
+bandgap {
+ reg = <0x4a0021e0 0xc
+ 0x4a00232c 0xc
+ 0x4a002380 0x2c
+ 0x4a0023C0 0x3c
+ 0x4a002564 0x8
+ 0x4a002574 0x50>;
+ compatible = "ti,dra752-bandgap";
+ interrupts = <0 126 4>; /* talert */
+};
diff --git a/Documentation/devicetree/bindings/timer/marvell,orion-timer.txt b/Documentation/devicetree/bindings/timer/marvell,orion-timer.txt
new file mode 100644
index 0000000..62bb826
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/marvell,orion-timer.txt
@@ -0,0 +1,17 @@
+Marvell Orion SoC timer
+
+Required properties:
+- compatible: shall be "marvell,orion-timer"
+- reg: base address of the timer register starting with TIMERS CONTROL register
+- interrupt-parent: phandle of the bridge interrupt controller
+- interrupts: should contain the interrupts for Timer0 and Timer1
+- clocks: phandle of timer reference clock (tclk)
+
+Example:
+ timer: timer {
+ compatible = "marvell,orion-timer";
+ reg = <0x20300 0x20>;
+ interrupt-parent = <&bridge_intc>;
+ interrupts = <1>, <2>;
+ clocks = <&core_clk 0>;
+ };
diff --git a/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt b/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt
index d209366..f801d71 100644
--- a/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt
+++ b/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt
@@ -5,9 +5,14 @@
- compatible : should be "brcm,bcm2835-pm-wdt"
- reg : Specifies base physical address and size of the registers.
+Optional properties:
+
+- timeout-sec : Contains the watchdog timeout in seconds
+
Example:
watchdog {
compatible = "brcm,bcm2835-pm-wdt";
reg = <0x7e100000 0x28>;
+ timeout-sec = <10>;
};
diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt
index 83577f0..12525b1 100644
--- a/Documentation/filesystems/xfs.txt
+++ b/Documentation/filesystems/xfs.txt
@@ -18,6 +18,8 @@
=============
When mounting an XFS filesystem, the following options are accepted.
+For boolean mount options, the names with the (*) suffix is the
+default behaviour.
allocsize=size
Sets the buffered I/O end-of-file preallocation size when
@@ -25,97 +27,128 @@
Valid values for this option are page size (typically 4KiB)
through to 1GiB, inclusive, in power-of-2 increments.
- attr2/noattr2
- The options enable/disable (default is disabled for backward
- compatibility on-disk) an "opportunistic" improvement to be
- made in the way inline extended attributes are stored on-disk.
- When the new form is used for the first time (by setting or
- removing extended attributes) the on-disk superblock feature
- bit field will be updated to reflect this format being in use.
+ The default behaviour is for dynamic end-of-file
+ preallocation size, which uses a set of heuristics to
+ optimise the preallocation size based on the current
+ allocation patterns within the file and the access patterns
+ to the file. Specifying a fixed allocsize value turns off
+ the dynamic behaviour.
+
+ attr2
+ noattr2
+ The options enable/disable an "opportunistic" improvement to
+ be made in the way inline extended attributes are stored
+ on-disk. When the new form is used for the first time when
+ attr2 is selected (either when setting or removing extended
+ attributes) the on-disk superblock feature bit field will be
+ updated to reflect this format being in use.
+
+ The default behaviour is determined by the on-disk feature
+ bit indicating that attr2 behaviour is active. If either
+ mount option it set, then that becomes the new default used
+ by the filesystem.
CRC enabled filesystems always use the attr2 format, and so
will reject the noattr2 mount option if it is set.
- barrier
- Enables the use of block layer write barriers for writes into
- the journal and unwritten extent conversion. This allows for
- drive level write caching to be enabled, for devices that
- support write barriers.
+ barrier (*)
+ nobarrier
+ Enables/disables the use of block layer write barriers for
+ writes into the journal and for data integrity operations.
+ This allows for 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.
+ nodiscard (*)
+ Enable/disable the issuing of commands 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.
- dmapi
- Enable the DMAPI (Data Management API) event callouts.
- Use with the "mtpt" option.
+ Note: It is currently recommended that you use the fstrim
+ application to discard unused blocks rather than the discard
+ mount option because the performance impact of this option
+ is quite severe.
- grpid/bsdgroups and nogrpid/sysvgroups
- These options define what group ID a newly created file gets.
- When grpid is set, it takes the group ID of the directory in
- which it is created; otherwise (the default) it takes the fsgid
- of the current process, unless the directory has the setgid bit
- set, in which case it takes the gid from the parent directory,
- and also gets the setgid bit set if it is a directory itself.
+ grpid/bsdgroups
+ nogrpid/sysvgroups (*)
+ These options define what group ID a newly created file
+ gets. When grpid is set, it takes the group ID of the
+ directory in which it is created; otherwise it takes the
+ fsgid of the current process, unless the directory has the
+ setgid bit set, in which case it takes the gid from the
+ parent directory, and also gets the setgid bit set if it is
+ a directory itself.
- ihashsize=value
- In memory inode hashes have been removed, so this option has
- no function as of August 2007. Option is deprecated.
+ filestreams
+ Make the data allocator use the filestreams allocation mode
+ across the entire filesystem rather than just on directories
+ configured to use it.
- ikeep/noikeep
- When ikeep is specified, XFS does not delete empty inode clusters
- and keeps them around on disk. ikeep is the traditional XFS
- behaviour. When noikeep is specified, empty inode clusters
- are returned to the free space pool. The default is noikeep for
- non-DMAPI mounts, while ikeep is the default when DMAPI is in use.
-
- inode64
- Indicates that XFS is allowed to create inodes at any location
- in the filesystem, including those which will result in inode
- numbers occupying more than 32 bits of significance. This is
- the default allocation option. Applications which do not handle
- inode numbers bigger than 32 bits, should use inode32 option.
+ ikeep
+ noikeep (*)
+ When ikeep is specified, XFS does not delete empty inode
+ clusters and keeps them around on disk. When noikeep is
+ specified, empty inode clusters are returned to the free
+ space pool.
inode32
- Indicates that XFS is limited to create inodes at locations which
- will not result in inode numbers with more than 32 bits of
- significance. This is provided for backwards compatibility, since
- 64 bits inode numbers might cause problems for some applications
- that cannot handle large inode numbers.
+ inode64 (*)
+ When inode32 is specified, it indicates that XFS limits
+ inode creation to locations which will not result in inode
+ numbers with more than 32 bits of significance.
- largeio/nolargeio
+ When inode64 is specified, it indicates that XFS is allowed
+ to create inodes at any location in the filesystem,
+ including those which will result in inode numbers occupying
+ more than 32 bits of significance.
+
+ inode32 is provided for backwards compatibility with older
+ systems and applications, since 64 bits inode numbers might
+ cause problems for some applications that cannot handle
+ large inode numbers. If applications are in use which do
+ not handle inode numbers bigger than 32 bits, the inode32
+ option should be specified.
+
+
+ largeio
+ nolargeio (*)
If "nolargeio" is specified, the optimal I/O reported in
- st_blksize by stat(2) will be as small as possible to allow user
- applications to avoid inefficient read/modify/write I/O.
- If "largeio" specified, a filesystem that has a "swidth" specified
- will return the "swidth" value (in bytes) in st_blksize. If the
- filesystem does not have a "swidth" specified but does specify
- an "allocsize" then "allocsize" (in bytes) will be returned
- instead.
- If neither of these two options are specified, then filesystem
- will behave as if "nolargeio" was specified.
+ st_blksize by stat(2) will be as small as possible to allow
+ user applications to avoid inefficient read/modify/write
+ I/O. This is typically the page size of the machine, as
+ this is the granularity of the page cache.
+
+ If "largeio" specified, a filesystem that was created with a
+ "swidth" specified will return the "swidth" value (in bytes)
+ in st_blksize. If the filesystem does not have a "swidth"
+ specified but does specify an "allocsize" then "allocsize"
+ (in bytes) will be returned instead. Otherwise the behaviour
+ is the same as if "nolargeio" was specified.
logbufs=value
- Set the number of in-memory log buffers. Valid numbers range
- from 2-8 inclusive.
- The default value is 8 buffers for filesystems with a
- blocksize of 64KiB, 4 buffers for filesystems with a blocksize
- of 32KiB, 3 buffers for filesystems with a blocksize of 16KiB
- and 2 buffers for all other configurations. Increasing the
- number of buffers may increase performance on some workloads
- at the cost of the memory used for the additional log buffers
- and their associated control structures.
+ Set the number of in-memory log buffers. Valid numbers
+ range from 2-8 inclusive.
+
+ The default value is 8 buffers.
+
+ If the memory cost of 8 log buffers is too high on small
+ systems, then it may be reduced at some cost to performance
+ on metadata intensive workloads. The logbsize option below
+ controls the size of each buffer and so is also relevent to
+ this case.
logbsize=value
- Set the size of each in-memory log buffer.
- Size may be specified in bytes, or in kilobytes with a "k" suffix.
- Valid sizes for version 1 and version 2 logs are 16384 (16k) and
- 32768 (32k). Valid sizes for version 2 logs also include
- 65536 (64k), 131072 (128k) and 262144 (256k).
- The default value for machines with more than 32MiB of memory
- is 32768, machines with less memory use 16384 by default.
+ Set the size of each in-memory log buffer. The size may be
+ specified in bytes, or in kilobytes with a "k" suffix.
+ Valid sizes for version 1 and version 2 logs are 16384 (16k)
+ and 32768 (32k). Valid sizes for version 2 logs also
+ include 65536 (64k), 131072 (128k) and 262144 (256k). The
+ logbsize must be an integer multiple of the log
+ stripe unit configured at mkfs time.
+
+ The default value for for version 1 logs is 32768, while the
+ default value for version 2 logs is MAX(32768, log_sunit).
logdev=device and rtdev=device
Use an external log (metadata journal) and/or real-time device.
@@ -124,16 +157,11 @@
optional, and the log section can be separate from the data
section or contained within it.
- mtpt=mountpoint
- Use with the "dmapi" option. The value specified here will be
- included in the DMAPI mount event, and should be the path of
- the actual mountpoint that is used.
-
noalign
- Data allocations will not be aligned at stripe unit boundaries.
-
- noatime
- Access timestamps are not updated when a file is read.
+ Data allocations will not be aligned at stripe unit
+ boundaries. This is only relevant to filesystems created
+ with non-zero data alignment parameters (sunit, swidth) by
+ mkfs.
norecovery
The filesystem will be mounted without running log recovery.
@@ -144,8 +172,14 @@
the mount will fail.
nouuid
- Don't check for double mounted file systems using the file system uuid.
- This is useful to mount LVM snapshot volumes.
+ Don't check for double mounted file systems using the file
+ system uuid. This is useful to mount LVM snapshot volumes,
+ and often used in combination with "norecovery" for mounting
+ read-only snapshots.
+
+ noquota
+ Forcibly turns off all quota accounting and enforcement
+ within the filesystem.
uquota/usrquota/uqnoenforce/quota
User disk quota accounting enabled, and limits (optionally)
@@ -160,24 +194,64 @@
enforced. Refer to xfs_quota(8) for further details.
sunit=value and swidth=value
- Used to specify the stripe unit and width for a RAID device or
- a stripe volume. "value" must be specified in 512-byte block
- units.
- If this option is not specified and the filesystem was made on
- a stripe volume or the stripe width or unit were specified for
- the RAID device at mkfs time, then the mount system call will
- restore the value from the superblock. For filesystems that
- are made directly on RAID devices, these options can be used
- to override the information in the superblock if the underlying
- disk layout changes after the filesystem has been created.
- The "swidth" option is required if the "sunit" option has been
- specified, and must be a multiple of the "sunit" value.
+ Used to specify the stripe unit and width for a RAID device
+ or a stripe volume. "value" must be specified in 512-byte
+ block units. These options are only relevant to filesystems
+ that were created with non-zero data alignment parameters.
+
+ The sunit and swidth parameters specified must be compatible
+ with the existing filesystem alignment characteristics. In
+ general, that means the only valid changes to sunit are
+ increasing it by a power-of-2 multiple. Valid swidth values
+ are any integer multiple of a valid sunit value.
+
+ Typically the only time these mount options are necessary if
+ after an underlying RAID device has had it's geometry
+ modified, such as adding a new disk to a RAID5 lun and
+ reshaping it.
swalloc
Data allocations will be rounded up to stripe width boundaries
when the current end of file is being extended and the file
size is larger than the stripe width size.
+ wsync
+ When specified, all filesystem namespace operations are
+ executed synchronously. This ensures that when the namespace
+ operation (create, unlink, etc) completes, the change to the
+ namespace is on stable storage. This is useful in HA setups
+ where failover must not result in clients seeing
+ inconsistent namespace presentation during or after a
+ failover event.
+
+
+Deprecated Mount Options
+========================
+
+ delaylog/nodelaylog
+ Delayed logging is the only logging method that XFS supports
+ now, so these mount options are now ignored.
+
+ Due for removal in 3.12.
+
+ ihashsize=value
+ In memory inode hashes have been removed, so this option has
+ no function as of August 2007. Option is deprecated.
+
+ Due for removal in 3.12.
+
+ irixsgid
+ This behaviour is now controlled by a sysctl, so the mount
+ option is ignored.
+
+ Due for removal in 3.12.
+
+ osyncisdsync
+ osyncisosync
+ O_SYNC and O_DSYNC are fully supported, so there is no need
+ for these options any more.
+
+ Due for removal in 3.12.
sysctls
=======
@@ -189,15 +263,20 @@
in /proc/fs/xfs/stat. It then immediately resets to "0".
fs.xfs.xfssyncd_centisecs (Min: 100 Default: 3000 Max: 720000)
- The interval at which the xfssyncd thread flushes metadata
- out to disk. This thread will flush log activity out, and
- do some processing on unlinked inodes.
+ The interval at which the filesystem flushes metadata
+ out to disk and runs internal cache cleanup routines.
- fs.xfs.xfsbufd_centisecs (Min: 50 Default: 100 Max: 3000)
- The interval at which xfsbufd scans the dirty metadata buffers list.
+ fs.xfs.filestream_centisecs (Min: 1 Default: 3000 Max: 360000)
+ The interval at which the filesystem ages filestreams cache
+ references and returns timed-out AGs back to the free stream
+ pool.
- fs.xfs.age_buffer_centisecs (Min: 100 Default: 1500 Max: 720000)
- The age at which xfsbufd flushes dirty metadata buffers to disk.
+ fs.xfs.speculative_prealloc_lifetime
+ (Units: seconds Min: 1 Default: 300 Max: 86400)
+ The interval at which the background scanning for inodes
+ with unused speculative preallocation runs. The scan
+ removes unused preallocation from clean inodes and releases
+ the unused space back to the free pool.
fs.xfs.error_level (Min: 0 Default: 3 Max: 11)
A volume knob for error reporting when internal errors occur.
@@ -254,9 +333,31 @@
by the xfs_io(8) chattr command on a directory to be
inherited by files in that directory.
+ fs.xfs.inherit_nodefrag (Min: 0 Default: 1 Max: 1)
+ Setting this to "1" will cause the "nodefrag" flag set
+ by the xfs_io(8) chattr command on a directory to be
+ inherited by files in that directory.
+
fs.xfs.rotorstep (Min: 1 Default: 1 Max: 256)
In "inode32" allocation mode, this option determines how many
files the allocator attempts to allocate in the same allocation
group before moving to the next allocation group. The intent
is to control the rate at which the allocator moves between
allocation groups when allocating extents for new files.
+
+Deprecated Sysctls
+==================
+
+ fs.xfs.xfsbufd_centisecs (Min: 50 Default: 100 Max: 3000)
+ Dirty metadata is now tracked by the log subsystem and
+ flushing is driven by log space and idling demands. The
+ xfsbufd no longer exists, so this syctl does nothing.
+
+ Due for removal in 3.14.
+
+ fs.xfs.age_buffer_centisecs (Min: 100 Default: 1500 Max: 720000)
+ Dirty metadata is now tracked by the log subsystem and
+ flushing is driven by log space and idling demands. The
+ xfsbufd no longer exists, so this syctl does nothing.
+
+ Due for removal in 3.14.
diff --git a/Documentation/kbuild/kconfig.txt b/Documentation/kbuild/kconfig.txt
index 213859e..e349f29 100644
--- a/Documentation/kbuild/kconfig.txt
+++ b/Documentation/kbuild/kconfig.txt
@@ -174,6 +174,19 @@
/^hotplug
+ When searching, symbols are sorted thus:
+ - exact match first: an exact match is when the search matches
+ the complete symbol name;
+ - alphabetical order: when two symbols do not match exactly,
+ they are sorted in alphabetical order (in the user's current
+ locale).
+ For example: ^ATH.K matches:
+ ATH5K ATH9K ATH5K_AHB ATH5K_DEBUG [...] ATH6KL ATH6KL_DEBUG
+ [...] ATH9K_AHB ATH9K_BTCOEX_SUPPORT ATH9K_COMMON [...]
+ of which only ATH5K and ATH9K match exactly and so are sorted
+ first (and in alphabetical order), then come all other symbols,
+ sorted in alphabetical order.
+
______________________________________________________________________
User interface options for 'menuconfig'
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 75236f1..15356ac 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3081,6 +3081,19 @@
See also Documentation/trace/ftrace.txt "trace options"
section.
+ traceoff_on_warning
+ [FTRACE] enable this option to disable tracing when a
+ warning is hit. This turns off "tracing_on". Tracing can
+ be enabled again by echoing '1' into the "tracing_on"
+ file located in /sys/kernel/debug/tracing/
+
+ This option is useful, as it disables the trace before
+ the WARNING dump is called, which prevents the trace to
+ be filled with content caused by the warning output.
+
+ This option can also be set at run time via the sysctl
+ option: kernel/traceoff_on_warning
+
transparent_hugepage=
[KNL]
Format: [always|madvise|never]
diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt
index eeced24..f552a75 100644
--- a/Documentation/media-framework.txt
+++ b/Documentation/media-framework.txt
@@ -265,7 +265,7 @@
media_entity_find_link(struct media_pad *source,
struct media_pad *sink);
- media_entity_remote_source(struct media_pad *pad);
+ media_entity_remote_pad(struct media_pad *pad);
Refer to the kerneldoc documentation for more information.
diff --git a/Documentation/thermal/x86_pkg_temperature_thermal b/Documentation/thermal/x86_pkg_temperature_thermal
new file mode 100644
index 0000000..17a3a4c
--- /dev/null
+++ b/Documentation/thermal/x86_pkg_temperature_thermal
@@ -0,0 +1,47 @@
+Kernel driver: x86_pkg_temp_thermal
+===================
+
+Supported chips:
+* x86: with package level thermal management
+(Verify using: CPUID.06H:EAX[bit 6] =1)
+
+Authors: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+
+Reference
+---
+Intel® 64 and IA-32 Architectures Software Developer’s Manual (Jan, 2013):
+Chapter 14.6: PACKAGE LEVEL THERMAL MANAGEMENT
+
+Description
+---------
+
+This driver register CPU digital temperature package level sensor as a thermal
+zone with maximum two user mode configurable trip points. Number of trip points
+depends on the capability of the package. Once the trip point is violated,
+user mode can receive notification via thermal notification mechanism and can
+take any action to control temperature.
+
+
+Threshold management
+--------------------
+Each package will register as a thermal zone under /sys/class/thermal.
+Example:
+/sys/class/thermal/thermal_zone1
+
+This contains two trip points:
+- trip_point_0_temp
+- trip_point_1_temp
+
+User can set any temperature between 0 to TJ-Max temperature. Temperature units
+are in milli-degree Celsius. Refer to "Documentation/thermal/sysfs-api.txt" for
+thermal sys-fs details.
+
+Any value other than 0 in these trip points, can trigger thermal notifications.
+Setting 0, stops sending thermal notifications.
+
+Thermal notifications: To get kobject-uevent notifications, set the thermal zone
+policy to "user_space". For example: echo -n "user_space" > policy
+
+
+
+
diff --git a/Documentation/trace/events.txt b/Documentation/trace/events.txt
index bb24c2a..37732a2 100644
--- a/Documentation/trace/events.txt
+++ b/Documentation/trace/events.txt
@@ -183,13 +183,22 @@
The operators available for numeric fields are:
-==, !=, <, <=, >, >=
+==, !=, <, <=, >, >=, &
And for string fields they are:
-==, !=
+==, !=, ~
-Currently, only exact string matches are supported.
+The glob (~) only accepts a wild card character (*) at the start and or
+end of the string. For example:
+
+ prev_comm ~ "*sh"
+ prev_comm ~ "sh*"
+ prev_comm ~ "*sh*"
+
+But does not allow for it to be within the string:
+
+ prev_comm ~ "ba*sh" <-- is invalid
5.2 Setting filters
-------------------
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
index bfe8c29..b937c6e 100644
--- a/Documentation/trace/ftrace.txt
+++ b/Documentation/trace/ftrace.txt
@@ -2430,6 +2430,19 @@
echo '!schedule:disable_event:sched:sched_switch' > \
set_ftrace_filter
+- dump
+ When the function is hit, it will dump the contents of the ftrace
+ ring buffer to the console. This is useful if you need to debug
+ something, and want to dump the trace when a certain function
+ is hit. Perhaps its a function that is called before a tripple
+ fault happens and does not allow you to get a regular dump.
+
+- cpudump
+ When the function is hit, it will dump the contents of the ftrace
+ ring buffer for the current CPU to the console. Unlike the "dump"
+ command, it only prints out the contents of the ring buffer for the
+ CPU that executed the function that triggered the dump.
+
trace_pipe
----------
diff --git a/Documentation/vfio.txt b/Documentation/vfio.txt
index c55533c..d7993dc 100644
--- a/Documentation/vfio.txt
+++ b/Documentation/vfio.txt
@@ -172,12 +172,12 @@
struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
/* Create a new container */
- container = open("/dev/vfio/vfio, O_RDWR);
+ container = open("/dev/vfio/vfio", O_RDWR);
if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
/* Unknown API version */
- if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_X86_IOMMU))
+ if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU))
/* Doesn't support the IOMMU driver we want. */
/* Open the group */
@@ -193,7 +193,7 @@
ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
/* Enable the IOMMU model we want */
- ioctl(container, VFIO_SET_IOMMU, VFIO_X86_IOMMU)
+ ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU)
/* Get addition IOMMU info */
ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv
index 581f666..f144750 100644
--- a/Documentation/video4linux/CARDLIST.bttv
+++ b/Documentation/video4linux/CARDLIST.bttv
@@ -160,3 +160,6 @@
159 -> ProVideo PV183 [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540]
160 -> Tongwei Video Technology TD-3116 [f200:3116]
161 -> Aposonic W-DVR [0279:0228]
+162 -> Adlink MPG24
+163 -> Bt848 Capture 14MHz
+164 -> CyberVision CV06 (SV)
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index b3ad683..8df17d0 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -190,3 +190,4 @@
189 -> Kworld PC150-U [17de:a134]
190 -> Asus My Cinema PS3-100 [1043:48cd]
191 -> Hawell HW-9004V1
+192 -> AverMedia AverTV Satellite Hybrid+FM A706 [1461:2055]
diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner
index 5b83a3f..ac88621 100644
--- a/Documentation/video4linux/CARDLIST.tuner
+++ b/Documentation/video4linux/CARDLIST.tuner
@@ -86,6 +86,6 @@
tuner=86 - Tena TNF5337 MFD
tuner=87 - Xceive 4000 tuner
tuner=88 - Xceive 5000C tuner
-tuner=89 - Sony PAL+SECAM (BTF-PG472Z)
-tuner=90 - Sony NTSC-M-JP (BTF-PK467Z)
-tuner=91 - Sony NTSC-M (BTF-PB463Z)
+tuner=89 - Sony BTF-PG472Z PAL/SECAM
+tuner=90 - Sony BTF-PK467Z NTSC-M-JP
+tuner=91 - Sony BTF-PB463Z NTSC-M
diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt
index 25f4d34..e51f1b5 100644
--- a/Documentation/video4linux/fimc.txt
+++ b/Documentation/video4linux/fimc.txt
@@ -1,6 +1,6 @@
Samsung S5P/EXYNOS4 FIMC driver
-Copyright (C) 2012 Samsung Electronics Co., Ltd.
+Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
---------------------------------------------------------------------------
The FIMC (Fully Interactive Mobile Camera) device available in Samsung
@@ -10,7 +10,7 @@
path. There are multiple FIMC instances in the SoCs (up to 4), having
slightly different capabilities, like pixel alignment constraints, rotator
availability, LCD writeback support, etc. The driver is located at
-drivers/media/platform/s5p-fimc directory.
+drivers/media/platform/exynos4-is directory.
1. Supported SoCs
=================
@@ -36,21 +36,21 @@
=====================
- media device driver
- drivers/media/platform/s5p-fimc/fimc-mdevice.[ch]
+ drivers/media/platform/exynos4-is/media-dev.[ch]
- camera capture video device driver
- drivers/media/platform/s5p-fimc/fimc-capture.c
+ drivers/media/platform/exynos4-is/fimc-capture.c
- MIPI-CSI2 receiver subdev
- drivers/media/platform/s5p-fimc/mipi-csis.[ch]
+ drivers/media/platform/exynos4-is/mipi-csis.[ch]
- video post-processor (mem-to-mem)
- drivers/media/platform/s5p-fimc/fimc-core.c
+ drivers/media/platform/exynos4-is/fimc-core.c
- common files
- drivers/media/platform/s5p-fimc/fimc-core.h
- drivers/media/platform/s5p-fimc/fimc-reg.h
- drivers/media/platform/s5p-fimc/regs-fimc.h
+ drivers/media/platform/exynos4-is/fimc-core.h
+ drivers/media/platform/exynos4-is/fimc-reg.h
+ drivers/media/platform/exynos4-is/regs-fimc.h
4. User space interfaces
========================
@@ -143,7 +143,8 @@
6. Platform support
===================
-The machine code (plat-s5p and arch/arm/mach-*) must select following options
+The machine code (arch/arm/plat-samsung and arch/arm/mach-*) must select
+following options:
CONFIG_S5P_DEV_FIMC0 mandatory
CONFIG_S5P_DEV_FIMC1 \
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index a300b28..6c4866b 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -246,7 +246,6 @@
It looks like this:
struct v4l2_subdev_core_ops {
- int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*init)(struct v4l2_subdev *sd, u32 val);
...
@@ -326,8 +325,27 @@
sink of the link. Subdev drivers are also free to use this function to
perform the checks mentioned above in addition to their own checks.
-A device (bridge) driver needs to register the v4l2_subdev with the
-v4l2_device:
+There are currently two ways to register subdevices with the V4L2 core. The
+first (traditional) possibility is to have subdevices registered by bridge
+drivers. This can be done when the bridge driver has the complete information
+about subdevices connected to it and knows exactly when to register them. This
+is typically the case for internal subdevices, like video data processing units
+within SoCs or complex PCI(e) boards, camera sensors in USB cameras or connected
+to SoCs, which pass information about them to bridge drivers, usually in their
+platform data.
+
+There are however also situations where subdevices have to be registered
+asynchronously to bridge devices. An example of such a configuration is a Device
+Tree based system where information about subdevices is made available to the
+system independently from the bridge devices, e.g. when subdevices are defined
+in DT as I2C device nodes. The API used in this second case is described further
+below.
+
+Using one or the other registration method only affects the probing process, the
+run-time bridge-subdevice interaction is in both cases the same.
+
+In the synchronous case a device (bridge) driver needs to register the
+v4l2_subdev with the v4l2_device:
int err = v4l2_device_register_subdev(v4l2_dev, sd);
@@ -346,24 +364,24 @@
You can call an ops function either directly:
- err = sd->ops->core->g_chip_ident(sd, &chip);
+ err = sd->ops->core->g_std(sd, &norm);
but it is better and easier to use this macro:
- err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
+ err = v4l2_subdev_call(sd, core, g_std, &norm);
The macro will to the right NULL pointer checks and returns -ENODEV if subdev
-is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is
-NULL, or the actual result of the subdev->ops->core->g_chip_ident ops.
+is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_std is
+NULL, or the actual result of the subdev->ops->core->g_std ops.
It is also possible to call all or a subset of the sub-devices:
- v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip);
+ v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);
Any subdev that does not support this ops is skipped and error results are
ignored. If you want to check for errors use this:
- err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip);
+ err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);
Any error except -ENOIOCTLCMD will exit the loop with that error. If no
errors (except -ENOIOCTLCMD) occurred, then 0 is returned.
@@ -394,6 +412,30 @@
up the device, but once the subdev is registered it is completely transparent.
+In the asynchronous case subdevice probing can be invoked independently of the
+bridge driver availability. The subdevice driver then has to verify whether all
+the requirements for a successful probing are satisfied. This can include a
+check for a master clock availability. If any of the conditions aren't satisfied
+the driver might decide to return -EPROBE_DEFER to request further reprobing
+attempts. Once all conditions are met the subdevice shall be registered using
+the v4l2_async_register_subdev() function. Unregistration is performed using
+the v4l2_async_unregister_subdev() call. Subdevices registered this way are
+stored in a global list of subdevices, ready to be picked up by bridge drivers.
+
+Bridge drivers in turn have to register a notifier object with an array of
+subdevice descriptors that the bridge device needs for its operation. This is
+performed using the v4l2_async_notifier_register() call. To unregister the
+notifier the driver has to call v4l2_async_notifier_unregister(). The former of
+the two functions takes two arguments: a pointer to struct v4l2_device and a
+pointer to struct v4l2_async_notifier. The latter contains a pointer to an array
+of pointers to subdevice descriptors of type struct v4l2_async_subdev type. The
+V4L2 core will then use these descriptors to match asynchronously registered
+subdevices to them. If a match is detected the .bound() notifier callback is
+called. After all subdevices have been located the .complete() callback is
+called. When a subdevice is removed from the system the .unbind() method is
+called. All three callbacks are optional.
+
+
V4L2 sub-device userspace API
-----------------------------
@@ -575,9 +617,13 @@
The default video_device_release() callback just calls kfree to free the
allocated memory.
+There is also a video_device_release_empty() function that does nothing
+(is empty) and can be used if the struct is embedded and there is nothing
+to do when it is released.
+
You should also set these fields:
-- v4l2_dev: set to the v4l2_device parent device.
+- v4l2_dev: must be set to the v4l2_device parent device.
- name: set to something descriptive and unique.
@@ -614,15 +660,16 @@
If you want to have a separate priority state per (group of) device node(s),
then you can point it to your own struct v4l2_prio_state.
-- parent: you only set this if v4l2_device was registered with NULL as
+- dev_parent: you only set this if v4l2_device was registered with NULL as
the parent device struct. This only happens in cases where one hardware
device has multiple PCI devices that all share the same v4l2_device core.
The cx88 driver is an example of this: one core v4l2_device struct, but
- it is used by both an raw video PCI device (cx8800) and a MPEG PCI device
- (cx8802). Since the v4l2_device cannot be associated with a particular
- PCI device it is setup without a parent device. But when the struct
- video_device is setup you do know which parent PCI device to use.
+ it is used by both a raw video PCI device (cx8800) and a MPEG PCI device
+ (cx8802). Since the v4l2_device cannot be associated with two PCI devices
+ at the same time it is setup without a parent device. But when the struct
+ video_device is initialized you *do* know which parent PCI device to use and
+ so you set dev_device to the correct PCI device.
- flags: optional. Set to V4L2_FL_USE_FH_PRIO if you want to let the framework
handle the VIDIOC_G/S_PRIORITY ioctls. This requires that you use struct
@@ -1061,3 +1108,29 @@
An example on how the V4L2 events may be used can be found in the OMAP
3 ISP driver (drivers/media/platform/omap3isp).
+
+
+V4L2 clocks
+-----------
+
+Many subdevices, like camera sensors, TV decoders and encoders, need a clock
+signal to be supplied by the system. Often this clock is supplied by the
+respective bridge device. The Linux kernel provides a Common Clock Framework for
+this purpose. However, it is not (yet) available on all architectures. Besides,
+the nature of the multi-functional (clock, data + synchronisation, I2C control)
+connection of subdevices to the system might impose special requirements on the
+clock API usage. E.g. V4L2 has to support clock provider driver unregistration
+while a subdevice driver is holding a reference to the clock. For these reasons
+a V4L2 clock helper API has been developed and is provided to bridge and
+subdevice drivers.
+
+The API consists of two parts: two functions to register and unregister a V4L2
+clock source: v4l2_clk_register() and v4l2_clk_unregister() and calls to control
+a clock object, similar to the respective generic clock API calls:
+v4l2_clk_get(), v4l2_clk_put(), v4l2_clk_enable(), v4l2_clk_disable(),
+v4l2_clk_get_rate(), and v4l2_clk_set_rate(). Clock suppliers have to provide
+clock operations that will be called when clock users invoke respective API
+methods.
+
+It is expected that once the CCF becomes available on all relevant
+architectures this API will be removed.
diff --git a/Documentation/vm/zswap.txt b/Documentation/vm/zswap.txt
new file mode 100644
index 0000000..7e492d8
--- /dev/null
+++ b/Documentation/vm/zswap.txt
@@ -0,0 +1,68 @@
+Overview:
+
+Zswap is a lightweight compressed cache for swap pages. It takes pages that are
+in the process of being swapped out and attempts to compress them into a
+dynamically allocated RAM-based memory pool. zswap basically trades CPU cycles
+for potentially reduced swap I/O. This trade-off can also result in a
+significant performance improvement if reads from the compressed cache are
+faster than reads from a swap device.
+
+NOTE: Zswap is a new feature as of v3.11 and interacts heavily with memory
+reclaim. This interaction has not be fully explored on the large set of
+potential configurations and workloads that exist. For this reason, zswap
+is a work in progress and should be considered experimental.
+
+Some potential benefits:
+* Desktop/laptop users with limited RAM capacities can mitigate the
+ performance impact of swapping.
+* Overcommitted guests that share a common I/O resource can
+ dramatically reduce their swap I/O pressure, avoiding heavy handed I/O
+ throttling by the hypervisor. This allows more work to get done with less
+ impact to the guest workload and guests sharing the I/O subsystem
+* Users with SSDs as swap devices can extend the life of the device by
+ drastically reducing life-shortening writes.
+
+Zswap evicts pages from compressed cache on an LRU basis to the backing swap
+device when the compressed pool reaches it size limit. This requirement had
+been identified in prior community discussions.
+
+To enabled zswap, the "enabled" attribute must be set to 1 at boot time. e.g.
+zswap.enabled=1
+
+Design:
+
+Zswap receives pages for compression through the Frontswap API and is able to
+evict pages from its own compressed pool on an LRU basis and write them back to
+the backing swap device in the case that the compressed pool is full.
+
+Zswap makes use of zbud for the managing the compressed memory pool. Each
+allocation in zbud is not directly accessible by address. Rather, a handle is
+return by the allocation routine and that handle must be mapped before being
+accessed. The compressed memory pool grows on demand and shrinks as compressed
+pages are freed. The pool is not preallocated.
+
+When a swap page is passed from frontswap to zswap, zswap maintains a mapping
+of the swap entry, a combination of the swap type and swap offset, to the zbud
+handle that references that compressed swap page. This mapping is achieved
+with a red-black tree per swap type. The swap offset is the search key for the
+tree nodes.
+
+During a page fault on a PTE that is a swap entry, frontswap calls the zswap
+load function to decompress the page into the page allocated by the page fault
+handler.
+
+Once there are no PTEs referencing a swap page stored in zswap (i.e. the count
+in the swap_map goes to 0) the swap code calls the zswap invalidate function,
+via frontswap, to free the compressed entry.
+
+Zswap seeks to be simple in its policies. Sysfs attributes allow for one user
+controlled policies:
+* max_pool_percent - The maximum percentage of memory that the compressed
+ pool can occupy.
+
+Zswap allows the compressor to be selected at kernel boot time by setting the
+“compressor” attribute. The default compressor is lzo. e.g.
+zswap.compressor=deflate
+
+A debugfs interface is provided for various statistic about pool size, number
+of pages stored, and various counters for the reasons pages are rejected.
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt
index 04fddbac..f9492fe 100644
--- a/Documentation/watchdog/watchdog-parameters.txt
+++ b/Documentation/watchdog/watchdog-parameters.txt
@@ -194,14 +194,6 @@
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
-------------------------------------------------
-mpcore_wdt:
-mpcore_margin: MPcore timer margin in seconds.
- (0 < mpcore_margin < 65536, default=60)
-nowayout: Watchdog cannot be stopped once started
- (default=kernel config parameter)
-mpcore_noboot: MPcore watchdog action, set to 1 to ignore reboots,
- 0 to reboot (default=0
--------------------------------------------------
mv64x60_wdt:
nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter)
diff --git a/Documentation/zh_CN/video4linux/v4l2-framework.txt b/Documentation/zh_CN/video4linux/v4l2-framework.txt
index 44c1d93..0da95db 100644
--- a/Documentation/zh_CN/video4linux/v4l2-framework.txt
+++ b/Documentation/zh_CN/video4linux/v4l2-framework.txt
@@ -247,7 +247,6 @@
这些结构体定义如下:
struct v4l2_subdev_core_ops {
- int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*init)(struct v4l2_subdev *sd, u32 val);
...
@@ -337,24 +336,24 @@
注册之设备后,可通过以下方式直接调用其操作函数:
- err = sd->ops->core->g_chip_ident(sd, &chip);
+ err = sd->ops->core->g_std(sd, &norm);
但使用如下宏会比较容易且合适:
- err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
+ err = v4l2_subdev_call(sd, core, g_std, &norm);
这个宏将会做 NULL 指针检查,如果 subdev 为 NULL,则返回-ENODEV;如果
-subdev->core 或 subdev->core->g_chip_ident 为 NULL,则返回 -ENOIOCTLCMD;
-否则将返回 subdev->ops->core->g_chip_ident ops 调用的实际结果。
+subdev->core 或 subdev->core->g_std 为 NULL,则返回 -ENOIOCTLCMD;
+否则将返回 subdev->ops->core->g_std ops 调用的实际结果。
有时也可能同时调用所有或一系列子设备的某个操作函数:
- v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip);
+ v4l2_device_call_all(v4l2_dev, 0, core, g_std, &norm);
任何不支持此操作的子设备都会被跳过,并忽略错误返回值。但如果你需要
检查出错码,则可使用如下函数:
- err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip);
+ err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_std, &norm);
除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果(除 -ENOIOCTLCMD
外)没有错误发生,则返回 0。
diff --git a/MAINTAINERS b/MAINTAINERS
index 6a904d3..bf61e04 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1165,15 +1165,6 @@
S: Maintained
F: drivers/media/platform/s5p-g2d/
-ARM/SAMSUNG S5P SERIES FIMC SUPPORT
-M: Kyungmin Park <kyungmin.park@samsung.com>
-M: Sylwester Nawrocki <s.nawrocki@samsung.com>
-L: linux-arm-kernel@lists.infradead.org
-L: linux-media@vger.kernel.org
-S: Maintained
-F: arch/arm/plat-samsung/include/plat/*fimc*
-F: drivers/media/platform/s5p-fimc/
-
ARM/SAMSUNG S5P SERIES Multi Format Codec (MFC) SUPPORT
M: Kyungmin Park <kyungmin.park@samsung.com>
M: Kamil Debski <k.debski@samsung.com>
@@ -1333,6 +1324,12 @@
F: arch/arm/mach-zynq/
F: drivers/cpuidle/cpuidle-zynq.c
+ARM SMMU DRIVER
+M: Will Deacon <will.deacon@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/iommu/arm-smmu.c
+
ARM64 PORT (AARCH64 ARCHITECTURE)
M: Catalin Marinas <catalin.marinas@arm.com>
M: Will Deacon <will.deacon@arm.com>
@@ -1589,7 +1586,7 @@
F: net/ax25/
AZ6007 DVB DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -1874,7 +1871,7 @@
F: fs/btrfs/
BTTV VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -2129,9 +2126,12 @@
M: Julia Lawall <Julia.Lawall@lip6.fr>
M: Gilles Muller <Gilles.Muller@lip6.fr>
M: Nicolas Palix <nicolas.palix@imag.fr>
+M: Michal Marek <mmarek@suse.cz>
L: cocci@systeme.lip6.fr (moderated for non-subscribers)
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild.git misc
W: http://coccinelle.lip6.fr/
S: Supported
+F: Documentation/coccinelle.txt
F: scripts/coccinelle/
F: scripts/coccicheck
@@ -2359,7 +2359,7 @@
F: include/media/cx2341x*
CX88 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -2565,6 +2565,7 @@
DEVICE-MAPPER (LVM)
M: Alasdair Kergon <agk@redhat.com>
+M: Mike Snitzer <snitzer@redhat.com>
M: dm-devel@redhat.com
L: dm-devel@redhat.com
W: http://sources.redhat.com/dm
@@ -2576,6 +2577,7 @@
F: drivers/md/persistent-data/
F: include/linux/device-mapper.h
F: include/linux/dm-*.h
+F: include/uapi/linux/dm-*.h
DIOLAN U2C-12 I2C DRIVER
M: Guenter Roeck <linux@roeck-us.net>
@@ -2979,7 +2981,7 @@
F: drivers/edac/e7xxx_edac.c
EDAC-GHES
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3007,21 +3009,21 @@
F: drivers/edac/i5000_edac.c
EDAC-I5400
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5400_edac.c
EDAC-I7300
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i7300_edac.c
EDAC-I7CORE
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3050,7 +3052,7 @@
F: drivers/edac/r82600_edac.c
EDAC-SBRIDGE
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3110,7 +3112,7 @@
F: drivers/net/ethernet/ibm/ehea/
EM28XX VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -5306,7 +5308,7 @@
F: drivers/media/radio/radio-maxiradio*
MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
P: LinuxTV.org Project
L: linux-media@vger.kernel.org
W: http://linuxtv.org
@@ -5385,6 +5387,12 @@
F: include/linux/mtd/
F: include/uapi/mtd/
+MEN A21 WATCHDOG DRIVER
+M: Johannes Thumshirn <johannes.thumshirn@men.de>
+L: linux-watchdog@vger.kernel.org
+S: Supported
+F: drivers/watchdog/mena21_wdt.c
+
METAG ARCHITECTURE
M: James Hogan <james.hogan@imgtec.com>
S: Supported
@@ -5428,6 +5436,28 @@
S: Odd Fixes
F: drivers/media/radio/radio-miropcm20*
+Mellanox MLX5 core VPI driver
+M: Eli Cohen <eli@mellanox.com>
+L: netdev@vger.kernel.org
+L: linux-rdma@vger.kernel.org
+W: http://www.mellanox.com
+Q: http://patchwork.ozlabs.org/project/netdev/list/
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+T: git://openfabrics.org/~eli/connect-ib.git
+S: Supported
+F: drivers/net/ethernet/mellanox/mlx5/core/
+F: include/linux/mlx5/
+
+Mellanox MLX5 IB driver
+M: Eli Cohen <eli@mellanox.com>
+L: linux-rdma@vger.kernel.org
+W: http://www.mellanox.com
+Q: http://patchwork.kernel.org/project/linux-rdma/list/
+T: git://openfabrics.org/~eli/connect-ib.git
+S: Supported
+F: include/linux/mlx5/
+F: drivers/infiniband/hw/mlx5/
+
MODULE SUPPORT
M: Rusty Russell <rusty@rustcorp.com.au>
S: Maintained
@@ -7004,7 +7034,7 @@
F: drivers/media/i2c/saa6588*
SAA7134 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -7049,6 +7079,15 @@
F: drivers/rtc/rtc-sec.c
F: include/linux/mfd/samsung/
+SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Sylwester Nawrocki <s.nawrocki@samsung.com>
+L: linux-media@vger.kernel.org
+Q: https://patchwork.linuxtv.org/project/linux-media/list/
+S: Supported
+F: drivers/media/platform/exynos4-is/
+F: include/media/s5p_fimc.h
+
SAMSUNG S3C24XX/S3C64XX SOC SERIES CAMIF DRIVER
M: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
L: linux-media@vger.kernel.org
@@ -7366,7 +7405,7 @@
F: drivers/media/radio/radio-si4713.h
SIANO DVB DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8071,7 +8110,7 @@
F: drivers/media/i2c/tda9840*
TEA5761 TUNER DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8079,7 +8118,7 @@
F: drivers/media/tuners/tea5761.*
TEA5767 TUNER DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8152,6 +8191,7 @@
M: Eduardo Valentin <eduardo.valentin@ti.com>
L: linux-pm@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal.git
Q: https://patchwork.kernel.org/project/linux-pm/list/
S: Supported
F: drivers/thermal/
@@ -8176,8 +8216,8 @@
TI BANDGAP AND THERMAL DRIVER
M: Eduardo Valentin <eduardo.valentin@ti.com>
L: linux-pm@vger.kernel.org
-S: Maintained
-F: drivers/staging/omap-thermal/
+S: Supported
+F: drivers/thermal/ti-soc-thermal/
TI FLASH MEDIA INTERFACE DRIVER
M: Alex Dubov <oakad@yahoo.com>
@@ -8317,7 +8357,7 @@
F: mm/shmem.c
TM6000 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8886,6 +8926,7 @@
L: virtualization@lists.linux-foundation.org
S: Maintained
F: drivers/virtio/
+F: tools/virtio/
F: drivers/net/virtio_net.c
F: drivers/block/virtio_blk.c
F: include/linux/virtio_*.h
@@ -9173,7 +9214,7 @@
F: arch/x86/kernel/cpu/mcheck/*
XC2028/3028 TUNER DRIVER
-M: Mauro Carvalho Chehab <mchehab@redhat.com>
+M: Mauro Carvalho Chehab <m.chehab@samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
diff --git a/Makefile b/Makefile
index 170ed7c..4e3575c 100644
--- a/Makefile
+++ b/Makefile
@@ -1116,6 +1116,7 @@
@echo ' gtags - Generate GNU GLOBAL index'
@echo ' kernelrelease - Output the release version string'
@echo ' kernelversion - Output the version stored in Makefile'
+ @echo ' image_name - Output the image name'
@echo ' headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH'; \
echo ' (default: $(INSTALL_HDR_PATH))'; \
echo ''
@@ -1310,7 +1311,7 @@
endif #ifeq ($(config-targets),1)
endif #ifeq ($(mixed-targets),1)
-PHONY += checkstack kernelrelease kernelversion
+PHONY += checkstack kernelrelease kernelversion image_name
# UML needs a little special treatment here. It wants to use the host
# toolchain, so needs $(SUBARCH) passed to checkstack.pl. Everyone
@@ -1331,6 +1332,9 @@
kernelversion:
@echo $(KERNELVERSION)
+image_name:
+ @echo $(KBUILD_IMAGE)
+
# Clear a bunch of variables before executing the submake
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0ac9be6..ba412e0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1316,7 +1316,7 @@
config ARM_ERRATA_364296
bool "ARM errata: Possible cache data corruption with hit-under-miss enabled"
- depends on CPU_V6 && !SMP
+ depends on CPU_V6
help
This options enables the workaround for the 364296 ARM1136
r0p2 erratum (possible cache data corruption with
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 5b7be8d..e401a76 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -510,6 +510,16 @@
Say Y here if you want the debug print routines to direct
their output to the uart1 port on SiRFmarco devices.
+ config DEBUG_STI_UART
+ depends on ARCH_STI
+ bool "Use StiH415/416 ASC for low-level debug"
+ help
+ Say Y here if you want kernel low-level debugging support
+ on StiH415/416 based platforms like B2000, B2020.
+ It support UART2 and SBC_UART1.
+
+ If unsure, say N.
+
config DEBUG_U300_UART
bool "Kernel low-level debugging messages via U300 UART0"
depends on ARCH_U300
@@ -564,16 +574,6 @@
This option selects UART0 on VIA/Wondermedia System-on-a-chip
devices, including VT8500, WM8505, WM8650 and WM8850.
- config DEBUG_STI_UART
- depends on ARCH_STI
- bool "Use StiH415/416 ASC for low-level debug"
- help
- Say Y here if you want kernel low-level debugging support
- on StiH415/416 based platforms like B2000, B2020.
- It support UART2 and SBC_UART1.
-
- If unsure, say N.
-
config DEBUG_LL_UART_NONE
bool "No low-level debugging UART"
depends on !ARCH_MULTIPLATFORM
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index ab177b4..365760b 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -828,6 +828,7 @@
regulator-name = "vdd_vbus_wup1";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
+ enable-active-high;
gpio = <&gpio 24 0>; /* PD0 */
};
};
diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts
index 1701599..ed4b901 100644
--- a/arch/arm/boot/dts/tegra20-trimslice.dts
+++ b/arch/arm/boot/dts/tegra20-trimslice.dts
@@ -410,6 +410,7 @@
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
+ enable-active-high;
gpio = <&gpio 170 0>; /* PV2 */
};
};
diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts
index ea078ab..ab67c94 100644
--- a/arch/arm/boot/dts/tegra20-whistler.dts
+++ b/arch/arm/boot/dts/tegra20-whistler.dts
@@ -586,6 +586,7 @@
regulator-name = "vbus1";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
+ enable-active-high;
gpio = <&tca6416 0 0>; /* GPIO_PMU0 */
};
@@ -595,6 +596,7 @@
regulator-name = "vbus3";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
+ enable-active-high;
gpio = <&tca6416 1 0>; /* GPIO_PMU1 */
};
};
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 340d550..fe0bdc3 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -1,88 +1,167 @@
-CONFIG_EXPERIMENTAL=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_ARCH_MVEBU=y
CONFIG_MACH_ARMADA_370=y
-CONFIG_ARCH_SIRF=y
CONFIG_MACH_ARMADA_XP=y
+CONFIG_ARCH_BCM=y
+CONFIG_GPIO_PCA953X=y
CONFIG_ARCH_HIGHBANK=y
+CONFIG_ARCH_KEYSTONE=y
+CONFIG_ARCH_MXC=y
+CONFIG_MACH_IMX51_DT=y
+CONFIG_SOC_IMX53=y
+CONFIG_SOC_IMX6Q=y
+CONFIG_SOC_IMX6SL=y
+CONFIG_SOC_VF610=y
+CONFIG_ARCH_OMAP3=y
+CONFIG_ARCH_OMAP4=y
+CONFIG_SOC_OMAP5=y
+CONFIG_SOC_AM33XX=y
+CONFIG_SOC_AM43XX=y
+CONFIG_ARCH_ROCKCHIP=y
CONFIG_ARCH_SOCFPGA=y
-CONFIG_ARCH_SUNXI=y
-CONFIG_ARCH_WM8850=y
-# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set
-CONFIG_ARCH_ZYNQ=y
-CONFIG_ARM_ERRATA_754322=y
CONFIG_PLAT_SPEAR=y
CONFIG_ARCH_SPEAR13XX=y
CONFIG_MACH_SPEAR1310=y
CONFIG_MACH_SPEAR1340=y
+CONFIG_ARCH_STI=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_ARCH_SIRF=y
+CONFIG_ARCH_TEGRA=y
+CONFIG_ARCH_TEGRA_2x_SOC=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_ARCH_TEGRA_114_SOC=y
+CONFIG_TEGRA_PCI=y
+CONFIG_TEGRA_EMC_SCALING_ENABLE=y
+CONFIG_ARCH_U8500=y
+CONFIG_MACH_SNOWBALL=y
+CONFIG_MACH_UX500_DT=y
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_ARCH_VEXPRESS_CA9X4=y
+CONFIG_ARCH_VIRT=y
+CONFIG_ARCH_WM8850=y
+CONFIG_ARCH_ZYNQ=y
CONFIG_SMP=y
-CONFIG_ARM_ARCH_TIMER=y
-CONFIG_AEABI=y
-CONFIG_HIGHMEM=y
CONFIG_HIGHPTE=y
CONFIG_ARM_APPENDED_DTB=y
-CONFIG_VFP=y
-CONFIG_NEON=y
CONFIG_NET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
CONFIG_BLK_DEV_SD=y
CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_SATA_HIGHBANK=y
CONFIG_SATA_MV=y
-CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_NETDEVICES=y
CONFIG_SUN4I_EMAC=y
CONFIG_NET_CALXEDA_XGMAC=y
CONFIG_SMSC911X=y
CONFIG_STMMAC_ETH=y
-CONFIG_SERIO_AMBAKMI=y
CONFIG_MDIO_SUN4I=y
+CONFIG_KEYBOARD_SPEAR=y
+CONFIG_SERIO_AMBAKMI=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_DW=y
-CONFIG_KEYBOARD_SPEAR=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
-CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIAL_SIRFSOC=y
CONFIG_SERIAL_SIRFSOC_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
CONFIG_SERIAL_VT8500=y
CONFIG_SERIAL_VT8500_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
CONFIG_SERIAL_XILINX_PS_UART=y
CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
-CONFIG_IPMI_HANDLER=y
-CONFIG_IPMI_SI=y
-CONFIG_I2C=y
+CONFIG_SERIAL_FSL_LPUART=y
+CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_I2C_SIRF=y
+CONFIG_I2C_TEGRA=y
CONFIG_SPI=y
CONFIG_SPI_PL022=y
CONFIG_SPI_SIRF=y
-CONFIG_GPIO_PL061=y
-CONFIG_FB=y
+CONFIG_SPI_TEGRA114=y
+CONFIG_SPI_TEGRA20_SLINK=y
+CONFIG_PINCTRL_SINGLE=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_AB8500=y
+CONFIG_REGULATOR_TPS51632=y
+CONFIG_REGULATOR_TPS62360=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_REGULATOR_VEXPRESS=y
+CONFIG_DRM=y
+CONFIG_TEGRA_HOST1X=y
+CONFIG_DRM_TEGRA=y
CONFIG_FB_ARMCLCD=y
CONFIG_FB_WM8505=y
-CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FB_SIMPLE=y
CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MXC=y
+CONFIG_USB_EHCI_TEGRA=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_ISP1760_HCD=y
CONFIG_USB_STORAGE=y
+CONFIG_AB8500_USB=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_OMAP_USB2=y
+CONFIG_OMAP_USB3=y
+CONFIG_SAMSUNG_USB2PHY=y
+CONFIG_SAMSUNG_USB3PHY=y
+CONFIG_USB_GPIO_VBUS=y
+CONFIG_USB_ISP1301=y
+CONFIG_USB_MXS_PHY=y
CONFIG_MMC=y
CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_TEGRA=y
CONFIG_MMC_SDHCI_SPEAR=y
-CONFIG_MMC_WMT=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_HIGHBANK_MC=y
CONFIG_EDAC_HIGHBANK_L2=y
CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TWL4030=y
CONFIG_RTC_DRV_PL031=y
CONFIG_RTC_DRV_VT8500=y
+CONFIG_RTC_DRV_TEGRA=y
+CONFIG_DMADEVICES=y
+CONFIG_DW_DMAC=y
+CONFIG_TEGRA20_APB_DMA=y
+CONFIG_STE_DMA40=y
+CONFIG_SIRF_DMA=y
+CONFIG_TI_EDMA=y
+CONFIG_PL330_DMA=y
+CONFIG_IMX_SDMA=y
+CONFIG_IMX_DMA=y
+CONFIG_MXS_DMA=y
+CONFIG_DMA_OMAP=y
CONFIG_PWM=y
CONFIG_PWM_VT8500=y
-CONFIG_DMADEVICES=y
-CONFIG_PL330_DMA=y
-CONFIG_SIRF_DMA=y
-CONFIG_DW_DMAC=y
+CONFIG_EXT4_FS=y
+CONFIG_TMPFS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_LOCKUP_DETECTOR=y
diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig
index a24c024..5339e6a 100644
--- a/arch/arm/configs/omap2plus_defconfig
+++ b/arch/arm/configs/omap2plus_defconfig
@@ -22,6 +22,10 @@
# CONFIG_BLK_DEV_BSG is not set
CONFIG_ARCH_MULTI_V6=y
CONFIG_ARCH_OMAP2PLUS=y
+CONFIG_ARCH_OMAP2=y
+CONFIG_ARCH_OMAP3=y
+CONFIG_ARCH_OMAP4=y
+CONFIG_SOC_AM33XX=y
CONFIG_OMAP_RESET_CLOCKS=y
CONFIG_OMAP_MUX_DEBUG=y
CONFIG_ARCH_VEXPRESS_CA9X4=y
@@ -34,6 +38,8 @@
CONFIG_LEDS=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootwait console=ttyO2,115200"
CONFIG_KEXEC=y
CONFIG_FPE_NWFPE=y
@@ -152,6 +158,13 @@
CONFIG_POWER_SUPPLY=y
CONFIG_SENSORS_LM75=m
CONFIG_WATCHDOG=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+CONFIG_THERMAL_GOV_FAIR_SHARE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_THERMAL_GOV_USER_SPACE=y
+CONFIG_CPU_THERMAL=y
CONFIG_OMAP_WATCHDOG=y
CONFIG_TWL4030_WATCHDOG=y
CONFIG_MFD_TPS65217=y
@@ -238,7 +251,13 @@
CONFIG_RTC_DRV_TWL4030=y
CONFIG_RTC_DRV_OMAP=y
CONFIG_DMADEVICES=y
+CONFIG_TI_EDMA=y
CONFIG_DMA_OMAP=y
+CONFIG_TI_SOC_THERMAL=y
+CONFIG_TI_THERMAL=y
+CONFIG_OMAP4_THERMAL=y
+CONFIG_OMAP5_THERMAL=y
+CONFIG_DRA752_THERMAL=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_FS_XATTR is not set
diff --git a/arch/arm/configs/spear13xx_defconfig b/arch/arm/configs/spear13xx_defconfig
index 1fdb826..82eaa55 100644
--- a/arch/arm/configs/spear13xx_defconfig
+++ b/arch/arm/configs/spear13xx_defconfig
@@ -61,7 +61,6 @@
CONFIG_GPIO_PL061=y
# CONFIG_HWMON is not set
CONFIG_WATCHDOG=y
-CONFIG_MPCORE_WATCHDOG=y
# CONFIG_HID_SUPPORT is not set
CONFIG_USB=y
# CONFIG_USB_DEVICE_CLASS is not set
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index c037aa1..a0025dc 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -1,6 +1,8 @@
-CONFIG_EXPERIMENTAL=y
+CONFIG_HIGHMEM=y
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_KALLSYMS_ALL=y
CONFIG_MODULES=y
@@ -9,10 +11,7 @@
CONFIG_ARCH_U8500=y
CONFIG_MACH_HREFV60=y
CONFIG_MACH_SNOWBALL=y
-CONFIG_MACH_U5500=y
CONFIG_MACH_UX500_DT=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2
CONFIG_PREEMPT=y
@@ -20,6 +19,7 @@
CONFIG_CMDLINE="root=/dev/ram0 console=ttyAMA2,115200n8"
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_PM_RUNTIME=y
@@ -36,7 +36,6 @@
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=65536
-CONFIG_AB8500_PWM=y
CONFIG_SENSORS_BH1780=y
CONFIG_NETDEVICES=y
CONFIG_SMSC911X=y
@@ -60,35 +59,39 @@
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_HW_RANDOM=y
-CONFIG_HW_RANDOM_NOMADIK=y
CONFIG_SPI=y
CONFIG_SPI_PL022=y
CONFIG_GPIO_STMPE=y
CONFIG_GPIO_TC3589X=y
-# CONFIG_POWER_SUPPLY is not set
-# CONFIG_AB8500_BM is not set
-# CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL is not set
CONFIG_THERMAL=y
CONFIG_CPU_THERMAL=y
+CONFIG_WATCHDOG=y
CONFIG_MFD_STMPE=y
CONFIG_MFD_TC3589X=y
-CONFIG_AB5500_CORE=y
-CONFIG_AB8500_CORE=y
-CONFIG_REGULATOR=y
-CONFIG_REGULATOR_AB8500=y
-CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_GPIO=y
-# CONFIG_HID_SUPPORT is not set
-CONFIG_USB_GADGET=y
+CONFIG_REGULATOR_AB8500=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_UX500=y
+CONFIG_SND_SOC_UX500_MACH_MOP500=y
+CONFIG_USB=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_UX500=y
+CONFIG_USB_PHY=y
CONFIG_AB8500_USB=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_USB_ETH=m
CONFIG_MMC=y
-CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_UNSAFE_RESUME=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
CONFIG_MMC_ARMMMCI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_LM3530=y
-CONFIG_LEDS_LP5521=y
CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_LP5521=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_RTC_CLASS=y
@@ -108,7 +111,6 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_CONFIGFS_FS=m
# CONFIG_MISC_FILESYSTEMS is not set
CONFIG_NFS_FS=y
CONFIG_ROOT_NFS=y
@@ -122,3 +124,7 @@
CONFIG_DEBUG_INFO=y
# CONFIG_FTRACE is not set
CONFIG_DEBUG_USER=y
+CONFIG_CRYPTO_DEV_UX500=y
+CONFIG_CRYPTO_DEV_UX500_CRYP=y
+CONFIG_CRYPTO_DEV_UX500_HASH=y
+CONFIG_CRYPTO_DEV_UX500_DEBUG=y
diff --git a/arch/arm/include/asm/smp_scu.h b/arch/arm/include/asm/smp_scu.h
index 18d1693..0393fba 100644
--- a/arch/arm/include/asm/smp_scu.h
+++ b/arch/arm/include/asm/smp_scu.h
@@ -23,10 +23,21 @@
return pa;
}
+#ifdef CONFIG_HAVE_ARM_SCU
unsigned int scu_get_core_count(void __iomem *);
int scu_power_mode(void __iomem *, unsigned int);
+#else
+static inline unsigned int scu_get_core_count(void __iomem *scu_base)
+{
+ return 0;
+}
+static inline int scu_power_mode(void __iomem *scu_base, unsigned int mode)
+{
+ return -EINVAL;
+}
+#endif
-#ifdef CONFIG_SMP
+#if defined(CONFIG_SMP) && defined(CONFIG_HAVE_ARM_SCU)
void scu_enable(void __iomem *scu_base);
#else
static inline void scu_enable(void __iomem *scu_base) {}
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index 5b391a6..76ab5ca5 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -133,6 +133,9 @@
ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)
+ __FINIT
+ .text
+
/*
* Read processor ID register (CP#15, CR0), and look up in the linker-built
* supported processor list. Note that we can't use the absolute addresses
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 90525d9..f6fd1d4 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -120,7 +120,7 @@
* changing cpu.
*/
if (flags == POST_RATE_CHANGE)
- smp_call_function(twd_update_frequency,
+ on_each_cpu(twd_update_frequency,
(void *)&cnd->new_rate, 1);
return NOTIFY_OK;
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c
index fd38c8d..afbc439 100644
--- a/arch/arm/mach-davinci/board-dm365-evm.c
+++ b/arch/arm/mach-davinci/board-dm365-evm.c
@@ -509,7 +509,6 @@
.ch_1 = 3,
.ch_2 = 3,
.ch_3 = 3,
- .init_enable = 1,
};
static struct amp_config_info vpbe_amp = {
diff --git a/arch/arm/mach-dove/include/mach/bridge-regs.h b/arch/arm/mach-dove/include/mach/bridge-regs.h
index 99f259e..5362df3 100644
--- a/arch/arm/mach-dove/include/mach/bridge-regs.h
+++ b/arch/arm/mach-dove/include/mach/bridge-regs.h
@@ -26,6 +26,7 @@
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
#define SOFT_RESET 0x00000001
+#define BRIDGE_CAUSE (BRIDGE_VIRT_BASE + 0x0110)
#define BRIDGE_INT_TIMER1_CLR (~0x0004)
#define IRQ_VIRT_BASE (BRIDGE_VIRT_BASE + 0x0200)
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index f5f65b5..855d4a7 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -38,7 +38,7 @@
depends on ARCH_EXYNOS4
select ARM_CPU_SUSPEND if PM
select PINCTRL_EXYNOS
- select PM_GENERIC_DOMAINS
+ select PM_GENERIC_DOMAINS if PM
select S5P_PM if PM
select S5P_SLEEP if PM
select SAMSUNG_DMADEV
diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c
index 686ef34..63de1b3 100644
--- a/arch/arm/mach-ixp4xx/dsmg600-setup.c
+++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c
@@ -28,6 +28,7 @@
#include <linux/i2c-gpio.h>
#include <mach/hardware.h>
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
diff --git a/arch/arm/mach-ixp4xx/include/mach/timex.h b/arch/arm/mach-ixp4xx/include/mach/timex.h
index c9e930f..0396d89 100644
--- a/arch/arm/mach-ixp4xx/include/mach/timex.h
+++ b/arch/arm/mach-ixp4xx/include/mach/timex.h
@@ -3,7 +3,7 @@
*
*/
-#include <mach/hardware.h>
+#include <mach/ixp4xx-regs.h>
/*
* We use IXP425 General purpose timer for our timer needs, it runs at
diff --git a/arch/arm/mach-ixp4xx/omixp-setup.c b/arch/arm/mach-ixp4xx/omixp-setup.c
index 46a89f5..75ef03d 100644
--- a/arch/arm/mach-ixp4xx/omixp-setup.c
+++ b/arch/arm/mach-ixp4xx/omixp-setup.c
@@ -27,6 +27,8 @@
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
+#include <mach/hardware.h>
+
static struct resource omixp_flash_resources[] = {
{
.flags = IORESOURCE_MEM,
diff --git a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
index d4cbe5e..91242c9 100644
--- a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
+++ b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h
@@ -21,14 +21,12 @@
#define CPU_RESET 0x00000002
#define RSTOUTn_MASK (BRIDGE_VIRT_BASE + 0x0108)
-#define WDT_RESET_OUT_EN 0x00000002
#define SOFT_RESET_OUT_EN 0x00000004
#define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE + 0x010c)
#define SOFT_RESET 0x00000001
#define BRIDGE_CAUSE (BRIDGE_VIRT_BASE + 0x0110)
-#define WDT_INT_REQ 0x0008
#define BRIDGE_INT_TIMER1_CLR (~0x0004)
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index c7b32a9..627fa7e 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -1,10 +1,92 @@
config ARCH_OMAP
bool
+config ARCH_OMAP2
+ bool "TI OMAP2"
+ depends on ARCH_MULTI_V6
+ select ARCH_OMAP2PLUS
+ select CPU_V6
+ select MULTI_IRQ_HANDLER
+ select SOC_HAS_OMAP2_SDRC
+ select COMMON_CLK
+
+config ARCH_OMAP3
+ bool "TI OMAP3"
+ depends on ARCH_MULTI_V7
+ select ARCH_OMAP2PLUS
+ select ARCH_HAS_OPP
+ select ARM_CPU_SUSPEND if PM
+ select CPU_V7
+ select MULTI_IRQ_HANDLER
+ select OMAP_INTERCONNECT
+ select PM_OPP if PM
+ select PM_RUNTIME if CPU_IDLE
+ select SOC_HAS_OMAP2_SDRC
+ select COMMON_CLK
+ select USB_ARCH_HAS_EHCI if USB_SUPPORT
+
+config ARCH_OMAP4
+ bool "TI OMAP4"
+ depends on ARCH_MULTI_V7
+ select ARCH_OMAP2PLUS
+ select ARCH_HAS_OPP
+ select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
+ select ARM_CPU_SUSPEND if PM
+ select ARM_ERRATA_720789
+ select ARM_GIC
+ select CACHE_L2X0
+ select CPU_V7
+ select HAVE_ARM_SCU if SMP
+ select HAVE_ARM_TWD if LOCAL_TIMERS
+ select HAVE_SMP
+ select LOCAL_TIMERS if SMP
+ select OMAP_INTERCONNECT
+ select PL310_ERRATA_588369
+ select PL310_ERRATA_727915
+ select PM_OPP if PM
+ select PM_RUNTIME if CPU_IDLE
+ select USB_ARCH_HAS_EHCI if USB_SUPPORT
+ select COMMON_CLK
+ select ARM_ERRATA_754322
+ select ARM_ERRATA_775420
+
+config SOC_OMAP5
+ bool "TI OMAP5"
+ depends on ARCH_MULTI_V7
+ select ARCH_OMAP2PLUS
+ select ARM_CPU_SUSPEND if PM
+ select ARM_GIC
+ select CPU_V7
+ select HAVE_ARM_SCU if SMP
+ select HAVE_ARM_TWD if LOCAL_TIMERS
+ select HAVE_SMP
+ select COMMON_CLK
+ select HAVE_ARM_ARCH_TIMER
+ select ARM_ERRATA_798181
+
+config SOC_AM33XX
+ bool "AM33XX support"
+ depends on ARCH_MULTI_V7
+ select ARCH_OMAP2PLUS
+ select ARM_CPU_SUSPEND if PM
+ select CPU_V7
+ select MULTI_IRQ_HANDLER
+ select COMMON_CLK
+
+config SOC_AM43XX
+ bool "TI AM43x"
+ depends on ARCH_MULTI_V7
+ select CPU_V7
+ select ARCH_OMAP2PLUS
+ select MULTI_IRQ_HANDLER
+ select ARM_GIC
+ select COMMON_CLK
+ select MACH_OMAP_GENERIC
+
config ARCH_OMAP2PLUS
- bool "TI OMAP2/3/4/5 SoCs with device tree support" if (ARCH_MULTI_V6 || ARCH_MULTI_V7)
- select ARCH_HAS_CPUFREQ
+ bool
select ARCH_HAS_BANDGAP
+ select ARCH_HAS_CPUFREQ
select ARCH_HAS_HOLES_MEMORYMODEL
select ARCH_OMAP
select ARCH_REQUIRE_GPIOLIB
@@ -53,68 +135,6 @@
depends on SOC_OMAP5
default y
-config ARCH_OMAP2
- bool "TI OMAP2"
- depends on ARCH_OMAP2PLUS
- depends on ARCH_MULTI_V6
- default y
- select CPU_V6
- select MULTI_IRQ_HANDLER
- select SOC_HAS_OMAP2_SDRC
- select COMMON_CLK
-
-config ARCH_OMAP3
- bool "TI OMAP3"
- depends on ARCH_OMAP2PLUS
- depends on ARCH_MULTI_V7
- default y
- select ARCH_HAS_OPP
- select ARM_CPU_SUSPEND if PM
- select CPU_V7
- select MULTI_IRQ_HANDLER
- select OMAP_INTERCONNECT
- select PM_OPP if PM
- select PM_RUNTIME if CPU_IDLE
- select SOC_HAS_OMAP2_SDRC
- select COMMON_CLK
- select USB_ARCH_HAS_EHCI if USB_SUPPORT
-
-config ARCH_OMAP4
- bool "TI OMAP4"
- default y
- depends on ARCH_OMAP2PLUS
- depends on ARCH_MULTI_V7
- select ARCH_HAS_OPP
- select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
- select ARM_CPU_SUSPEND if PM
- select ARM_ERRATA_720789
- select ARM_GIC
- select CACHE_L2X0
- select CPU_V7
- select HAVE_ARM_SCU if SMP
- select HAVE_ARM_TWD if LOCAL_TIMERS
- select HAVE_SMP
- select LOCAL_TIMERS if SMP
- select OMAP_INTERCONNECT
- select PL310_ERRATA_588369
- select PL310_ERRATA_727915
- select PM_OPP if PM
- select PM_RUNTIME if CPU_IDLE
- select USB_ARCH_HAS_EHCI if USB_SUPPORT
- select COMMON_CLK
- select ARM_ERRATA_754322
- select ARM_ERRATA_775420
-
-config SOC_OMAP5
- bool "TI OMAP5"
- depends on ARCH_MULTI_V7
- select ARM_CPU_SUSPEND if PM
- select ARM_GIC
- select CPU_V7
- select HAVE_SMP
- select COMMON_CLK
- select HAVE_ARM_ARCH_TIMER
-
comment "OMAP Core Type"
depends on ARCH_OMAP2
@@ -142,23 +162,6 @@
depends on ARCH_OMAP3
default y
-config SOC_AM33XX
- bool "AM33XX support"
- depends on ARCH_MULTI_V7
- default y
- select ARM_CPU_SUSPEND if PM
- select CPU_V7
- select MULTI_IRQ_HANDLER
- select COMMON_CLK
-
-config SOC_AM43XX
- bool "TI AM43x"
- select CPU_V7
- select MULTI_IRQ_HANDLER
- select ARM_GIC
- select COMMON_CLK
- select MACH_OMAP_GENERIC
-
config OMAP_PACKAGE_ZAF
bool
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index ea5a27f..d4f6715 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -95,10 +95,6 @@
AFLAGS_sleep24xx.o :=-Wa,-march=armv6
AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec)
-ifeq ($(CONFIG_PM_VERBOSE),y)
-CFLAGS_pm_bus.o += -DDEBUG
-endif
-
endif
ifeq ($(CONFIG_CPU_IDLE),y)
diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c
index b54562d..87e65dd 100644
--- a/arch/arm/mach-omap2/board-igep0020.c
+++ b/arch/arm/mach-omap2/board-igep0020.c
@@ -553,6 +553,37 @@
#ifdef CONFIG_OMAP_MUX
static struct omap_board_mux board_mux[] __initdata = {
+ /* Display Sub System */
+ OMAP3_MUX(DSS_PCLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_HSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_VSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_ACBIAS, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA0, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA2, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA3, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA4, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA5, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA6, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA7, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA8, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA9, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA10, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA11, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA12, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA13, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA14, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA15, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA16, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA17, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA18, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA19, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA20, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA21, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA22, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ OMAP3_MUX(DSS_DATA23, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT),
+ /* TFP410 PanelBus DVI Transmitte (GPIO_170) */
+ OMAP3_MUX(HDQ_SIO, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT),
/* SMSC9221 LAN Controller ETH IRQ (GPIO_176) */
OMAP3_MUX(MCSPI1_CS2, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),
{ .reg_offset = OMAP_MUX_TERMINATOR },
diff --git a/arch/arm/mach-omap2/board-rx51-video.c b/arch/arm/mach-omap2/board-rx51-video.c
index bd74f9f..bdd1e3a 100644
--- a/arch/arm/mach-omap2/board-rx51-video.c
+++ b/arch/arm/mach-omap2/board-rx51-video.c
@@ -61,7 +61,7 @@
static int __init rx51_video_init(void)
{
- if (!machine_is_nokia_rx51())
+ if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900"))
return 0;
if (omap_mux_init_gpio(RX51_LCD_RESET_GPIO, OMAP_PIN_OUTPUT)) {
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index aef96e4..3c1279f 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -15,7 +15,6 @@
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/pinctrl/machine.h>
@@ -66,7 +65,7 @@
WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
- return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+ return PTR_RET(pdev);
}
omap_postcore_initcall(omap3_l3_init);
@@ -100,7 +99,7 @@
WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
- return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+ return PTR_RET(pdev);
}
omap_postcore_initcall(omap4_l3_init);
diff --git a/arch/arm/mach-omap2/fb.c b/arch/arm/mach-omap2/fb.c
index 190ae49..2ca33cc 100644
--- a/arch/arm/mach-omap2/fb.c
+++ b/arch/arm/mach-omap2/fb.c
@@ -83,10 +83,7 @@
pdev = platform_device_register_resndata(NULL, "omapvrfb", -1,
res, num_res, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
- else
- return 0;
+ return PTR_RET(pdev);
}
omap_arch_initcall(omap_init_vrfb);
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
index 1c7969e..f3fdd6a 100644
--- a/arch/arm/mach-omap2/gpmc.c
+++ b/arch/arm/mach-omap2/gpmc.c
@@ -1734,7 +1734,7 @@
pdev = omap_device_build(DEVICE_NAME, -1, oh, NULL, 0);
WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
- return IS_ERR(pdev) ? PTR_ERR(pdev) : 0;
+ return PTR_RET(pdev);
}
omap_postcore_initcall(omap_gpmc_init);
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index fe3253a..4a3f06f 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -394,7 +394,7 @@
omap_pm_if_early_init();
}
-static void __init omap_common_late_init(void)
+static void __init __maybe_unused omap_common_late_init(void)
{
omap_mux_late_init();
omap2_common_pm_late_init();
diff --git a/arch/arm/mach-omap2/pmu.c b/arch/arm/mach-omap2/pmu.c
index 9ace8ea..33c8846 100644
--- a/arch/arm/mach-omap2/pmu.c
+++ b/arch/arm/mach-omap2/pmu.c
@@ -54,10 +54,7 @@
WARN(IS_ERR(omap_pmu_dev), "Can't build omap_device for %s.\n",
dev_name);
- if (IS_ERR(omap_pmu_dev))
- return PTR_ERR(omap_pmu_dev);
-
- return 0;
+ return PTR_RET(omap_pmu_dev);
}
static int __init omap_init_pmu(void)
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
index 88ff83a..9086ce0 100644
--- a/arch/arm/mach-omap2/sleep44xx.S
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -34,6 +34,8 @@
ppa_por_params:
.word 1, 0
+#ifdef CONFIG_ARCH_OMAP4
+
/*
* =============================
* == CPU suspend finisher ==
@@ -326,7 +328,9 @@
b cpu_resume @ Jump to generic resume
ENDPROC(omap4_cpu_resume)
-#endif
+#endif /* CONFIG_ARCH_OMAP4 */
+
+#endif /* defined(CONFIG_SMP) && defined(CONFIG_PM) */
#ifndef CONFIG_OMAP4_ERRATA_I688
ENTRY(omap_bus_sync)
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 29ac667..b37e1fc 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -220,7 +220,7 @@
int posted)
{
char name[10]; /* 10 = sizeof("gptXX_Xck0") */
- const char *oh_name;
+ const char *oh_name = NULL;
struct device_node *np;
struct omap_hwmod *oh;
struct resource irq, mem;
diff --git a/arch/arm/mach-orion5x/include/mach/bridge-regs.h b/arch/arm/mach-orion5x/include/mach/bridge-regs.h
index 461fd69..f727d03 100644
--- a/arch/arm/mach-orion5x/include/mach/bridge-regs.h
+++ b/arch/arm/mach-orion5x/include/mach/bridge-regs.h
@@ -18,7 +18,6 @@
#define CPU_CTRL (ORION5X_BRIDGE_VIRT_BASE + 0x104)
#define RSTOUTn_MASK (ORION5X_BRIDGE_VIRT_BASE + 0x108)
-#define WDT_RESET_OUT_EN 0x0002
#define CPU_SOFT_RESET (ORION5X_BRIDGE_VIRT_BASE + 0x10c)
@@ -26,8 +25,6 @@
#define POWER_MNG_CTRL_REG (ORION5X_BRIDGE_VIRT_BASE + 0x11C)
-#define WDT_INT_REQ 0x0008
-
#define BRIDGE_INT_TIMER1_CLR (~0x0004)
#define MAIN_IRQ_CAUSE (ORION5X_BRIDGE_VIRT_BASE + 0x200)
diff --git a/arch/arm/mach-shmobile/setup-emev2.c b/arch/arm/mach-shmobile/setup-emev2.c
index 899a86c..1ccddd2 100644
--- a/arch/arm/mach-shmobile/setup-emev2.c
+++ b/arch/arm/mach-shmobile/setup-emev2.c
@@ -287,14 +287,14 @@
static struct resource gio3_resources[] = {
[0] = {
.name = "GIO_096",
- .start = 0xe0050100,
- .end = 0xe005012b,
+ .start = 0xe0050180,
+ .end = 0xe00501ab,
.flags = IORESOURCE_MEM,
},
[1] = {
.name = "GIO_096",
- .start = 0xe0050140,
- .end = 0xe005015f,
+ .start = 0xe00501c0,
+ .end = 0xe00501df,
.flags = IORESOURCE_MEM,
},
[2] = {
diff --git a/arch/arm/mach-shmobile/setup-r8a73a4.c b/arch/arm/mach-shmobile/setup-r8a73a4.c
index c5a75a7..7f45c2e 100644
--- a/arch/arm/mach-shmobile/setup-r8a73a4.c
+++ b/arch/arm/mach-shmobile/setup-r8a73a4.c
@@ -62,7 +62,7 @@
static const struct plat_sci_port scif[] = {
SCIFA_DATA(SCIFA0, 0xe6c40000, gic_spi(144)), /* SCIFA0 */
SCIFA_DATA(SCIFA1, 0xe6c50000, gic_spi(145)), /* SCIFA1 */
- SCIFB_DATA(SCIFB0, 0xe6c50000, gic_spi(145)), /* SCIFB0 */
+ SCIFB_DATA(SCIFB0, 0xe6c20000, gic_spi(148)), /* SCIFB0 */
SCIFB_DATA(SCIFB1, 0xe6c30000, gic_spi(149)), /* SCIFB1 */
SCIFB_DATA(SCIFB2, 0xe6ce0000, gic_spi(150)), /* SCIFB2 */
SCIFB_DATA(SCIFB3, 0xe6cf0000, gic_spi(151)), /* SCIFB3 */
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 4130e65..5b799c2 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -101,7 +101,7 @@
NULL
};
-MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
+DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_machine = zynq_init_machine,
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 6833cbe..15225d8 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -597,7 +597,7 @@
#ifdef CONFIG_SA1111
/* now that our DMA memory is actually so designated, we can free it */
- free_reserved_area(__va(PHYS_PFN_OFFSET), swapper_pg_dir, -1, NULL);
+ free_reserved_area(__va(PHYS_OFFSET), swapper_pg_dir, -1, NULL);
#endif
free_highpages();
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 10062ce..0c63562 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -181,11 +181,9 @@
if (mmap_is_legacy()) {
mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base(random_factor);
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index d7229d2..4f56617 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -950,7 +950,7 @@
map.virtual &= PAGE_MASK;
map.length = PAGE_SIZE;
map.type = MT_DEVICE;
- create_mapping(&map);
+ iotable_init(&map, 1);
}
#endif
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 4143d9b..9737e97 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -270,6 +270,8 @@
source "fs/Kconfig"
+source "arch/arm64/kvm/Kconfig"
+
source "arch/arm64/Kconfig.debug"
source "security/Kconfig"
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 49c162c..666e231 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -21,6 +21,7 @@
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
+#include <linux/kvm_host.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
#include <asm/cputable.h>
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
new file mode 100644
index 0000000..21e9082
--- /dev/null
+++ b/arch/arm64/kvm/Kconfig
@@ -0,0 +1,51 @@
+#
+# KVM configuration
+#
+
+source "virt/kvm/Kconfig"
+
+menuconfig VIRTUALIZATION
+ bool "Virtualization"
+ ---help---
+ Say Y here to get to see options for using your Linux host to run
+ other operating systems inside virtual machines (guests).
+ This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and
+ disabled.
+
+if VIRTUALIZATION
+
+config KVM
+ bool "Kernel-based Virtual Machine (KVM) support"
+ select MMU_NOTIFIER
+ select PREEMPT_NOTIFIERS
+ select ANON_INODES
+ select KVM_MMIO
+ select KVM_ARM_HOST
+ select KVM_ARM_VGIC
+ select KVM_ARM_TIMER
+ ---help---
+ Support hosting virtualized guest machines.
+
+ If unsure, say N.
+
+config KVM_ARM_HOST
+ bool
+ ---help---
+ Provides host support for ARM processors.
+
+config KVM_ARM_VGIC
+ bool
+ depends on KVM_ARM_HOST && OF
+ select HAVE_KVM_IRQCHIP
+ ---help---
+ Adds support for a hardware assisted, in-kernel GIC emulation.
+
+config KVM_ARM_TIMER
+ bool
+ depends on KVM_ARM_VGIC
+ ---help---
+ Adds support for the Architected Timers in virtual machines.
+
+endif # VIRTUALIZATION
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index 7c7be78..8ed6cb1 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -90,11 +90,9 @@
if (mmap_is_legacy()) {
mm->mmap_base = TASK_UNMAPPED_BASE;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base();
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index 4b597d9..d9d81c2 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -30,7 +30,6 @@
platforms += sni
platforms += txx9
platforms += vr41xx
-platforms += wrppmc
# include the platform specific files
include $(patsubst %, $(srctree)/arch/mips/%/Platform, $(platforms))
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index beeff43..4758a8f 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1,6 +1,7 @@
config MIPS
bool
default y
+ select HAVE_CONTEXT_TRACKING
select HAVE_GENERIC_DMA_COHERENT
select HAVE_IDE
select HAVE_OPROFILE
@@ -27,6 +28,7 @@
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
+ select GENERIC_PCI_IOMAP
select HAVE_ARCH_JUMP_LABEL
select ARCH_WANT_IPC_PARSE_VERSION
select IRQ_FORCED_THREADING
@@ -46,9 +48,6 @@
menu "Machine selection"
-config ZONE_DMA
- bool
-
choice
prompt "System type"
default SGI_IP22
@@ -124,11 +123,14 @@
config BCM63XX
bool "Broadcom BCM63XX based boards"
+ select BOOT_RAW
select CEVT_R4K
select CSRC_R4K
select DMA_NONCOHERENT
select IRQ_CPU
select SYS_HAS_CPU_MIPS32_R1
+ select SYS_HAS_CPU_BMIPS4350 if !BCM63XX_CPU_6338 && !BCM63XX_CPU_6345 && !BCM63XX_CPU_6348
+ select NR_CPUS_DEFAULT_2
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_HAS_EARLY_PRINTK
@@ -341,7 +343,6 @@
select DMA_NONCOHERENT
select IRQ_CPU
select IRQ_GIC
- select MIPS_CPU_SCACHE
select MIPS_MSC
select SYS_HAS_CPU_MIPS32_R1
select SYS_HAS_CPU_MIPS32_R2
@@ -420,7 +421,6 @@
select CSRC_POWERTV
select DMA_NONCOHERENT
select HW_HAS_PCI
- select SYS_HAS_EARLY_PRINTK
select SYS_HAS_CPU_MIPS32_R2
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
@@ -713,46 +713,8 @@
Support the Mikrotik(tm) RouterBoard 532 series,
based on the IDT RC32434 SoC.
-config WR_PPMC
- bool "Wind River PPMC board"
- select CEVT_R4K
- select CSRC_R4K
- select IRQ_CPU
- select BOOT_ELF32
- select DMA_NONCOHERENT
- select HW_HAS_PCI
- select PCI_GT64XXX_PCI0
- select SWAP_IO_SPACE
- select SYS_HAS_CPU_MIPS32_R1
- select SYS_HAS_CPU_MIPS32_R2
- select SYS_HAS_CPU_MIPS64_R1
- select SYS_HAS_CPU_NEVADA
- select SYS_HAS_CPU_RM7000
- select SYS_SUPPORTS_32BIT_KERNEL
- select SYS_SUPPORTS_64BIT_KERNEL
- select SYS_SUPPORTS_BIG_ENDIAN
- select SYS_SUPPORTS_LITTLE_ENDIAN
- help
- This enables support for the Wind River MIPS32 4KC PPMC evaluation
- board, which is based on GT64120 bridge chip.
-
-config CAVIUM_OCTEON_SIMULATOR
- bool "Cavium Networks Octeon Simulator"
- select CEVT_R4K
- select 64BIT_PHYS_ADDR
- select DMA_COHERENT
- select SYS_SUPPORTS_64BIT_KERNEL
- select SYS_SUPPORTS_BIG_ENDIAN
- select SYS_SUPPORTS_HOTPLUG_CPU
- select SYS_HAS_CPU_CAVIUM_OCTEON
- select HOLES_IN_ZONE
- help
- The Octeon simulator is software performance model of the Cavium
- Octeon Processor. It supports simulating Octeon processors on x86
- hardware.
-
-config CAVIUM_OCTEON_REFERENCE_BOARD
- bool "Cavium Networks Octeon reference board"
+config CAVIUM_OCTEON_SOC
+ bool "Cavium Networks Octeon SoC based boards"
select CEVT_R4K
select 64BIT_PHYS_ADDR
select DMA_COHERENT
@@ -806,6 +768,8 @@
select SYS_HAS_EARLY_PRINTK
select USB_ARCH_HAS_OHCI if USB_SUPPORT
select USB_ARCH_HAS_EHCI if USB_SUPPORT
+ select SYS_SUPPORTS_ZBOOT
+ select SYS_SUPPORTS_ZBOOT_UART16550
help
Support for systems based on Netlogic XLR and XLS processors.
Say Y here if you have a XLR or XLS based board.
@@ -832,6 +796,8 @@
select SYNC_R4K
select SYS_HAS_EARLY_PRINTK
select USE_OF
+ select SYS_SUPPORTS_ZBOOT
+ select SYS_SUPPORTS_ZBOOT_UART16550
help
This board is based on Netlogic XLP Processor.
Say Y here if you have a XLP based board.
@@ -1031,7 +997,6 @@
config CPU_LITTLE_ENDIAN
bool "Little endian"
depends on SYS_SUPPORTS_LITTLE_ENDIAN
- help
endchoice
@@ -1964,7 +1929,7 @@
config MIPS_VPE_LOADER
bool "VPE loader support."
- depends on SYS_SUPPORTS_MULTITHREADING
+ depends on SYS_SUPPORTS_MULTITHREADING && MODULES
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_IRQ_EI
select MIPS_MT
@@ -2382,6 +2347,19 @@
If unsure, say Y. Only embedded should say N here.
+config CC_STACKPROTECTOR
+ bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
+ help
+ This option turns on the -fstack-protector GCC feature. This
+ feature puts, at the beginning of functions, a canary value on
+ the stack just before the return address, and validates
+ the value just before actually returning. Stack based buffer
+ overflows (that need to overwrite this return address) now also
+ overwrite the canary, which gets detected and the attack is then
+ neutralized via a kernel panic.
+
+ This feature requires gcc version 4.2 or above.
+
config USE_OF
bool
select OF
@@ -2413,7 +2391,6 @@
bool "Support for PCI controller"
depends on HW_HAS_PCI
select PCI_DOMAINS
- select GENERIC_PCI_IOMAP
select NO_GENERIC_PCI_IOPORT_MAP
help
Find out whether you have a PCI motherboard. PCI is the name of a
@@ -2479,6 +2456,9 @@
select CLKEVT_I8253
select MIPS_EXTERNAL_TIMER
+config ZONE_DMA
+ bool
+
config ZONE_DMA32
bool
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index dd58a04..37f9ef3 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -227,6 +227,10 @@
LDFLAGS += -m $(ld-emul)
+ifdef CONFIG_CC_STACKPROTECTOR
+ KBUILD_CFLAGS += -fstack-protector
+endif
+
ifdef CONFIG_MIPS
CHECKFLAGS += $(shell $(CC) $(KBUILD_CFLAGS) -dM -E -x c /dev/null | \
egrep -vw '__GNUC_(|MINOR_|PATCHLEVEL_)_' | \
diff --git a/arch/mips/ath79/mach-ap136.c b/arch/mips/ath79/mach-ap136.c
index 479dd4b..07eac58 100644
--- a/arch/mips/ath79/mach-ap136.c
+++ b/arch/mips/ath79/mach-ap136.c
@@ -132,7 +132,7 @@
ath79_register_pci();
}
#else
-static inline void ap136_pci_init(void) {}
+static inline void ap136_pci_init(u8 *eeprom) {}
#endif /* CONFIG_PCI */
static void __init ap136_setup(void)
diff --git a/arch/mips/bcm63xx/Kconfig b/arch/mips/bcm63xx/Kconfig
index 5639662..b78306c 100644
--- a/arch/mips/bcm63xx/Kconfig
+++ b/arch/mips/bcm63xx/Kconfig
@@ -1,6 +1,10 @@
menu "CPU support"
depends on BCM63XX
+config BCM63XX_CPU_3368
+ bool "support 3368 CPU"
+ select HW_HAS_PCI
+
config BCM63XX_CPU_6328
bool "support 6328 CPU"
select HW_HAS_PCI
@@ -8,14 +12,9 @@
config BCM63XX_CPU_6338
bool "support 6338 CPU"
select HW_HAS_PCI
- select USB_ARCH_HAS_OHCI
- select USB_OHCI_BIG_ENDIAN_DESC
- select USB_OHCI_BIG_ENDIAN_MMIO
config BCM63XX_CPU_6345
bool "support 6345 CPU"
- select USB_OHCI_BIG_ENDIAN_DESC
- select USB_OHCI_BIG_ENDIAN_MMIO
config BCM63XX_CPU_6348
bool "support 6348 CPU"
diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c
index 9c0ddaf..5b974eb 100644
--- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
+++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
@@ -28,11 +28,47 @@
#include <bcm63xx_dev_usb_usbd.h>
#include <board_bcm963xx.h>
+#include <uapi/linux/bcm933xx_hcs.h>
+
#define PFX "board_bcm963xx: "
+#define HCS_OFFSET_128K 0x20000
+
static struct board_info board;
/*
+ * known 3368 boards
+ */
+#ifdef CONFIG_BCM63XX_CPU_3368
+static struct board_info __initdata board_cvg834g = {
+ .name = "CVG834G_E15R3921",
+ .expected_cpu_id = 0x3368,
+
+ .has_uart0 = 1,
+ .has_uart1 = 1,
+
+ .has_enet0 = 1,
+ .has_pci = 1,
+
+ .enet0 = {
+ .has_phy = 1,
+ .use_internal_phy = 1,
+ },
+
+ .leds = {
+ {
+ .name = "CVG834G:green:power",
+ .gpio = 37,
+ .default_trigger= "default-on",
+ },
+ },
+
+ .ephy_reset_gpio = 36,
+ .ephy_reset_gpio_flags = GPIOF_INIT_HIGH,
+};
+#endif
+
+/*
* known 6328 boards
*/
#ifdef CONFIG_BCM63XX_CPU_6328
@@ -639,6 +675,9 @@
* all boards
*/
static const struct board_info __initconst *bcm963xx_boards[] = {
+#ifdef CONFIG_BCM63XX_CPU_3368
+ &board_cvg834g,
+#endif
#ifdef CONFIG_BCM63XX_CPU_6328
&board_96328avng,
#endif
@@ -722,8 +761,9 @@
unsigned int i;
u8 *boot_addr, *cfe;
char cfe_version[32];
- char *board_name;
+ char *board_name = NULL;
u32 val;
+ struct bcm_hcs *hcs;
/* read base address of boot chip select (0)
* 6328/6362 do not have MPI but boot from a fixed address
@@ -747,7 +787,12 @@
bcm63xx_nvram_init(boot_addr + BCM963XX_NVRAM_OFFSET);
- board_name = bcm63xx_nvram_get_name();
+ if (BCMCPU_IS_3368()) {
+ hcs = (struct bcm_hcs *)boot_addr;
+ board_name = hcs->filename;
+ } else {
+ board_name = bcm63xx_nvram_get_name();
+ }
/* find board by name */
for (i = 0; i < ARRAY_SIZE(bcm963xx_boards); i++) {
if (strncmp(board_name, bcm963xx_boards[i]->name, 16))
@@ -877,5 +922,9 @@
platform_device_register(&bcm63xx_gpio_leds);
+ if (board.ephy_reset_gpio && board.ephy_reset_gpio_flags)
+ gpio_request_one(board.ephy_reset_gpio,
+ board.ephy_reset_gpio_flags, "ephy-reset");
+
return 0;
}
diff --git a/arch/mips/bcm63xx/clk.c b/arch/mips/bcm63xx/clk.c
index c726a97..43da4ae 100644
--- a/arch/mips/bcm63xx/clk.c
+++ b/arch/mips/bcm63xx/clk.c
@@ -84,7 +84,7 @@
else
clk_disable_unlocked(&clk_enet_misc);
- if (BCMCPU_IS_6358()) {
+ if (BCMCPU_IS_3368() || BCMCPU_IS_6358()) {
u32 mask;
if (clk->id == 0)
@@ -110,9 +110,8 @@
*/
static void ephy_set(struct clk *clk, int enable)
{
- if (!BCMCPU_IS_6358())
- return;
- bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable);
+ if (BCMCPU_IS_3368() || BCMCPU_IS_6358())
+ bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable);
}
@@ -155,9 +154,10 @@
*/
static void pcm_set(struct clk *clk, int enable)
{
- if (!BCMCPU_IS_6358())
- return;
- bcm_hwclock_set(CKCTL_6358_PCM_EN, enable);
+ if (BCMCPU_IS_3368())
+ bcm_hwclock_set(CKCTL_3368_PCM_EN, enable);
+ if (BCMCPU_IS_6358())
+ bcm_hwclock_set(CKCTL_6358_PCM_EN, enable);
}
static struct clk clk_pcm = {
@@ -211,7 +211,7 @@
mask = CKCTL_6338_SPI_EN;
else if (BCMCPU_IS_6348())
mask = CKCTL_6348_SPI_EN;
- else if (BCMCPU_IS_6358())
+ else if (BCMCPU_IS_3368() || BCMCPU_IS_6358())
mask = CKCTL_6358_SPI_EN;
else if (BCMCPU_IS_6362())
mask = CKCTL_6362_SPI_EN;
@@ -318,6 +318,18 @@
EXPORT_SYMBOL(clk_get_rate);
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
struct clk *clk_get(struct device *dev, const char *id)
{
if (!strcmp(id, "enet0"))
@@ -338,7 +350,7 @@
return &clk_xtm;
if (!strcmp(id, "periph"))
return &clk_periph;
- if (BCMCPU_IS_6358() && !strcmp(id, "pcm"))
+ if ((BCMCPU_IS_3368() || BCMCPU_IS_6358()) && !strcmp(id, "pcm"))
return &clk_pcm;
if ((BCMCPU_IS_6362() || BCMCPU_IS_6368()) && !strcmp(id, "ipsec"))
return &clk_ipsec;
diff --git a/arch/mips/bcm63xx/cpu.c b/arch/mips/bcm63xx/cpu.c
index 79fe32d..7e17374 100644
--- a/arch/mips/bcm63xx/cpu.c
+++ b/arch/mips/bcm63xx/cpu.c
@@ -29,6 +29,14 @@
static unsigned int bcm63xx_cpu_freq;
static unsigned int bcm63xx_memory_size;
+static const unsigned long bcm3368_regs_base[] = {
+ __GEN_CPU_REGS_TABLE(3368)
+};
+
+static const int bcm3368_irqs[] = {
+ __GEN_CPU_IRQ_TABLE(3368)
+};
+
static const unsigned long bcm6328_regs_base[] = {
__GEN_CPU_REGS_TABLE(6328)
};
@@ -116,6 +124,9 @@
static unsigned int detect_cpu_clock(void)
{
switch (bcm63xx_get_cpu_id()) {
+ case BCM3368_CPU_ID:
+ return 300000000;
+
case BCM6328_CPU_ID:
{
unsigned int tmp, mips_pll_fcvo;
@@ -266,7 +277,7 @@
banks = (val & SDRAM_CFG_BANK_MASK) ? 2 : 1;
}
- if (BCMCPU_IS_6358() || BCMCPU_IS_6368()) {
+ if (BCMCPU_IS_3368() || BCMCPU_IS_6358() || BCMCPU_IS_6368()) {
val = bcm_memc_readl(MEMC_CFG_REG);
rows = (val & MEMC_CFG_ROW_MASK) >> MEMC_CFG_ROW_SHIFT;
cols = (val & MEMC_CFG_COL_MASK) >> MEMC_CFG_COL_SHIFT;
@@ -302,10 +313,17 @@
chipid_reg = BCM_6345_PERF_BASE;
break;
case CPU_BMIPS4350:
- if ((read_c0_prid() & 0xf0) == 0x10)
+ switch ((read_c0_prid() & 0xff)) {
+ case 0x04:
+ chipid_reg = BCM_3368_PERF_BASE;
+ break;
+ case 0x10:
chipid_reg = BCM_6345_PERF_BASE;
- else
+ break;
+ default:
chipid_reg = BCM_6368_PERF_BASE;
+ break;
+ }
break;
}
@@ -322,6 +340,10 @@
bcm63xx_cpu_rev = (tmp & REV_REVID_MASK) >> REV_REVID_SHIFT;
switch (bcm63xx_cpu_id) {
+ case BCM3368_CPU_ID:
+ bcm63xx_regs_base = bcm3368_regs_base;
+ bcm63xx_irqs = bcm3368_irqs;
+ break;
case BCM6328_CPU_ID:
bcm63xx_regs_base = bcm6328_regs_base;
bcm63xx_irqs = bcm6328_irqs;
diff --git a/arch/mips/bcm63xx/dev-flash.c b/arch/mips/bcm63xx/dev-flash.c
index 588d1ec..172dd83 100644
--- a/arch/mips/bcm63xx/dev-flash.c
+++ b/arch/mips/bcm63xx/dev-flash.c
@@ -71,6 +71,7 @@
case BCM6348_CPU_ID:
/* no way to auto detect so assume parallel */
return BCM63XX_FLASH_TYPE_PARALLEL;
+ case BCM3368_CPU_ID:
case BCM6358_CPU_ID:
val = bcm_gpio_readl(GPIO_STRAPBUS_REG);
if (val & STRAPBUS_6358_BOOT_SEL_PARALLEL)
diff --git a/arch/mips/bcm63xx/dev-spi.c b/arch/mips/bcm63xx/dev-spi.c
index 3065bb6..d12daed 100644
--- a/arch/mips/bcm63xx/dev-spi.c
+++ b/arch/mips/bcm63xx/dev-spi.c
@@ -37,7 +37,8 @@
{
if (BCMCPU_IS_6338() || BCMCPU_IS_6348())
bcm63xx_regs_spi = bcm6348_regs_spi;
- if (BCMCPU_IS_6358() || BCMCPU_IS_6362() || BCMCPU_IS_6368())
+ if (BCMCPU_IS_3368() || BCMCPU_IS_6358() ||
+ BCMCPU_IS_6362() || BCMCPU_IS_6368())
bcm63xx_regs_spi = bcm6358_regs_spi;
}
#else
@@ -87,7 +88,8 @@
spi_pdata.msg_ctl_width = SPI_6348_MSG_CTL_WIDTH;
}
- if (BCMCPU_IS_6358() || BCMCPU_IS_6362() || BCMCPU_IS_6368()) {
+ if (BCMCPU_IS_3368() || BCMCPU_IS_6358() || BCMCPU_IS_6362() ||
+ BCMCPU_IS_6368()) {
spi_resources[0].end += BCM_6358_RSET_SPI_SIZE - 1;
spi_pdata.fifo_size = SPI_6358_MSG_DATA_SIZE;
spi_pdata.msg_type_shift = SPI_6358_MSG_TYPE_SHIFT;
diff --git a/arch/mips/bcm63xx/dev-uart.c b/arch/mips/bcm63xx/dev-uart.c
index d6e42c6..3bc7f3b 100644
--- a/arch/mips/bcm63xx/dev-uart.c
+++ b/arch/mips/bcm63xx/dev-uart.c
@@ -54,7 +54,8 @@
if (id >= ARRAY_SIZE(bcm63xx_uart_devices))
return -ENODEV;
- if (id == 1 && (!BCMCPU_IS_6358() && !BCMCPU_IS_6368()))
+ if (id == 1 && (!BCMCPU_IS_3368() && !BCMCPU_IS_6358() &&
+ !BCMCPU_IS_6368()))
return -ENODEV;
if (id == 0) {
diff --git a/arch/mips/bcm63xx/irq.c b/arch/mips/bcm63xx/irq.c
index c0ab388..1525f8a 100644
--- a/arch/mips/bcm63xx/irq.c
+++ b/arch/mips/bcm63xx/irq.c
@@ -27,6 +27,17 @@
static void __internal_irq_unmask_64(unsigned int irq) __maybe_unused;
#ifndef BCMCPU_RUNTIME_DETECT
+#ifdef CONFIG_BCM63XX_CPU_3368
+#define irq_stat_reg PERF_IRQSTAT_3368_REG
+#define irq_mask_reg PERF_IRQMASK_3368_REG
+#define irq_bits 32
+#define is_ext_irq_cascaded 0
+#define ext_irq_start 0
+#define ext_irq_end 0
+#define ext_irq_count 4
+#define ext_irq_cfg_reg1 PERF_EXTIRQ_CFG_REG_3368
+#define ext_irq_cfg_reg2 0
+#endif
#ifdef CONFIG_BCM63XX_CPU_6328
#define irq_stat_reg PERF_IRQSTAT_6328_REG
#define irq_mask_reg PERF_IRQMASK_6328_REG
@@ -140,6 +151,13 @@
irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
switch (bcm63xx_get_cpu_id()) {
+ case BCM3368_CPU_ID:
+ irq_stat_addr += PERF_IRQSTAT_3368_REG;
+ irq_mask_addr += PERF_IRQMASK_3368_REG;
+ irq_bits = 32;
+ ext_irq_count = 4;
+ ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368;
+ break;
case BCM6328_CPU_ID:
irq_stat_addr += PERF_IRQSTAT_6328_REG;
irq_mask_addr += PERF_IRQMASK_6328_REG;
@@ -294,6 +312,10 @@
if (cause & CAUSEF_IP7)
do_IRQ(7);
+ if (cause & CAUSEF_IP0)
+ do_IRQ(0);
+ if (cause & CAUSEF_IP1)
+ do_IRQ(1);
if (cause & CAUSEF_IP2)
dispatch_internal();
if (!is_ext_irq_cascaded) {
@@ -475,6 +497,7 @@
reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
break;
+ case BCM3368_CPU_ID:
case BCM6328_CPU_ID:
case BCM6338_CPU_ID:
case BCM6345_CPU_ID:
diff --git a/arch/mips/bcm63xx/nvram.c b/arch/mips/bcm63xx/nvram.c
index a4b8864..e652e57 100644
--- a/arch/mips/bcm63xx/nvram.c
+++ b/arch/mips/bcm63xx/nvram.c
@@ -42,6 +42,7 @@
{
unsigned int check_len;
u32 crc, expected_crc;
+ u8 hcs_mac_addr[ETH_ALEN] = { 0x00, 0x10, 0x18, 0xff, 0xff, 0xff };
/* extract nvram data */
memcpy(&nvram, addr, sizeof(nvram));
@@ -62,6 +63,15 @@
if (crc != expected_crc)
pr_warn("nvram checksum failed, contents may be invalid (expected %08x, got %08x)\n",
expected_crc, crc);
+
+ /* Cable modems have a different NVRAM which is embedded in the eCos
+ * firmware and not easily extractible, give at least a MAC address
+ * pool.
+ */
+ if (BCMCPU_IS_3368()) {
+ memcpy(nvram.mac_addr_base, hcs_mac_addr, ETH_ALEN);
+ nvram.mac_addr_count = 2;
+ }
}
u8 *bcm63xx_nvram_get_name(void)
diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
index fd69808..8ac4e09 100644
--- a/arch/mips/bcm63xx/prom.c
+++ b/arch/mips/bcm63xx/prom.c
@@ -8,7 +8,11 @@
#include <linux/init.h>
#include <linux/bootmem.h>
+#include <linux/smp.h>
#include <asm/bootinfo.h>
+#include <asm/bmips.h>
+#include <asm/smp-ops.h>
+#include <asm/mipsregs.h>
#include <bcm63xx_board.h>
#include <bcm63xx_cpu.h>
#include <bcm63xx_io.h>
@@ -26,7 +30,9 @@
bcm_wdt_writel(WDT_STOP_2, WDT_CTL_REG);
/* disable all hardware blocks clock for now */
- if (BCMCPU_IS_6328())
+ if (BCMCPU_IS_3368())
+ mask = CKCTL_3368_ALL_SAFE_EN;
+ else if (BCMCPU_IS_6328())
mask = CKCTL_6328_ALL_SAFE_EN;
else if (BCMCPU_IS_6338())
mask = CKCTL_6338_ALL_SAFE_EN;
@@ -52,6 +58,47 @@
/* do low level board init */
board_prom_init();
+
+ if (IS_ENABLED(CONFIG_CPU_BMIPS4350) && IS_ENABLED(CONFIG_SMP)) {
+ /* set up SMP */
+ register_smp_ops(&bmips_smp_ops);
+
+ /*
+ * BCM6328 might not have its second CPU enabled, while BCM6358
+ * needs special handling for its shared TLB, so disable SMP
+ * for now.
+ */
+ if (BCMCPU_IS_6328()) {
+ reg = bcm_readl(BCM_6328_OTP_BASE +
+ OTP_USER_BITS_6328_REG(3));
+
+ if (reg & OTP_6328_REG3_TP1_DISABLED)
+ bmips_smp_enabled = 0;
+ } else if (BCMCPU_IS_6358()) {
+ bmips_smp_enabled = 0;
+ }
+
+ if (!bmips_smp_enabled)
+ return;
+
+ /*
+ * The bootloader has set up the CPU1 reset vector at
+ * 0xa000_0200.
+ * This conflicts with the special interrupt vector (IV).
+ * The bootloader has also set up CPU1 to respond to the wrong
+ * IPI interrupt.
+ * Here we will start up CPU1 in the background and ask it to
+ * reconfigure itself then go back to sleep.
+ */
+ memcpy((void *)0xa0000200, &bmips_smp_movevec, 0x20);
+ __sync();
+ set_c0_cause(C_SW0);
+ cpumask_set_cpu(1, &bmips_booted_mask);
+
+ /*
+ * FIXME: we really should have some sort of hazard barrier here
+ */
+ }
}
void __init prom_free_prom_memory(void)
diff --git a/arch/mips/bcm63xx/reset.c b/arch/mips/bcm63xx/reset.c
index 317931c..acbeb1f 100644
--- a/arch/mips/bcm63xx/reset.c
+++ b/arch/mips/bcm63xx/reset.c
@@ -30,6 +30,19 @@
[BCM63XX_RESET_PCIE] = BCM## __cpu ##_RESET_PCIE, \
[BCM63XX_RESET_PCIE_EXT] = BCM## __cpu ##_RESET_PCIE_EXT,
+#define BCM3368_RESET_SPI SOFTRESET_3368_SPI_MASK
+#define BCM3368_RESET_ENET SOFTRESET_3368_ENET_MASK
+#define BCM3368_RESET_USBH 0
+#define BCM3368_RESET_USBD SOFTRESET_3368_USBS_MASK
+#define BCM3368_RESET_DSL 0
+#define BCM3368_RESET_SAR 0
+#define BCM3368_RESET_EPHY SOFTRESET_3368_EPHY_MASK
+#define BCM3368_RESET_ENETSW 0
+#define BCM3368_RESET_PCM SOFTRESET_3368_PCM_MASK
+#define BCM3368_RESET_MPI SOFTRESET_3368_MPI_MASK
+#define BCM3368_RESET_PCIE 0
+#define BCM3368_RESET_PCIE_EXT 0
+
#define BCM6328_RESET_SPI SOFTRESET_6328_SPI_MASK
#define BCM6328_RESET_ENET 0
#define BCM6328_RESET_USBH SOFTRESET_6328_USBH_MASK
@@ -117,6 +130,10 @@
/*
* core reset bits
*/
+static const u32 bcm3368_reset_bits[] = {
+ __GEN_RESET_BITS_TABLE(3368)
+};
+
static const u32 bcm6328_reset_bits[] = {
__GEN_RESET_BITS_TABLE(6328)
};
@@ -146,7 +163,10 @@
static int __init bcm63xx_reset_bits_init(void)
{
- if (BCMCPU_IS_6328()) {
+ if (BCMCPU_IS_3368()) {
+ reset_reg = PERF_SOFTRESET_6358_REG;
+ bcm63xx_reset_bits = bcm3368_reset_bits;
+ } else if (BCMCPU_IS_6328()) {
reset_reg = PERF_SOFTRESET_6328_REG;
bcm63xx_reset_bits = bcm6328_reset_bits;
} else if (BCMCPU_IS_6338()) {
@@ -170,6 +190,13 @@
}
#else
+#ifdef CONFIG_BCM63XX_CPU_3368
+static const u32 bcm63xx_reset_bits[] = {
+ __GEN_RESET_BITS_TABLE(3368)
+};
+#define reset_reg PERF_SOFTRESET_6358_REG
+#endif
+
#ifdef CONFIG_BCM63XX_CPU_6328
static const u32 bcm63xx_reset_bits[] = {
__GEN_RESET_BITS_TABLE(6328)
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
index 24a2444..6660c7d 100644
--- a/arch/mips/bcm63xx/setup.c
+++ b/arch/mips/bcm63xx/setup.c
@@ -68,6 +68,9 @@
/* mask and clear all external irq */
switch (bcm63xx_get_cpu_id()) {
+ case BCM3368_CPU_ID:
+ perf_regs[0] = PERF_EXTIRQ_CFG_REG_3368;
+ break;
case BCM6328_CPU_ID:
perf_regs[0] = PERF_EXTIRQ_CFG_REG_6328;
break;
diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile
index bbaa1d4..bb1dbf4 100644
--- a/arch/mips/boot/compressed/Makefile
+++ b/arch/mips/boot/compressed/Makefile
@@ -18,6 +18,8 @@
# Disable Function Tracer
KBUILD_CFLAGS := $(shell echo $(KBUILD_CFLAGS) | sed -e "s/-pg//")
+KBUILD_CFLAGS := $(filter-out -fstack-protector, $(KBUILD_CFLAGS))
+
KBUILD_CFLAGS := $(LINUXINCLUDE) $(KBUILD_CFLAGS) -D__KERNEL__ \
-DBOOT_HEAP_SIZE=$(BOOT_HEAP_SIZE) -D"VMLINUX_LOAD_ADDRESS_ULL=$(VMLINUX_LOAD_ADDRESS)ull"
diff --git a/arch/mips/boot/compressed/uart-16550.c b/arch/mips/boot/compressed/uart-16550.c
index 1c7b739b..c01d343 100644
--- a/arch/mips/boot/compressed/uart-16550.c
+++ b/arch/mips/boot/compressed/uart-16550.c
@@ -23,23 +23,39 @@
#define PORT(offset) (UART0_BASE + (4 * offset))
#endif
+#ifdef CONFIG_CPU_XLR
+#define UART0_BASE 0x1EF14000
+#define PORT(offset) (CKSEG1ADDR(UART0_BASE) + (4 * offset))
+#define IOTYPE unsigned int
+#endif
+
+#ifdef CONFIG_CPU_XLP
+#define UART0_BASE 0x18030100
+#define PORT(offset) (CKSEG1ADDR(UART0_BASE) + (4 * offset))
+#define IOTYPE unsigned int
+#endif
+
+#ifndef IOTYPE
+#define IOTYPE char
+#endif
+
#ifndef PORT
#error please define the serial port address for your own machine
#endif
static inline unsigned int serial_in(int offset)
{
- return *((char *)PORT(offset));
+ return *((volatile IOTYPE *)PORT(offset)) & 0xFF;
}
static inline void serial_out(int offset, int value)
{
- *((char *)PORT(offset)) = value;
+ *((volatile IOTYPE *)PORT(offset)) = value & 0xFF;
}
void putc(char c)
{
- int timeout = 1024;
+ int timeout = 1000000;
while (((serial_in(UART_LSR) & UART_LSR_THRE) == 0) && (timeout-- > 0))
;
diff --git a/arch/mips/cavium-octeon/Kconfig b/arch/mips/cavium-octeon/Kconfig
index 75a6df7..227705d 100644
--- a/arch/mips/cavium-octeon/Kconfig
+++ b/arch/mips/cavium-octeon/Kconfig
@@ -10,6 +10,10 @@
non-CN63XXP1 hardware, so it is recommended to select "n"
unless it is known the workarounds are needed.
+endif # CPU_CAVIUM_OCTEON
+
+if CAVIUM_OCTEON_SOC
+
config CAVIUM_OCTEON_2ND_KERNEL
bool "Build the kernel to be used as a 2nd kernel on the same chip"
default "n"
@@ -19,17 +23,6 @@
with this option to be run at the same time as one built without this
option.
-config CAVIUM_OCTEON_HW_FIX_UNALIGNED
- bool "Enable hardware fixups of unaligned loads and stores"
- default "y"
- help
- Configure the Octeon hardware to automatically fix unaligned loads
- and stores. Normally unaligned accesses are fixed using a kernel
- exception handler. This option enables the hardware automatic fixups,
- which requires only an extra 3 cycles. Disable this option if you
- are running code that relies on address exceptions on unaligned
- accesses.
-
config CAVIUM_OCTEON_CVMSEG_SIZE
int "Number of L1 cache lines reserved for CVMSEG memory"
range 0 54
@@ -103,4 +96,4 @@
To compile this driver as a module, choose M here. The module
will be called octeon-ilm
-endif # CPU_CAVIUM_OCTEON
+endif # CAVIUM_OCTEON_SOC
diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile
index 3595aff..4e95204 100644
--- a/arch/mips/cavium-octeon/Makefile
+++ b/arch/mips/cavium-octeon/Makefile
@@ -12,11 +12,12 @@
CFLAGS_octeon-platform.o = -I$(src)/../../../scripts/dtc/libfdt
CFLAGS_setup.o = -I$(src)/../../../scripts/dtc/libfdt
-obj-y := cpu.o setup.o serial.o octeon-platform.o octeon-irq.o csrc-octeon.o
-obj-y += dma-octeon.o flash_setup.o
+obj-y := cpu.o setup.o octeon-platform.o octeon-irq.o csrc-octeon.o
+obj-y += dma-octeon.o
obj-y += octeon-memcpy.o
obj-y += executive/
+obj-$(CONFIG_MTD) += flash_setup.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_OCTEON_ILM) += oct_ilm.o
diff --git a/arch/mips/cavium-octeon/Platform b/arch/mips/cavium-octeon/Platform
index 1e43ccf..8a301cb 100644
--- a/arch/mips/cavium-octeon/Platform
+++ b/arch/mips/cavium-octeon/Platform
@@ -1,11 +1,11 @@
#
# Cavium Octeon
#
-platform-$(CONFIG_CPU_CAVIUM_OCTEON) += cavium-octeon/
-cflags-$(CONFIG_CPU_CAVIUM_OCTEON) += \
+platform-$(CONFIG_CAVIUM_OCTEON_SOC) += cavium-octeon/
+cflags-$(CONFIG_CAVIUM_OCTEON_SOC) += \
-I$(srctree)/arch/mips/include/asm/mach-cavium-octeon
ifdef CONFIG_CAVIUM_OCTEON_2ND_KERNEL
-load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff84100000
+load-$(CONFIG_CAVIUM_OCTEON_SOC) += 0xffffffff84100000
else
-load-$(CONFIG_CPU_CAVIUM_OCTEON) += 0xffffffff81100000
+load-$(CONFIG_CAVIUM_OCTEON_SOC) += 0xffffffff81100000
endif
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c
index 7c64977..0a1283c 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-helper-board.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-board.c
@@ -181,6 +181,11 @@
return ipd_port - 16 + 4;
else
return -1;
+ case CVMX_BOARD_TYPE_UBNT_E100:
+ if (ipd_port >= 0 && ipd_port <= 2)
+ return 7 - ipd_port;
+ else
+ return -1;
}
/* Some unknown board. Somebody forgot to update this function... */
@@ -706,6 +711,14 @@
}
}
}
+ } else if (cvmx_sysinfo_get()->board_type ==
+ CVMX_BOARD_TYPE_UBNT_E100) {
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(0, interface), 0);
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(0, interface), 0x10);
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(1, interface), 0);
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(1, interface), 0x10);
+ cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(2, interface), 0);
+ cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(2, interface), 0x10);
}
return 0;
}
diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c
index 389512e..7b746e7 100644
--- a/arch/mips/cavium-octeon/octeon-platform.c
+++ b/arch/mips/cavium-octeon/octeon-platform.c
@@ -490,8 +490,15 @@
if (alias_prop) {
uart = fdt_path_offset(initial_boot_params, alias_prop);
- if (uart_mask & (1 << i))
+ if (uart_mask & (1 << i)) {
+ __be32 f;
+
+ f = cpu_to_be32(octeon_get_io_clock_rate());
+ fdt_setprop_inplace(initial_boot_params,
+ uart, "clock-frequency",
+ &f, sizeof(f));
continue;
+ }
pr_debug("Deleting uart%d\n", i);
fdt_nop_node(initial_boot_params, uart);
fdt_nop_property(initial_boot_params, aliases,
diff --git a/arch/mips/cavium-octeon/serial.c b/arch/mips/cavium-octeon/serial.c
deleted file mode 100644
index f393f65..0000000
--- a/arch/mips/cavium-octeon/serial.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2004-2007 Cavium Networks
- */
-#include <linux/console.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/serial.h>
-#include <linux/serial_8250.h>
-#include <linux/serial_reg.h>
-#include <linux/tty.h>
-#include <linux/irq.h>
-
-#include <asm/time.h>
-
-#include <asm/octeon/octeon.h>
-
-#define DEBUG_UART 1
-
-unsigned int octeon_serial_in(struct uart_port *up, int offset)
-{
- int rv = cvmx_read_csr((uint64_t)(up->membase + (offset << 3)));
- if (offset == UART_IIR && (rv & 0xf) == 7) {
- /* Busy interrupt, read the USR (39) and try again. */
- cvmx_read_csr((uint64_t)(up->membase + (39 << 3)));
- rv = cvmx_read_csr((uint64_t)(up->membase + (offset << 3)));
- }
- return rv;
-}
-
-void octeon_serial_out(struct uart_port *up, int offset, int value)
-{
- /*
- * If bits 6 or 7 of the OCTEON UART's LCR are set, it quits
- * working.
- */
- if (offset == UART_LCR)
- value &= 0x9f;
- cvmx_write_csr((uint64_t)(up->membase + (offset << 3)), (u8)value);
-}
-
-static int octeon_serial_probe(struct platform_device *pdev)
-{
- int irq, res;
- struct resource *res_mem;
- struct uart_8250_port up;
-
- /* All adaptors have an irq. */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
- memset(&up, 0, sizeof(up));
-
- up.port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
- up.port.type = PORT_OCTEON;
- up.port.iotype = UPIO_MEM;
- up.port.regshift = 3;
- up.port.dev = &pdev->dev;
-
- if (octeon_is_simulation())
- /* Make simulator output fast*/
- up.port.uartclk = 115200 * 16;
- else
- up.port.uartclk = octeon_get_io_clock_rate();
-
- up.port.serial_in = octeon_serial_in;
- up.port.serial_out = octeon_serial_out;
- up.port.irq = irq;
-
- res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res_mem == NULL) {
- dev_err(&pdev->dev, "found no memory resource\n");
- return -ENXIO;
- }
- up.port.mapbase = res_mem->start;
- up.port.membase = ioremap(res_mem->start, resource_size(res_mem));
-
- res = serial8250_register_8250_port(&up);
-
- return res >= 0 ? 0 : res;
-}
-
-static struct of_device_id octeon_serial_match[] = {
- {
- .compatible = "cavium,octeon-3860-uart",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, octeon_serial_match);
-
-static struct platform_driver octeon_serial_driver = {
- .probe = octeon_serial_probe,
- .driver = {
- .owner = THIS_MODULE,
- .name = "octeon_serial",
- .of_match_table = octeon_serial_match,
- },
-};
-
-static int __init octeon_serial_init(void)
-{
- return platform_driver_register(&octeon_serial_driver);
-}
-late_initcall(octeon_serial_init);
diff --git a/arch/mips/cavium-octeon/setup.c b/arch/mips/cavium-octeon/setup.c
index 01b1b3f..48b08eb 100644
--- a/arch/mips/cavium-octeon/setup.c
+++ b/arch/mips/cavium-octeon/setup.c
@@ -7,6 +7,7 @@
* Copyright (C) 2008, 2009 Wind River Systems
* written by Ralf Baechle <ralf@linux-mips.org>
*/
+#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/console.h>
@@ -40,12 +41,6 @@
#include <asm/octeon/pci-octeon.h>
#include <asm/octeon/cvmx-mio-defs.h>
-#ifdef CONFIG_CAVIUM_DECODE_RSL
-extern void cvmx_interrupt_rsl_decode(void);
-extern int __cvmx_interrupt_ecc_report_single_bit_errors;
-extern void cvmx_interrupt_rsl_enable(void);
-#endif
-
extern struct plat_smp_ops octeon_smp_ops;
#ifdef CONFIG_PCI
@@ -463,18 +458,6 @@
}
/**
- * Handle all the error condition interrupts that might occur.
- *
- */
-#ifdef CONFIG_CAVIUM_DECODE_RSL
-static irqreturn_t octeon_rlm_interrupt(int cpl, void *dev_id)
-{
- cvmx_interrupt_rsl_decode();
- return IRQ_HANDLED;
-}
-#endif
-
-/**
* Return a string representing the system type
*
* Returns
@@ -712,7 +695,7 @@
if (cvmx_read_csr(CVMX_L2D_FUS3) & (3ull << 34)) {
pr_info("Skipping L2 locking due to reduced L2 cache size\n");
} else {
- uint32_t ebase = read_c0_ebase() & 0x3ffff000;
+ uint32_t __maybe_unused ebase = read_c0_ebase() & 0x3ffff000;
#ifdef CONFIG_CAVIUM_OCTEON_LOCK_L2_TLB
/* TLB refill */
cvmx_l2c_lock_mem_region(ebase, 0x100);
@@ -996,7 +979,7 @@
cvmx_bootmem_unlock();
/* Add the memory region for the kernel. */
kernel_start = (unsigned long) _text;
- kernel_size = ALIGN(_end - _text, 0x100000);
+ kernel_size = _end - _text;
/* Adjust for physical offset. */
kernel_start &= ~0xffffffff80000000ULL;
@@ -1064,15 +1047,6 @@
panic("Core-14449 WAR not in place (%04x).\n"
"Please build kernel with proper options (CONFIG_CAVIUM_CN63XXP1).", insn);
}
-#ifdef CONFIG_CAVIUM_DECODE_RSL
- cvmx_interrupt_rsl_enable();
-
- /* Add an interrupt handler for general failures. */
- if (request_irq(OCTEON_IRQ_RML, octeon_rlm_interrupt, IRQF_SHARED,
- "RML/RSL", octeon_rlm_interrupt)) {
- panic("Unable to request_irq(OCTEON_IRQ_RML)");
- }
-#endif
}
int octeon_prune_device_tree(void);
diff --git a/arch/mips/configs/cavium_octeon_defconfig b/arch/mips/configs/cavium_octeon_defconfig
index 014ba4b..dace582 100644
--- a/arch/mips/configs/cavium_octeon_defconfig
+++ b/arch/mips/configs/cavium_octeon_defconfig
@@ -1,13 +1,11 @@
-CONFIG_CAVIUM_OCTEON_REFERENCE_BOARD=y
+CONFIG_CAVIUM_OCTEON_SOC=y
CONFIG_CAVIUM_CN63XXP1=y
CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE=2
-CONFIG_SPARSEMEM_MANUAL=y
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_SMP=y
CONFIG_NR_CPUS=32
CONFIG_HZ_100=y
CONFIG_PREEMPT=y
-CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
CONFIG_BSD_PROCESS_ACCT=y
@@ -50,7 +48,6 @@
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
# CONFIG_MTD_OF_PARTS is not set
-CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y
@@ -114,6 +111,7 @@
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=2
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_DW=y
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_OCTEON=y
diff --git a/arch/mips/configs/wrppmc_defconfig b/arch/mips/configs/wrppmc_defconfig
deleted file mode 100644
index 44a451b..0000000
--- a/arch/mips/configs/wrppmc_defconfig
+++ /dev/null
@@ -1,97 +0,0 @@
-CONFIG_WR_PPMC=y
-CONFIG_HZ_1000=y
-CONFIG_EXPERIMENTAL=y
-# CONFIG_SWAP is not set
-CONFIG_SYSVIPC=y
-CONFIG_BSD_PROCESS_ACCT=y
-CONFIG_LOG_BUF_SHIFT=14
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-CONFIG_EXPERT=y
-CONFIG_KALLSYMS_EXTRA_PASS=y
-# CONFIG_EPOLL is not set
-CONFIG_SLAB=y
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_PCI=y
-CONFIG_HOTPLUG_PCI=y
-CONFIG_BINFMT_MISC=y
-CONFIG_PM=y
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_XFRM_MIGRATE=y
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-CONFIG_IP_PNP_BOOTP=y
-CONFIG_IP_PNP_RARP=y
-CONFIG_IP_MROUTE=y
-CONFIG_ARPD=y
-CONFIG_INET_XFRM_MODE_TRANSPORT=m
-CONFIG_INET_XFRM_MODE_TUNNEL=m
-CONFIG_INET_XFRM_MODE_BEET=m
-CONFIG_TCP_MD5SIG=y
-# CONFIG_IPV6 is not set
-CONFIG_NETWORK_SECMARK=y
-CONFIG_FW_LOADER=m
-CONFIG_BLK_DEV_RAM=y
-CONFIG_SGI_IOC4=m
-CONFIG_NETDEVICES=y
-CONFIG_PHYLIB=y
-CONFIG_VITESSE_PHY=m
-CONFIG_SMSC_PHY=m
-CONFIG_NET_ETHERNET=y
-CONFIG_NET_PCI=y
-CONFIG_E100=y
-CONFIG_QLA3XXX=m
-CONFIG_CHELSIO_T3=m
-CONFIG_NETXEN_NIC=m
-# CONFIG_INPUT is not set
-# CONFIG_SERIO is not set
-# CONFIG_VT is not set
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=1
-CONFIG_SERIAL_8250_RUNTIME_UARTS=1
-# CONFIG_HW_RANDOM is not set
-CONFIG_PROC_KCORE=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-CONFIG_ROOT_NFS=y
-CONFIG_DLM=m
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE="console=ttyS0,115200n8"
-CONFIG_CRYPTO_NULL=m
-CONFIG_CRYPTO_CBC=m
-CONFIG_CRYPTO_ECB=m
-CONFIG_CRYPTO_LRW=m
-CONFIG_CRYPTO_PCBC=m
-CONFIG_CRYPTO_XCBC=m
-CONFIG_CRYPTO_MD4=m
-CONFIG_CRYPTO_MICHAEL_MIC=m
-CONFIG_CRYPTO_SHA256=m
-CONFIG_CRYPTO_SHA512=m
-CONFIG_CRYPTO_TGR192=m
-CONFIG_CRYPTO_WP512=m
-CONFIG_CRYPTO_ANUBIS=m
-CONFIG_CRYPTO_ARC4=m
-CONFIG_CRYPTO_BLOWFISH=m
-CONFIG_CRYPTO_CAMELLIA=m
-CONFIG_CRYPTO_CAST5=m
-CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_DES=m
-CONFIG_CRYPTO_FCRYPT=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_TWOFISH=m
-CONFIG_CRYPTO_DEFLATE=m
-CONFIG_CRC_CCITT=y
-CONFIG_CRC16=y
-CONFIG_LIBCRC32C=y
diff --git a/arch/mips/dec/Makefile b/arch/mips/dec/Makefile
index 9eb2f9c..3d5d2c5 100644
--- a/arch/mips/dec/Makefile
+++ b/arch/mips/dec/Makefile
@@ -5,6 +5,5 @@
obj-y := ecc-berr.o int-handler.o ioasic-irq.o kn01-berr.o \
kn02-irq.o kn02xa-berr.o reset.o setup.o time.o
-obj-$(CONFIG_PROM_CONSOLE) += promcon.o
obj-$(CONFIG_TC) += tc.o
obj-$(CONFIG_CPU_HAS_WB) += wbflush.o
diff --git a/arch/mips/dec/promcon.c b/arch/mips/dec/promcon.c
deleted file mode 100644
index c239c25b..0000000
--- a/arch/mips/dec/promcon.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Wrap-around code for a console using the
- * DECstation PROM io-routines.
- *
- * Copyright (c) 1998 Harald Koerfgen
- */
-
-#include <linux/tty.h>
-#include <linux/ptrace.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/fs.h>
-
-#include <asm/dec/prom.h>
-
-static void prom_console_write(struct console *co, const char *s,
- unsigned count)
-{
- unsigned i;
-
- /*
- * Now, do each character
- */
- for (i = 0; i < count; i++) {
- if (*s == 10)
- prom_printf("%c", 13);
- prom_printf("%c", *s++);
- }
-}
-
-static int __init prom_console_setup(struct console *co, char *options)
-{
- return 0;
-}
-
-static struct console sercons = {
- .name = "ttyS",
- .write = prom_console_write,
- .setup = prom_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-
-/*
- * Register console.
- */
-
-static int __init prom_console_init(void)
-{
- register_console(&sercons);
-
- return 0;
-}
-console_initcall(prom_console_init);
diff --git a/arch/mips/fw/cfe/cfe_api.c b/arch/mips/fw/cfe/cfe_api.c
index d06dc5a6..cf84f01 100644
--- a/arch/mips/fw/cfe/cfe_api.c
+++ b/arch/mips/fw/cfe/cfe_api.c
@@ -406,12 +406,12 @@
return xiocb.xiocb_status;
}
-int cfe_write(int handle, unsigned char *buffer, int length)
+int cfe_write(int handle, const char *buffer, int length)
{
return cfe_writeblk(handle, 0, buffer, length);
}
-int cfe_writeblk(int handle, s64 offset, unsigned char *buffer, int length)
+int cfe_writeblk(int handle, s64 offset, const char *buffer, int length)
{
struct cfe_xiocb xiocb;
diff --git a/arch/mips/include/asm/cop2.h b/arch/mips/include/asm/cop2.h
index 3532e2c..c1516cc 100644
--- a/arch/mips/include/asm/cop2.h
+++ b/arch/mips/include/asm/cop2.h
@@ -11,6 +11,35 @@
#include <linux/notifier.h>
+#if defined(CONFIG_CPU_CAVIUM_OCTEON)
+
+extern void octeon_cop2_save(struct octeon_cop2_state *);
+extern void octeon_cop2_restore(struct octeon_cop2_state *);
+
+#define cop2_save(r) octeon_cop2_save(r)
+#define cop2_restore(r) octeon_cop2_restore(r)
+
+#define cop2_present 1
+#define cop2_lazy_restore 1
+
+#elif defined(CONFIG_CPU_XLP)
+
+extern void nlm_cop2_save(struct nlm_cop2_state *);
+extern void nlm_cop2_restore(struct nlm_cop2_state *);
+#define cop2_save(r) nlm_cop2_save(r)
+#define cop2_restore(r) nlm_cop2_restore(r)
+
+#define cop2_present 1
+#define cop2_lazy_restore 0
+
+#else
+
+#define cop2_present 0
+#define cop2_lazy_restore 0
+#define cop2_save(r)
+#define cop2_restore(r)
+#endif
+
enum cu2_ops {
CU2_EXCEPTION,
CU2_LWC2_OP,
diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h
index e5ec8fc..1dc0860 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -24,6 +24,16 @@
#ifndef cpu_has_tlb
#define cpu_has_tlb (cpu_data[0].options & MIPS_CPU_TLB)
#endif
+
+/*
+ * For the moment we don't consider R6000 and R8000 so we can assume that
+ * anything that doesn't support R4000-style exceptions and interrupts is
+ * R3000-like. Users should still treat these two macro definitions as
+ * opaque.
+ */
+#ifndef cpu_has_3kex
+#define cpu_has_3kex (!cpu_has_4kex)
+#endif
#ifndef cpu_has_4kex
#define cpu_has_4kex (cpu_data[0].options & MIPS_CPU_4KEX)
#endif
@@ -87,19 +97,23 @@
#define cpu_has_mips16 (cpu_data[0].ases & MIPS_ASE_MIPS16)
#endif
#ifndef cpu_has_mdmx
-#define cpu_has_mdmx (cpu_data[0].ases & MIPS_ASE_MDMX)
+#define cpu_has_mdmx (cpu_data[0].ases & MIPS_ASE_MDMX)
#endif
#ifndef cpu_has_mips3d
-#define cpu_has_mips3d (cpu_data[0].ases & MIPS_ASE_MIPS3D)
+#define cpu_has_mips3d (cpu_data[0].ases & MIPS_ASE_MIPS3D)
#endif
#ifndef cpu_has_smartmips
-#define cpu_has_smartmips (cpu_data[0].ases & MIPS_ASE_SMARTMIPS)
+#define cpu_has_smartmips (cpu_data[0].ases & MIPS_ASE_SMARTMIPS)
#endif
#ifndef cpu_has_rixi
#define cpu_has_rixi (cpu_data[0].options & MIPS_CPU_RIXI)
#endif
#ifndef cpu_has_mmips
-#define cpu_has_mmips (cpu_data[0].options & MIPS_CPU_MICROMIPS)
+# ifdef CONFIG_SYS_SUPPORTS_MICROMIPS
+# define cpu_has_mmips (cpu_data[0].options & MIPS_CPU_MICROMIPS)
+# else
+# define cpu_has_mmips 0
+# endif
#endif
#ifndef cpu_has_vtag_icache
#define cpu_has_vtag_icache (cpu_data[0].icache.flags & MIPS_CACHE_VTAG)
@@ -111,7 +125,7 @@
#define cpu_has_ic_fills_f_dc (cpu_data[0].icache.flags & MIPS_CACHE_IC_F_DC)
#endif
#ifndef cpu_has_pindexed_dcache
-#define cpu_has_pindexed_dcache (cpu_data[0].dcache.flags & MIPS_CACHE_PINDEX)
+#define cpu_has_pindexed_dcache (cpu_data[0].dcache.flags & MIPS_CACHE_PINDEX)
#endif
#ifndef cpu_has_local_ebase
#define cpu_has_local_ebase 1
@@ -136,7 +150,6 @@
#endif
#endif
-# define cpu_has_mips_1 (cpu_data[0].isa_level & MIPS_CPU_ISA_I)
#ifndef cpu_has_mips_2
# define cpu_has_mips_2 (cpu_data[0].isa_level & MIPS_CPU_ISA_II)
#endif
@@ -149,18 +162,18 @@
#ifndef cpu_has_mips_5
# define cpu_has_mips_5 (cpu_data[0].isa_level & MIPS_CPU_ISA_V)
#endif
-# ifndef cpu_has_mips32r1
+#ifndef cpu_has_mips32r1
# define cpu_has_mips32r1 (cpu_data[0].isa_level & MIPS_CPU_ISA_M32R1)
-# endif
-# ifndef cpu_has_mips32r2
+#endif
+#ifndef cpu_has_mips32r2
# define cpu_has_mips32r2 (cpu_data[0].isa_level & MIPS_CPU_ISA_M32R2)
-# endif
-# ifndef cpu_has_mips64r1
+#endif
+#ifndef cpu_has_mips64r1
# define cpu_has_mips64r1 (cpu_data[0].isa_level & MIPS_CPU_ISA_M64R1)
-# endif
-# ifndef cpu_has_mips64r2
+#endif
+#ifndef cpu_has_mips64r2
# define cpu_has_mips64r2 (cpu_data[0].isa_level & MIPS_CPU_ISA_M64R2)
-# endif
+#endif
/*
* Shortcuts ...
@@ -182,9 +195,9 @@
* has CLO and CLZ but not DCLO nor DCLZ. For 64-bit kernels
* cpu_has_clo_clz also indicates the availability of DCLO and DCLZ.
*/
-# ifndef cpu_has_clo_clz
-# define cpu_has_clo_clz cpu_has_mips_r
-# endif
+#ifndef cpu_has_clo_clz
+#define cpu_has_clo_clz cpu_has_mips_r
+#endif
#ifndef cpu_has_dsp
#define cpu_has_dsp (cpu_data[0].ases & MIPS_ASE_DSP)
@@ -210,7 +223,7 @@
# define cpu_has_64bits (cpu_data[0].isa_level & MIPS_CPU_ISA_64BIT)
# endif
# ifndef cpu_has_64bit_zero_reg
-# define cpu_has_64bit_zero_reg (cpu_data[0].isa_level & MIPS_CPU_ISA_64BIT)
+# define cpu_has_64bit_zero_reg (cpu_data[0].isa_level & MIPS_CPU_ISA_64BIT)
# endif
# ifndef cpu_has_64bit_gp_regs
# define cpu_has_64bit_gp_regs 0
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index dd86ab2..632bbe5 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -282,18 +282,17 @@
* ISA Level encodings
*
*/
-#define MIPS_CPU_ISA_I 0x00000001
-#define MIPS_CPU_ISA_II 0x00000002
-#define MIPS_CPU_ISA_III 0x00000004
-#define MIPS_CPU_ISA_IV 0x00000008
-#define MIPS_CPU_ISA_V 0x00000010
-#define MIPS_CPU_ISA_M32R1 0x00000020
-#define MIPS_CPU_ISA_M32R2 0x00000040
-#define MIPS_CPU_ISA_M64R1 0x00000080
-#define MIPS_CPU_ISA_M64R2 0x00000100
+#define MIPS_CPU_ISA_II 0x00000001
+#define MIPS_CPU_ISA_III 0x00000002
+#define MIPS_CPU_ISA_IV 0x00000004
+#define MIPS_CPU_ISA_V 0x00000008
+#define MIPS_CPU_ISA_M32R1 0x00000010
+#define MIPS_CPU_ISA_M32R2 0x00000020
+#define MIPS_CPU_ISA_M64R1 0x00000040
+#define MIPS_CPU_ISA_M64R2 0x00000080
-#define MIPS_CPU_ISA_32BIT (MIPS_CPU_ISA_I | MIPS_CPU_ISA_II | \
- MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M32R2)
+#define MIPS_CPU_ISA_32BIT (MIPS_CPU_ISA_II | MIPS_CPU_ISA_M32R1 | \
+ MIPS_CPU_ISA_M32R2)
#define MIPS_CPU_ISA_64BIT (MIPS_CPU_ISA_III | MIPS_CPU_ISA_IV | \
MIPS_CPU_ISA_V | MIPS_CPU_ISA_M64R1 | MIPS_CPU_ISA_M64R2)
diff --git a/arch/mips/include/asm/fw/cfe/cfe_api.h b/arch/mips/include/asm/fw/cfe/cfe_api.h
index 1734755..a0ea69e 100644
--- a/arch/mips/include/asm/fw/cfe/cfe_api.h
+++ b/arch/mips/include/asm/fw/cfe/cfe_api.h
@@ -115,8 +115,8 @@
int cfe_readblk(int handle, int64_t offset, unsigned char *buffer,
int length);
int cfe_setenv(char *name, char *val);
-int cfe_write(int handle, unsigned char *buffer, int length);
-int cfe_writeblk(int handle, int64_t offset, unsigned char *buffer,
+int cfe_write(int handle, const char *buffer, int length);
+int cfe_writeblk(int handle, int64_t offset, const char *buffer,
int length);
#endif /* CFE_API_H */
diff --git a/arch/mips/include/asm/gic.h b/arch/mips/include/asm/gic.h
index 7153b32..b2e3e93 100644
--- a/arch/mips/include/asm/gic.h
+++ b/arch/mips/include/asm/gic.h
@@ -347,7 +347,7 @@
#define GIC_CPU_INT2 2 /* . */
#define GIC_CPU_INT3 3 /* . */
#define GIC_CPU_INT4 4 /* . */
-#define GIC_CPU_INT5 5 /* Core Interrupt 5 */
+#define GIC_CPU_INT5 5 /* Core Interrupt 7 */
/* Local GIC interrupts. */
#define GIC_INT_TMR (GIC_CPU_INT5)
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index b7e5985..3321dd5 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -170,6 +170,11 @@
extern void __iomem * __ioremap(phys_t offset, phys_t size, unsigned long flags);
extern void __iounmap(const volatile void __iomem *addr);
+#ifndef CONFIG_PCI
+struct pci_dev;
+static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {}
+#endif
+
static inline void __iomem * __ioremap_mode(phys_t offset, unsigned long size,
unsigned long flags)
{
@@ -449,6 +454,11 @@
#define readl_relaxed readl
#define readq_relaxed readq
+#define writeb_relaxed writeb
+#define writew_relaxed writew
+#define writel_relaxed writel
+#define writeq_relaxed writeq
+
#define readb_be(addr) \
__raw_readb((__force unsigned *)(addr))
#define readw_be(addr) \
diff --git a/arch/mips/include/asm/kspd.h b/arch/mips/include/asm/kspd.h
deleted file mode 100644
index ec68329..0000000
--- a/arch/mips/include/asm/kspd.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved.
- *
- * This program is free software; you can distribute 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 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 _ASM_KSPD_H
-#define _ASM_KSPD_H
-
-struct kspd_notifications {
- void (*kspd_sp_exit)(int sp_id);
-
- struct list_head list;
-};
-
-static inline void kspd_notify(struct kspd_notifications *notify)
-{
-}
-
-#endif
diff --git a/arch/mips/include/asm/mach-ar7/spaces.h b/arch/mips/include/asm/mach-ar7/spaces.h
index ac28f27..660ab64 100644
--- a/arch/mips/include/asm/mach-ar7/spaces.h
+++ b/arch/mips/include/asm/mach-ar7/spaces.h
@@ -14,8 +14,11 @@
* This handles the memory map.
* We handle pages at KSEG0 for kernels with 32 bit address space.
*/
-#define PAGE_OFFSET 0x94000000UL
-#define PHYS_OFFSET 0x14000000UL
+#define PAGE_OFFSET _AC(0x94000000, UL)
+#define PHYS_OFFSET _AC(0x14000000, UL)
+
+#define UNCAC_BASE _AC(0xb4000000, UL) /* 0xa0000000 + PHYS_OFFSET */
+#define IO_BASE UNCAC_BASE
#include <asm/mach-generic/spaces.h>
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
index e6e65dc..19f9134 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
@@ -9,6 +9,7 @@
* compile time if only one CPU support is enabled (idea stolen from
* arm mach-types)
*/
+#define BCM3368_CPU_ID 0x3368
#define BCM6328_CPU_ID 0x6328
#define BCM6338_CPU_ID 0x6338
#define BCM6345_CPU_ID 0x6345
@@ -22,6 +23,19 @@
u8 bcm63xx_get_cpu_rev(void);
unsigned int bcm63xx_get_cpu_freq(void);
+#ifdef CONFIG_BCM63XX_CPU_3368
+# ifdef bcm63xx_get_cpu_id
+# undef bcm63xx_get_cpu_id
+# define bcm63xx_get_cpu_id() __bcm63xx_get_cpu_id()
+# define BCMCPU_RUNTIME_DETECT
+# else
+# define bcm63xx_get_cpu_id() BCM3368_CPU_ID
+# endif
+# define BCMCPU_IS_3368() (bcm63xx_get_cpu_id() == BCM3368_CPU_ID)
+#else
+# define BCMCPU_IS_3368() (0)
+#endif
+
#ifdef CONFIG_BCM63XX_CPU_6328
# ifdef bcm63xx_get_cpu_id
# undef bcm63xx_get_cpu_id
@@ -194,6 +208,53 @@
#define RSET_RNG_SIZE 20
/*
+ * 3368 register sets base address
+ */
+#define BCM_3368_DSL_LMEM_BASE (0xdeadbeef)
+#define BCM_3368_PERF_BASE (0xfff8c000)
+#define BCM_3368_TIMER_BASE (0xfff8c040)
+#define BCM_3368_WDT_BASE (0xfff8c080)
+#define BCM_3368_UART0_BASE (0xfff8c100)
+#define BCM_3368_UART1_BASE (0xfff8c120)
+#define BCM_3368_GPIO_BASE (0xfff8c080)
+#define BCM_3368_SPI_BASE (0xfff8c800)
+#define BCM_3368_HSSPI_BASE (0xdeadbeef)
+#define BCM_3368_UDC0_BASE (0xdeadbeef)
+#define BCM_3368_USBDMA_BASE (0xdeadbeef)
+#define BCM_3368_OHCI0_BASE (0xdeadbeef)
+#define BCM_3368_OHCI_PRIV_BASE (0xdeadbeef)
+#define BCM_3368_USBH_PRIV_BASE (0xdeadbeef)
+#define BCM_3368_USBD_BASE (0xdeadbeef)
+#define BCM_3368_MPI_BASE (0xfff80000)
+#define BCM_3368_PCMCIA_BASE (0xfff80054)
+#define BCM_3368_PCIE_BASE (0xdeadbeef)
+#define BCM_3368_SDRAM_REGS_BASE (0xdeadbeef)
+#define BCM_3368_DSL_BASE (0xdeadbeef)
+#define BCM_3368_UBUS_BASE (0xdeadbeef)
+#define BCM_3368_ENET0_BASE (0xfff98000)
+#define BCM_3368_ENET1_BASE (0xfff98800)
+#define BCM_3368_ENETDMA_BASE (0xfff99800)
+#define BCM_3368_ENETDMAC_BASE (0xfff99900)
+#define BCM_3368_ENETDMAS_BASE (0xfff99a00)
+#define BCM_3368_ENETSW_BASE (0xdeadbeef)
+#define BCM_3368_EHCI0_BASE (0xdeadbeef)
+#define BCM_3368_SDRAM_BASE (0xdeadbeef)
+#define BCM_3368_MEMC_BASE (0xfff84000)
+#define BCM_3368_DDR_BASE (0xdeadbeef)
+#define BCM_3368_M2M_BASE (0xdeadbeef)
+#define BCM_3368_ATM_BASE (0xdeadbeef)
+#define BCM_3368_XTM_BASE (0xdeadbeef)
+#define BCM_3368_XTMDMA_BASE (0xdeadbeef)
+#define BCM_3368_XTMDMAC_BASE (0xdeadbeef)
+#define BCM_3368_XTMDMAS_BASE (0xdeadbeef)
+#define BCM_3368_PCM_BASE (0xfff9c200)
+#define BCM_3368_PCMDMA_BASE (0xdeadbeef)
+#define BCM_3368_PCMDMAC_BASE (0xdeadbeef)
+#define BCM_3368_PCMDMAS_BASE (0xdeadbeef)
+#define BCM_3368_RNG_BASE (0xdeadbeef)
+#define BCM_3368_MISC_BASE (0xdeadbeef)
+
+/*
* 6328 register sets base address
*/
#define BCM_6328_DSL_LMEM_BASE (0xdeadbeef)
@@ -238,6 +299,8 @@
#define BCM_6328_PCMDMAS_BASE (0xdeadbeef)
#define BCM_6328_RNG_BASE (0xdeadbeef)
#define BCM_6328_MISC_BASE (0xb0001800)
+#define BCM_6328_OTP_BASE (0xb0000600)
+
/*
* 6338 register sets base address
*/
@@ -623,6 +686,9 @@
#ifdef BCMCPU_RUNTIME_DETECT
return bcm63xx_regs_base[set];
#else
+#ifdef CONFIG_BCM63XX_CPU_3368
+ __GEN_RSET(3368)
+#endif
#ifdef CONFIG_BCM63XX_CPU_6328
__GEN_RSET(6328)
#endif
@@ -690,6 +756,52 @@
};
/*
+ * 3368 irqs
+ */
+#define BCM_3368_TIMER_IRQ (IRQ_INTERNAL_BASE + 0)
+#define BCM_3368_SPI_IRQ (IRQ_INTERNAL_BASE + 1)
+#define BCM_3368_UART0_IRQ (IRQ_INTERNAL_BASE + 2)
+#define BCM_3368_UART1_IRQ (IRQ_INTERNAL_BASE + 3)
+#define BCM_3368_DSL_IRQ 0
+#define BCM_3368_UDC0_IRQ 0
+#define BCM_3368_OHCI0_IRQ 0
+#define BCM_3368_ENET0_IRQ (IRQ_INTERNAL_BASE + 8)
+#define BCM_3368_ENET1_IRQ (IRQ_INTERNAL_BASE + 6)
+#define BCM_3368_ENET_PHY_IRQ (IRQ_INTERNAL_BASE + 9)
+#define BCM_3368_ENET0_RXDMA_IRQ (IRQ_INTERNAL_BASE + 15)
+#define BCM_3368_ENET0_TXDMA_IRQ (IRQ_INTERNAL_BASE + 16)
+#define BCM_3368_HSSPI_IRQ 0
+#define BCM_3368_EHCI0_IRQ 0
+#define BCM_3368_USBD_IRQ 0
+#define BCM_3368_USBD_RXDMA0_IRQ 0
+#define BCM_3368_USBD_TXDMA0_IRQ 0
+#define BCM_3368_USBD_RXDMA1_IRQ 0
+#define BCM_3368_USBD_TXDMA1_IRQ 0
+#define BCM_3368_USBD_RXDMA2_IRQ 0
+#define BCM_3368_USBD_TXDMA2_IRQ 0
+#define BCM_3368_ENET1_RXDMA_IRQ (IRQ_INTERNAL_BASE + 17)
+#define BCM_3368_ENET1_TXDMA_IRQ (IRQ_INTERNAL_BASE + 18)
+#define BCM_3368_PCI_IRQ (IRQ_INTERNAL_BASE + 31)
+#define BCM_3368_PCMCIA_IRQ 0
+#define BCM_3368_ATM_IRQ 0
+#define BCM_3368_ENETSW_RXDMA0_IRQ 0
+#define BCM_3368_ENETSW_RXDMA1_IRQ 0
+#define BCM_3368_ENETSW_RXDMA2_IRQ 0
+#define BCM_3368_ENETSW_RXDMA3_IRQ 0
+#define BCM_3368_ENETSW_TXDMA0_IRQ 0
+#define BCM_3368_ENETSW_TXDMA1_IRQ 0
+#define BCM_3368_ENETSW_TXDMA2_IRQ 0
+#define BCM_3368_ENETSW_TXDMA3_IRQ 0
+#define BCM_3368_XTM_IRQ 0
+#define BCM_3368_XTM_DMA0_IRQ 0
+
+#define BCM_3368_EXT_IRQ0 (IRQ_INTERNAL_BASE + 25)
+#define BCM_3368_EXT_IRQ1 (IRQ_INTERNAL_BASE + 26)
+#define BCM_3368_EXT_IRQ2 (IRQ_INTERNAL_BASE + 27)
+#define BCM_3368_EXT_IRQ3 (IRQ_INTERNAL_BASE + 28)
+
+
+/*
* 6328 irqs
*/
#define BCM_6328_HIGH_IRQ_BASE (IRQ_INTERNAL_BASE + 32)
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_gpio.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_gpio.h
index 35baa1a..565ff36 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_gpio.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_gpio.h
@@ -11,6 +11,7 @@
switch (bcm63xx_get_cpu_id()) {
case BCM6328_CPU_ID:
return 32;
+ case BCM3368_CPU_ID:
case BCM6358_CPU_ID:
return 40;
case BCM6338_CPU_ID:
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index eff7ca7..9875db3 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -15,6 +15,39 @@
/* Clock Control register */
#define PERF_CKCTL_REG 0x4
+#define CKCTL_3368_MAC_EN (1 << 3)
+#define CKCTL_3368_TC_EN (1 << 5)
+#define CKCTL_3368_US_TOP_EN (1 << 6)
+#define CKCTL_3368_DS_TOP_EN (1 << 7)
+#define CKCTL_3368_APM_EN (1 << 8)
+#define CKCTL_3368_SPI_EN (1 << 9)
+#define CKCTL_3368_USBS_EN (1 << 10)
+#define CKCTL_3368_BMU_EN (1 << 11)
+#define CKCTL_3368_PCM_EN (1 << 12)
+#define CKCTL_3368_NTP_EN (1 << 13)
+#define CKCTL_3368_ACP_B_EN (1 << 14)
+#define CKCTL_3368_ACP_A_EN (1 << 15)
+#define CKCTL_3368_EMUSB_EN (1 << 17)
+#define CKCTL_3368_ENET0_EN (1 << 18)
+#define CKCTL_3368_ENET1_EN (1 << 19)
+#define CKCTL_3368_USBU_EN (1 << 20)
+#define CKCTL_3368_EPHY_EN (1 << 21)
+
+#define CKCTL_3368_ALL_SAFE_EN (CKCTL_3368_MAC_EN | \
+ CKCTL_3368_TC_EN | \
+ CKCTL_3368_US_TOP_EN | \
+ CKCTL_3368_DS_TOP_EN | \
+ CKCTL_3368_APM_EN | \
+ CKCTL_3368_SPI_EN | \
+ CKCTL_3368_USBS_EN | \
+ CKCTL_3368_BMU_EN | \
+ CKCTL_3368_PCM_EN | \
+ CKCTL_3368_NTP_EN | \
+ CKCTL_3368_ACP_B_EN | \
+ CKCTL_3368_ACP_A_EN | \
+ CKCTL_3368_EMUSB_EN | \
+ CKCTL_3368_USBU_EN)
+
#define CKCTL_6328_PHYMIPS_EN (1 << 0)
#define CKCTL_6328_ADSL_QPROC_EN (1 << 1)
#define CKCTL_6328_ADSL_AFE_EN (1 << 2)
@@ -181,6 +214,7 @@
#define SYS_PLL_SOFT_RESET 0x1
/* Interrupt Mask register */
+#define PERF_IRQMASK_3368_REG 0xc
#define PERF_IRQMASK_6328_REG 0x20
#define PERF_IRQMASK_6338_REG 0xc
#define PERF_IRQMASK_6345_REG 0xc
@@ -190,6 +224,7 @@
#define PERF_IRQMASK_6368_REG 0x20
/* Interrupt Status register */
+#define PERF_IRQSTAT_3368_REG 0x10
#define PERF_IRQSTAT_6328_REG 0x28
#define PERF_IRQSTAT_6338_REG 0x10
#define PERF_IRQSTAT_6345_REG 0x10
@@ -199,6 +234,7 @@
#define PERF_IRQSTAT_6368_REG 0x28
/* External Interrupt Configuration register */
+#define PERF_EXTIRQ_CFG_REG_3368 0x14
#define PERF_EXTIRQ_CFG_REG_6328 0x18
#define PERF_EXTIRQ_CFG_REG_6338 0x14
#define PERF_EXTIRQ_CFG_REG_6345 0x14
@@ -236,6 +272,13 @@
#define PERF_SOFTRESET_6362_REG 0x10
#define PERF_SOFTRESET_6368_REG 0x10
+#define SOFTRESET_3368_SPI_MASK (1 << 0)
+#define SOFTRESET_3368_ENET_MASK (1 << 2)
+#define SOFTRESET_3368_MPI_MASK (1 << 3)
+#define SOFTRESET_3368_EPHY_MASK (1 << 6)
+#define SOFTRESET_3368_USBS_MASK (1 << 11)
+#define SOFTRESET_3368_PCM_MASK (1 << 13)
+
#define SOFTRESET_6328_SPI_MASK (1 << 0)
#define SOFTRESET_6328_EPHY_MASK (1 << 1)
#define SOFTRESET_6328_SAR_MASK (1 << 2)
@@ -1370,7 +1413,7 @@
#define SPI_6348_RX_DATA 0x80
#define SPI_6348_RX_DATA_SIZE 0x3f
-/* BCM 6358/6262/6368 SPI core */
+/* BCM 3368/6358/6262/6368 SPI core */
#define SPI_6358_MSG_CTL 0x00 /* 16-bits register */
#define SPI_6358_MSG_CTL_WIDTH 16
#define SPI_6358_MSG_DATA 0x02
@@ -1511,4 +1554,11 @@
#define PCIE_DEVICE_OFFSET 0x8000
+/*************************************************************************
+ * _REG relative to RSET_OTP
+ *************************************************************************/
+
+#define OTP_USER_BITS_6328_REG(i) (0x20 + (i) * 4)
+#define OTP_6328_REG3_TP1_DISABLED BIT(9)
+
#endif /* BCM63XX_REGS_H_ */
diff --git a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
index d9aee1a..b86a0ef 100644
--- a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
+++ b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h
@@ -47,6 +47,12 @@
/* GPIO LEDs */
struct gpio_led leds[5];
+
+ /* External PHY reset GPIO */
+ unsigned int ephy_reset_gpio;
+
+ /* External PHY reset GPIO flags from gpio.h */
+ unsigned long ephy_reset_gpio_flags;
};
#endif /* ! BOARD_BCM963XX_H_ */
diff --git a/arch/mips/include/asm/mach-bcm63xx/ioremap.h b/arch/mips/include/asm/mach-bcm63xx/ioremap.h
index 94e3011..ff15e3b 100644
--- a/arch/mips/include/asm/mach-bcm63xx/ioremap.h
+++ b/arch/mips/include/asm/mach-bcm63xx/ioremap.h
@@ -11,6 +11,10 @@
static inline int is_bcm63xx_internal_registers(phys_t offset)
{
switch (bcm63xx_get_cpu_id()) {
+ case BCM3368_CPU_ID:
+ if (offset >= 0xfff80000)
+ return 1;
+ break;
case BCM6338_CPU_ID:
case BCM6345_CPU_ID:
case BCM6348_CPU_ID:
diff --git a/arch/mips/include/asm/mach-cavium-octeon/dma-coherence.h b/arch/mips/include/asm/mach-cavium-octeon/dma-coherence.h
index be8fb42..47fb247 100644
--- a/arch/mips/include/asm/mach-cavium-octeon/dma-coherence.h
+++ b/arch/mips/include/asm/mach-cavium-octeon/dma-coherence.h
@@ -13,6 +13,8 @@
#ifndef __ASM_MACH_CAVIUM_OCTEON_DMA_COHERENCE_H
#define __ASM_MACH_CAVIUM_OCTEON_DMA_COHERENCE_H
+#include <linux/bug.h>
+
struct device;
extern void octeon_pci_dma_init(void);
@@ -21,18 +23,21 @@
size_t size)
{
BUG();
+ return 0;
}
static inline dma_addr_t plat_map_dma_mem_page(struct device *dev,
struct page *page)
{
BUG();
+ return 0;
}
static inline unsigned long plat_dma_addr_to_phys(struct device *dev,
dma_addr_t dma_addr)
{
BUG();
+ return 0;
}
static inline void plat_unmap_dma_mem(struct device *dev, dma_addr_t dma_addr,
@@ -44,6 +49,7 @@
static inline int plat_dma_supported(struct device *dev, u64 mask)
{
BUG();
+ return 0;
}
static inline void plat_extra_sync_for_device(struct device *dev)
@@ -60,6 +66,7 @@
dma_addr_t dma_addr)
{
BUG();
+ return 0;
}
dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
diff --git a/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h b/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
index 1e7dbb1..1668ee5 100644
--- a/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
@@ -34,15 +34,10 @@
ori v0, CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE
dmtc0 v0, CP0_CVMMEMCTL_REG # Write the cavium mem control register
dmfc0 v0, CP0_CVMCTL_REG # Read the cavium control register
-#ifdef CONFIG_CAVIUM_OCTEON_HW_FIX_UNALIGNED
# Disable unaligned load/store support but leave HW fixup enabled
+ # Needed for octeon specific memcpy
or v0, v0, 0x5001
xor v0, v0, 0x1001
-#else
- # Disable unaligned load/store and HW fixup support
- or v0, v0, 0x5001
- xor v0, v0, 0x5001
-#endif
# Read the processor ID register
mfc0 v1, CP0_PRID_REG
# Disable instruction prefetching (Octeon Pass1 errata)
diff --git a/arch/mips/include/asm/mach-cavium-octeon/spaces.h b/arch/mips/include/asm/mach-cavium-octeon/spaces.h
new file mode 100644
index 0000000..daa91ac
--- /dev/null
+++ b/arch/mips/include/asm/mach-cavium-octeon/spaces.h
@@ -0,0 +1,24 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium, Inc.
+ */
+#ifndef _ASM_MACH_CAVIUM_OCTEON_SPACES_H
+#define _ASM_MACH_CAVIUM_OCTEON_SPACES_H
+
+#include <linux/const.h>
+
+#ifdef CONFIG_64BIT
+/* They are all the same and some OCTEON II cores cannot handle 0xa8.. */
+#define CAC_BASE _AC(0x8000000000000000, UL)
+#define UNCAC_BASE _AC(0x8000000000000000, UL)
+#define IO_BASE _AC(0x8000000000000000, UL)
+
+
+#endif /* CONFIG_64BIT */
+
+#include <asm/mach-generic/spaces.h>
+
+#endif /* _ASM_MACH_CAVIUM_OCTEON_SPACES_H */
diff --git a/arch/mips/include/asm/mach-generic/dma-coherence.h b/arch/mips/include/asm/mach-generic/dma-coherence.h
index fe23034..74cb992 100644
--- a/arch/mips/include/asm/mach-generic/dma-coherence.h
+++ b/arch/mips/include/asm/mach-generic/dma-coherence.h
@@ -66,4 +66,16 @@
#endif
}
+#ifdef CONFIG_SWIOTLB
+static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+ return paddr;
+}
+
+static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
+{
+ return daddr;
+}
+#endif
+
#endif /* __ASM_MACH_GENERIC_DMA_COHERENCE_H */
diff --git a/arch/mips/include/asm/mach-generic/kernel-entry-init.h b/arch/mips/include/asm/mach-generic/kernel-entry-init.h
index 7e66505..13b0751 100644
--- a/arch/mips/include/asm/mach-generic/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-generic/kernel-entry-init.h
@@ -12,8 +12,8 @@
/* Intentionally empty macro, used in head.S. Override in
* arch/mips/mach-xxx/kernel-entry-init.h when necessary.
*/
-.macro kernel_entry_setup
-.endm
+ .macro kernel_entry_setup
+ .endm
/*
* Do SMP slave processor setup necessary before we can savely execute C code.
diff --git a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
index a323efb..b087cb8 100644
--- a/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-ip27/kernel-entry-init.h
@@ -24,6 +24,53 @@
.endm
/*
+ * TLB bits
+ */
+#define PAGE_GLOBAL (1 << 6)
+#define PAGE_VALID (1 << 7)
+#define PAGE_DIRTY (1 << 8)
+#define CACHE_CACHABLE_COW (5 << 9)
+
+ /*
+ * inputs are the text nasid in t1, data nasid in t2.
+ */
+ .macro MAPPED_KERNEL_SETUP_TLB
+#ifdef CONFIG_MAPPED_KERNEL
+ /*
+ * This needs to read the nasid - assume 0 for now.
+ * Drop in 0xffffffffc0000000 in tlbhi, 0+VG in tlblo_0,
+ * 0+DVG in tlblo_1.
+ */
+ dli t0, 0xffffffffc0000000
+ dmtc0 t0, CP0_ENTRYHI
+ li t0, 0x1c000 # Offset of text into node memory
+ dsll t1, NASID_SHFT # Shift text nasid into place
+ dsll t2, NASID_SHFT # Same for data nasid
+ or t1, t1, t0 # Physical load address of kernel text
+ or t2, t2, t0 # Physical load address of kernel data
+ dsrl t1, 12 # 4K pfn
+ dsrl t2, 12 # 4K pfn
+ dsll t1, 6 # Get pfn into place
+ dsll t2, 6 # Get pfn into place
+ li t0, ((PAGE_GLOBAL | PAGE_VALID | CACHE_CACHABLE_COW) >> 6)
+ or t0, t0, t1
+ mtc0 t0, CP0_ENTRYLO0 # physaddr, VG, cach exlwr
+ li t0, ((PAGE_GLOBAL | PAGE_VALID | PAGE_DIRTY | CACHE_CACHABLE_COW) >> 6)
+ or t0, t0, t2
+ mtc0 t0, CP0_ENTRYLO1 # physaddr, DVG, cach exlwr
+ li t0, 0x1ffe000 # MAPPED_KERN_TLBMASK, TLBPGMASK_16M
+ mtc0 t0, CP0_PAGEMASK
+ li t0, 0 # KMAP_INX
+ mtc0 t0, CP0_INDEX
+ li t0, 1
+ mtc0 t0, CP0_WIRED
+ tlbwi
+#else
+ mtc0 zero, CP0_WIRED
+#endif
+ .endm
+
+/*
* Intentionally empty macro, used in head.S. Override in
* arch/mips/mach-xxx/kernel-entry-init.h when necessary.
*/
diff --git a/arch/mips/include/asm/mach-ip28/spaces.h b/arch/mips/include/asm/mach-ip28/spaces.h
index 5edf05d..5d6a764 100644
--- a/arch/mips/include/asm/mach-ip28/spaces.h
+++ b/arch/mips/include/asm/mach-ip28/spaces.h
@@ -11,11 +11,14 @@
#ifndef _ASM_MACH_IP28_SPACES_H
#define _ASM_MACH_IP28_SPACES_H
-#define CAC_BASE 0xa800000000000000
+#define CAC_BASE _AC(0xa800000000000000, UL)
-#define HIGHMEM_START (~0UL)
+#define HIGHMEM_START (~0UL)
-#define PHYS_OFFSET _AC(0x20000000, UL)
+#define PHYS_OFFSET _AC(0x20000000, UL)
+
+#define UNCAC_BASE _AC(0xc0000000, UL) /* 0xa0000000 + PHYS_OFFSET */
+#define IO_BASE UNCAC_BASE
#include <asm/mach-generic/spaces.h>
diff --git a/arch/mips/include/asm/mach-pmcs-msp71xx/gpio.h b/arch/mips/include/asm/mach-pmcs-msp71xx/gpio.h
deleted file mode 100644
index ebdbab9..0000000
--- a/arch/mips/include/asm/mach-pmcs-msp71xx/gpio.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * include/asm-mips/pmc-sierra/msp71xx/gpio.h
- *
- * 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.
- *
- * @author Patrick Glass <patrickglass@gmail.com>
- */
-
-#ifndef __PMC_MSP71XX_GPIO_H
-#define __PMC_MSP71XX_GPIO_H
-
-/* Max number of gpio's is 28 on chip plus 3 banks of I2C IO Expanders */
-#define ARCH_NR_GPIOS (28 + (3 * 8))
-
-/* new generic GPIO API - see Documentation/gpio.txt */
-#include <asm-generic/gpio.h>
-
-#define gpio_get_value __gpio_get_value
-#define gpio_set_value __gpio_set_value
-#define gpio_cansleep __gpio_cansleep
-
-/* Setup calls for the gpio and gpio extended */
-extern void msp71xx_init_gpio(void);
-extern void msp71xx_init_gpio_extended(void);
-extern int msp71xx_set_output_drive(unsigned gpio, int value);
-
-/* Custom output drive functionss */
-static inline int gpio_set_output_drive(unsigned gpio, int value)
-{
- return msp71xx_set_output_drive(gpio, value);
-}
-
-/* IRQ's are not supported for gpio lines */
-static inline int gpio_to_irq(unsigned gpio)
-{
- return -EINVAL;
-}
-
-static inline int irq_to_gpio(unsigned irq)
-{
- return -EINVAL;
-}
-
-#endif /* __PMC_MSP71XX_GPIO_H */
diff --git a/arch/mips/include/asm/mach-wrppmc/mach-gt64120.h b/arch/mips/include/asm/mach-wrppmc/mach-gt64120.h
deleted file mode 100644
index 00fa368..0000000
--- a/arch/mips/include/asm/mach-wrppmc/mach-gt64120.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * This is a direct copy of the ev96100.h file, with a global
- * search and replace. The numbers are the same.
- *
- * The reason I'm duplicating this is so that the 64120/96100
- * defines won't be confusing in the source code.
- */
-#ifndef __ASM_MIPS_GT64120_H
-#define __ASM_MIPS_GT64120_H
-
-/*
- * This is the CPU physical memory map of PPMC Board:
- *
- * 0x00000000-0x03FFFFFF - 64MB SDRAM (SCS[0]#)
- * 0x1C000000-0x1C000000 - LED (CS0)
- * 0x1C800000-0x1C800007 - UART 16550 port (CS1)
- * 0x1F000000-0x1F000000 - MailBox (CS3)
- * 0x1FC00000-0x20000000 - 4MB Flash (BOOT CS)
- */
-
-#define WRPPMC_SDRAM_SCS0_BASE 0x00000000
-#define WRPPMC_SDRAM_SCS0_SIZE 0x04000000
-
-#define WRPPMC_UART16550_BASE 0x1C800000
-#define WRPPMC_UART16550_CLOCK 3686400 /* 3.68MHZ */
-
-#define WRPPMC_LED_BASE 0x1C000000
-#define WRPPMC_MBOX_BASE 0x1F000000
-
-#define WRPPMC_BOOTROM_BASE 0x1FC00000
-#define WRPPMC_BOOTROM_SIZE 0x00400000 /* 4M Flash */
-
-#define WRPPMC_MIPS_TIMER_IRQ 7 /* MIPS compare/count timer interrupt */
-#define WRPPMC_UART16550_IRQ 6
-#define WRPPMC_PCI_INTA_IRQ 3
-
-/*
- * PCI Bus I/O and Memory resources allocation
- *
- * NOTE: We only have PCI_0 hose interface
- */
-#define GT_PCI_MEM_BASE 0x13000000UL
-#define GT_PCI_MEM_SIZE 0x02000000UL
-#define GT_PCI_IO_BASE 0x11000000UL
-#define GT_PCI_IO_SIZE 0x02000000UL
-
-/*
- * PCI interrupts will come in on either the INTA or INTD interrupt lines,
- * which are mapped to the #2 and #5 interrupt pins of the MIPS. On our
- * boards, they all either come in on IntD or they all come in on IntA, they
- * aren't mixed. There can be numerous PCI interrupts, so we keep a list of the
- * "requested" interrupt numbers and go through the list whenever we get an
- * IntA/D.
- *
- * Interrupts < 8 are directly wired to the processor; PCI INTA is 8 and
- * INTD is 11.
- */
-#define GT_TIMER 4
-#define GT_INTA 2
-#define GT_INTD 5
-
-#ifndef __ASSEMBLY__
-
-/*
- * GT64120 internal register space base address
- */
-extern unsigned long gt64120_base;
-
-#define GT64120_BASE (gt64120_base)
-
-/* define WRPPMC_EARLY_DEBUG to enable early output something to UART */
-#undef WRPPMC_EARLY_DEBUG
-
-#ifdef WRPPMC_EARLY_DEBUG
-extern void wrppmc_led_on(int mask);
-extern void wrppmc_led_off(int mask);
-extern void wrppmc_early_printk(const char *fmt, ...);
-#else
-#define wrppmc_early_printk(fmt, ...) do {} while (0)
-#endif /* WRPPMC_EARLY_DEBUG */
-
-#endif /* __ASSEMBLY__ */
-#endif /* __ASM_MIPS_GT64120_H */
diff --git a/arch/mips/include/asm/mach-wrppmc/war.h b/arch/mips/include/asm/mach-wrppmc/war.h
deleted file mode 100644
index e86084c..0000000
--- a/arch/mips/include/asm/mach-wrppmc/war.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org>
- */
-#ifndef __ASM_MIPS_MACH_WRPPMC_WAR_H
-#define __ASM_MIPS_MACH_WRPPMC_WAR_H
-
-#define R4600_V1_INDEX_ICACHEOP_WAR 0
-#define R4600_V1_HIT_CACHEOP_WAR 0
-#define R4600_V2_HIT_CACHEOP_WAR 0
-#define R5432_CP0_INTERRUPT_WAR 0
-#define BCM1250_M3_WAR 0
-#define SIBYTE_1956_WAR 0
-#define MIPS4K_ICACHE_REFILL_WAR 0
-#define MIPS_CACHE_SYNC_WAR 0
-#define TX49XX_ICACHE_INDEX_INV_WAR 0
-#define ICACHE_REFILLS_WORKAROUND_WAR 1
-#define R10000_LLSC_WAR 0
-#define MIPS34K_MISSED_ITLB_WAR 0
-
-#endif /* __ASM_MIPS_MACH_WRPPMC_WAR_H */
diff --git a/arch/mips/include/asm/mips-boards/generic.h b/arch/mips/include/asm/mips-boards/generic.h
index bd9746f..4861681 100644
--- a/arch/mips/include/asm/mips-boards/generic.h
+++ b/arch/mips/include/asm/mips-boards/generic.h
@@ -24,12 +24,6 @@
#define ASCII_DISPLAY_POS_BASE 0x1f000418
/*
- * Reset register.
- */
-#define SOFTRES_REG 0x1f000500
-#define GORESET 0x42
-
-/*
* Revision register.
*/
#define MIPS_REVISION_REG 0x1fc00010
diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index 87e6207..fed1c3e 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -596,7 +596,7 @@
#define MIPS_CONF3_RXI (_ULCAST_(1) << 12)
#define MIPS_CONF3_ULRI (_ULCAST_(1) << 13)
#define MIPS_CONF3_ISA (_ULCAST_(3) << 14)
-#define MIPS_CONF3_ISA_OE (_ULCAST_(3) << 16)
+#define MIPS_CONF3_ISA_OE (_ULCAST_(1) << 16)
#define MIPS_CONF3_VZ (_ULCAST_(1) << 23)
#define MIPS_CONF4_MMUSIZEEXT (_ULCAST_(255) << 0)
diff --git a/arch/mips/include/asm/mmu_context.h b/arch/mips/include/asm/mmu_context.h
index 516e6e9..3b29079 100644
--- a/arch/mips/include/asm/mmu_context.h
+++ b/arch/mips/include/asm/mmu_context.h
@@ -28,11 +28,7 @@
#define TLBMISS_HANDLER_SETUP_PGD(pgd) \
do { \
- void (*tlbmiss_handler_setup_pgd)(unsigned long); \
- extern u32 tlbmiss_handler_setup_pgd_array[16]; \
- \
- tlbmiss_handler_setup_pgd = \
- (__typeof__(tlbmiss_handler_setup_pgd)) tlbmiss_handler_setup_pgd_array; \
+ extern void tlbmiss_handler_setup_pgd(unsigned long); \
tlbmiss_handler_setup_pgd((unsigned long)(pgd)); \
} while (0)
diff --git a/arch/mips/include/asm/netlogic/common.h b/arch/mips/include/asm/netlogic/common.h
index aef560a..bb68c33 100644
--- a/arch/mips/include/asm/netlogic/common.h
+++ b/arch/mips/include/asm/netlogic/common.h
@@ -39,11 +39,17 @@
* Common SMP definitions
*/
#define RESET_VEC_PHYS 0x1fc00000
+#define RESET_VEC_SIZE 8192 /* 8KB reset code and data */
#define RESET_DATA_PHYS (RESET_VEC_PHYS + (1<<10))
+
+/* Offsets of parameters in the RESET_DATA_PHYS area */
#define BOOT_THREAD_MODE 0
#define BOOT_NMI_LOCK 4
#define BOOT_NMI_HANDLER 8
+/* CPU ready flags for each CPU */
+#define BOOT_CPU_READY 2048
+
#ifndef __ASSEMBLY__
#include <linux/cpumask.h>
#include <linux/spinlock.h>
@@ -59,23 +65,32 @@
void nlm_rmiboot_preboot(void);
void nlm_percpu_init(int hwcpuid);
+static inline void *
+nlm_get_boot_data(int offset)
+{
+ return (void *)(CKSEG1ADDR(RESET_DATA_PHYS) + offset);
+}
+
static inline void
nlm_set_nmi_handler(void *handler)
{
- char *reset_data;
+ void *nmih = nlm_get_boot_data(BOOT_NMI_HANDLER);
- reset_data = (char *)CKSEG1ADDR(RESET_DATA_PHYS);
- *(int64_t *)(reset_data + BOOT_NMI_HANDLER) = (long)handler;
+ *(int64_t *)nmih = (long)handler;
}
/*
* Misc.
*/
+void nlm_init_boot_cpu(void);
unsigned int nlm_get_cpu_frequency(void);
void nlm_node_init(int node);
extern struct plat_smp_ops nlm_smp_ops;
extern char nlm_reset_entry[], nlm_reset_entry_end[];
+/* SWIOTLB */
+extern struct dma_map_ops nlm_swiotlb_dma_ops;
+
extern unsigned int nlm_threads_per_core;
extern cpumask_t nlm_cpumask;
diff --git a/arch/mips/include/asm/netlogic/xlp-hal/pic.h b/arch/mips/include/asm/netlogic/xlp-hal/pic.h
index a981f46..4b5108d 100644
--- a/arch/mips/include/asm/netlogic/xlp-hal/pic.h
+++ b/arch/mips/include/asm/netlogic/xlp-hal/pic.h
@@ -315,7 +315,7 @@
{
uint64_t ipi;
- ipi = (nmi << 31) | (irq << 20);
+ ipi = ((uint64_t)nmi << 31) | (irq << 20);
ipi |= ((hwt >> 4) << 16) | (1 << (hwt & 0xf)); /* cpuset and mask */
nlm_write_pic_reg(base, PIC_IPI_CTL, ipi);
}
diff --git a/arch/mips/include/asm/netlogic/xlp-hal/xlp.h b/arch/mips/include/asm/netlogic/xlp-hal/xlp.h
index 7e47209..f4ea0f7 100644
--- a/arch/mips/include/asm/netlogic/xlp-hal/xlp.h
+++ b/arch/mips/include/asm/netlogic/xlp-hal/xlp.h
@@ -59,6 +59,7 @@
void xlp_mmu_init(void);
void nlm_hal_init(void);
+void *xlp_dt_init(void *fdtp);
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_NLM_XLP_H */
diff --git a/arch/mips/include/asm/netlogic/xlr/fmn.h b/arch/mips/include/asm/netlogic/xlr/fmn.h
index 2a78929..5604db3 100644
--- a/arch/mips/include/asm/netlogic/xlr/fmn.h
+++ b/arch/mips/include/asm/netlogic/xlr/fmn.h
@@ -175,6 +175,10 @@
#define nlm_write_c2_cc14(s, v) __write_32bit_c2_register($30, s, v)
#define nlm_write_c2_cc15(s, v) __write_32bit_c2_register($31, s, v)
+#define nlm_read_c2_status0() __read_32bit_c2_register($2, 0)
+#define nlm_write_c2_status0(v) __write_32bit_c2_register($2, 0, v)
+#define nlm_read_c2_status1() __read_32bit_c2_register($2, 1)
+#define nlm_write_c2_status1(v) __write_32bit_c2_register($2, 1, v)
#define nlm_read_c2_status(sel) __read_32bit_c2_register($2, 0)
#define nlm_read_c2_config() __read_32bit_c2_register($3, 0)
#define nlm_write_c2_config(v) __write_32bit_c2_register($3, 0, v)
@@ -237,7 +241,7 @@
/*
* Disable interrupts and enable COP2 access
*/
-static inline uint32_t nlm_cop2_enable(void)
+static inline uint32_t nlm_cop2_enable_irqsave(void)
{
uint32_t sr = read_c0_status();
@@ -245,7 +249,7 @@
return sr;
}
-static inline void nlm_cop2_restore(uint32_t sr)
+static inline void nlm_cop2_disable_irqrestore(uint32_t sr)
{
write_c0_status(sr);
}
@@ -296,7 +300,7 @@
*/
for (i = 0; i < 8; i++) {
nlm_msgsnd(dest);
- status = nlm_read_c2_status(0);
+ status = nlm_read_c2_status0();
if ((status & 0x2) == 1)
pr_info("Send pending fail!\n");
if ((status & 0x4) == 0)
@@ -316,7 +320,7 @@
/* wait for load pending to clear */
do {
- status = nlm_read_c2_status(1);
+ status = nlm_read_c2_status0();
} while ((status & 0x08) != 0);
/* receive error bits */
diff --git a/arch/mips/include/asm/octeon/cvmx-bootinfo.h b/arch/mips/include/asm/octeon/cvmx-bootinfo.h
index 284fa8d..7b7818d 100644
--- a/arch/mips/include/asm/octeon/cvmx-bootinfo.h
+++ b/arch/mips/include/asm/octeon/cvmx-bootinfo.h
@@ -227,6 +227,7 @@
* use any numbers in this range.
*/
CVMX_BOARD_TYPE_CUST_PRIVATE_MIN = 20001,
+ CVMX_BOARD_TYPE_UBNT_E100 = 20002,
CVMX_BOARD_TYPE_CUST_PRIVATE_MAX = 30000,
/* The remaining range is reserved for future use. */
@@ -325,6 +326,7 @@
/* Customer private range */
ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_CUST_PRIVATE_MIN)
+ ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_UBNT_E100)
ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_CUST_PRIVATE_MAX)
}
return "Unsupported Board";
diff --git a/arch/mips/include/asm/page.h b/arch/mips/include/asm/page.h
index f59552f..f6be474 100644
--- a/arch/mips/include/asm/page.h
+++ b/arch/mips/include/asm/page.h
@@ -205,10 +205,8 @@
#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
-#define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE + \
- PHYS_OFFSET)
-#define CAC_ADDR(addr) ((addr) - UNCAC_BASE + PAGE_OFFSET - \
- PHYS_OFFSET)
+#define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE)
+#define CAC_ADDR(addr) ((addr) - UNCAC_BASE + PAGE_OFFSET)
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h
index b8e24fd..fa8e0aa 100644
--- a/arch/mips/include/asm/pci.h
+++ b/arch/mips/include/asm/pci.h
@@ -52,7 +52,6 @@
/*
* Used by boards to register their PCI busses before the actual scanning.
*/
-extern struct pci_controller * alloc_pci_controller(void);
extern void register_pci_controller(struct pci_controller *hose);
/*
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index 1470b7b..3605b84 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -137,7 +137,7 @@
struct mips3264_watch_reg_state mips3264;
};
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
+#if defined(CONFIG_CPU_CAVIUM_OCTEON)
struct octeon_cop2_state {
/* DMFC2 rt, 0x0201 */
@@ -182,13 +182,26 @@
/* DMFC2 rt, 0x025A; DMFC2 rt, 0x025B - Pass2 */
unsigned long cop2_gfm_result[2];
};
-#define INIT_OCTEON_COP2 {0,}
+#define COP2_INIT \
+ .cp2 = {0,},
struct octeon_cvmseg_state {
unsigned long cvmseg[CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE]
[cpu_dcache_line_size() / sizeof(unsigned long)];
};
+#elif defined(CONFIG_CPU_XLP)
+struct nlm_cop2_state {
+ u64 rx[4];
+ u64 tx[4];
+ u32 tx_msg_status;
+ u32 rx_msg_status;
+};
+
+#define COP2_INIT \
+ .cp2 = {{0}, {0}, 0, 0},
+#else
+#define COP2_INIT
#endif
typedef struct {
@@ -231,8 +244,11 @@
unsigned long cp0_baduaddr; /* Last kernel fault accessing USEG */
unsigned long error_code;
#ifdef CONFIG_CPU_CAVIUM_OCTEON
- struct octeon_cop2_state cp2 __attribute__ ((__aligned__(128)));
- struct octeon_cvmseg_state cvmseg __attribute__ ((__aligned__(128)));
+ struct octeon_cop2_state cp2 __attribute__ ((__aligned__(128)));
+ struct octeon_cvmseg_state cvmseg __attribute__ ((__aligned__(128)));
+#endif
+#ifdef CONFIG_CPU_XLP
+ struct nlm_cop2_state cp2;
#endif
struct mips_abi *abi;
};
@@ -245,13 +261,6 @@
#define FPAFF_INIT
#endif /* CONFIG_MIPS_MT_FPAFF */
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
-#define OCTEON_INIT \
- .cp2 = INIT_OCTEON_COP2,
-#else
-#define OCTEON_INIT
-#endif /* CONFIG_CPU_CAVIUM_OCTEON */
-
#define INIT_THREAD { \
/* \
* Saved main processor registers \
@@ -300,9 +309,9 @@
.cp0_baduaddr = 0, \
.error_code = 0, \
/* \
- * Cavium Octeon specifics (null if not Octeon) \
+ * Platform specific cop2 registers(null if no COP2) \
*/ \
- OCTEON_INIT \
+ COP2_INIT \
}
struct task_struct;
diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h
index a89d1b1..23fc95e 100644
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -70,6 +70,14 @@
#ifndef CONFIG_CPU_HAS_SMARTMIPS
LONG_S v1, PT_LO(sp)
#endif
+#ifdef CONFIG_CPU_CAVIUM_OCTEON
+ /*
+ * The Octeon multiplier state is affected by general
+ * multiply instructions. It must be saved before and
+ * kernel code might corrupt it
+ */
+ jal octeon_mult_save
+#endif
.endm
.macro SAVE_STATIC
@@ -218,17 +226,8 @@
ori $28, sp, _THREAD_MASK
xori $28, _THREAD_MASK
#ifdef CONFIG_CPU_CAVIUM_OCTEON
- .set mips64
- pref 0, 0($28) /* Prefetch the current pointer */
- pref 0, PT_R31(sp) /* Prefetch the $31(ra) */
- /* The Octeon multiplier state is affected by general multiply
- instructions. It must be saved before and kernel code might
- corrupt it */
- jal octeon_mult_save
- LONG_L v1, 0($28) /* Load the current pointer */
- /* Restore $31(ra) that was changed by the jal */
- LONG_L ra, PT_R31(sp)
- pref 0, 0(v1) /* Prefetch the current thread */
+ .set mips64
+ pref 0, 0($28) /* Prefetch the current pointer */
#endif
.set pop
.endm
@@ -248,6 +247,10 @@
.endm
.macro RESTORE_TEMP
+#ifdef CONFIG_CPU_CAVIUM_OCTEON
+ /* Restore the Octeon multiplier state */
+ jal octeon_mult_restore
+#endif
#ifdef CONFIG_CPU_HAS_SMARTMIPS
LONG_L $24, PT_ACX(sp)
mtlhx $24
@@ -360,10 +363,6 @@
DVPE 5 # dvpe a1
jal mips_ihb
#endif /* CONFIG_MIPS_MT_SMTC */
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
- /* Restore the Octeon multiplier state */
- jal octeon_mult_restore
-#endif
mfc0 a0, CP0_STATUS
ori a0, STATMASK
xori a0, STATMASK
diff --git a/arch/mips/include/asm/stackprotector.h b/arch/mips/include/asm/stackprotector.h
new file mode 100644
index 0000000..eb9b103
--- /dev/null
+++ b/arch/mips/include/asm/stackprotector.h
@@ -0,0 +1,40 @@
+/*
+ * GCC stack protector support.
+ *
+ * (This is directly adopted from the ARM implementation)
+ *
+ * Stack protector works by putting predefined pattern at the start of
+ * the stack frame and verifying that it hasn't been overwritten when
+ * returning from the function. The pattern is called stack canary
+ * and gcc expects it to be defined by a global variable called
+ * "__stack_chk_guard" on MIPS. This unfortunately means that on SMP
+ * we cannot have a different canary value per task.
+ */
+
+#ifndef _ASM_STACKPROTECTOR_H
+#define _ASM_STACKPROTECTOR_H 1
+
+#include <linux/random.h>
+#include <linux/version.h>
+
+extern unsigned long __stack_chk_guard;
+
+/*
+ * Initialize the stackprotector canary value.
+ *
+ * NOTE: this must only be called from functions that never return,
+ * and it must always be inlined.
+ */
+static __always_inline void boot_init_stack_canary(void)
+{
+ unsigned long canary;
+
+ /* Try to get a semi random initial value. */
+ get_random_bytes(&canary, sizeof(canary));
+ canary ^= LINUX_VERSION_CODE;
+
+ current->stack_canary = canary;
+ __stack_chk_guard = current->stack_canary;
+}
+
+#endif /* _ASM_STACKPROTECTOR_H */
diff --git a/arch/mips/include/asm/switch_to.h b/arch/mips/include/asm/switch_to.h
index fd16bcb..eb0af15 100644
--- a/arch/mips/include/asm/switch_to.h
+++ b/arch/mips/include/asm/switch_to.h
@@ -15,6 +15,7 @@
#include <asm/cpu-features.h>
#include <asm/watch.h>
#include <asm/dsp.h>
+#include <asm/cop2.h>
struct task_struct;
@@ -66,10 +67,18 @@
#define switch_to(prev, next, last) \
do { \
- u32 __usedfpu; \
+ u32 __usedfpu, __c0_stat; \
__mips_mt_fpaff_switch_to(prev); \
if (cpu_has_dsp) \
__save_dsp(prev); \
+ if (cop2_present && (KSTK_STATUS(prev) & ST0_CU2)) { \
+ if (cop2_lazy_restore) \
+ KSTK_STATUS(prev) &= ~ST0_CU2; \
+ __c0_stat = read_c0_status(); \
+ write_c0_status(__c0_stat | ST0_CU2); \
+ cop2_save(&prev->thread.cp2); \
+ write_c0_status(__c0_stat & ~ST0_CU2); \
+ } \
__clear_software_ll_bit(); \
__usedfpu = test_and_clear_tsk_thread_flag(prev, TIF_USEDFPU); \
(last) = resume(prev, next, task_thread_info(next), __usedfpu); \
@@ -77,6 +86,14 @@
#define finish_arch_switch(prev) \
do { \
+ u32 __c0_stat; \
+ if (cop2_present && !cop2_lazy_restore && \
+ (KSTK_STATUS(current) & ST0_CU2)) { \
+ __c0_stat = read_c0_status(); \
+ write_c0_status(__c0_stat | ST0_CU2); \
+ cop2_restore(¤t->thread.cp2); \
+ write_c0_status(__c0_stat & ~ST0_CU2); \
+ } \
if (cpu_has_dsp) \
__restore_dsp(current); \
if (cpu_has_userlocal) \
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 895320e..61215a3 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -109,6 +109,7 @@
#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */
#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
+#define TIF_NOHZ 19 /* in adaptive nohz mode */
#define TIF_FIXADE 20 /* Fix address errors in software */
#define TIF_LOGADE 21 /* Log address errors to syslog */
#define TIF_32BIT_REGS 22 /* also implies 16/32 fprs */
@@ -124,6 +125,7 @@
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_USEDFPU (1<<TIF_USEDFPU)
+#define _TIF_NOHZ (1<<TIF_NOHZ)
#define _TIF_FIXADE (1<<TIF_FIXADE)
#define _TIF_LOGADE (1<<TIF_LOGADE)
#define _TIF_32BIT_REGS (1<<TIF_32BIT_REGS)
@@ -131,14 +133,19 @@
#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH)
+#define _TIF_WORK_SYSCALL_ENTRY (_TIF_NOHZ | _TIF_SYSCALL_TRACE | \
+ _TIF_SYSCALL_AUDIT)
+
/* work to do in syscall_trace_leave() */
-#define _TIF_WORK_SYSCALL_EXIT (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT)
+#define _TIF_WORK_SYSCALL_EXIT (_TIF_NOHZ | _TIF_SYSCALL_TRACE | \
+ _TIF_SYSCALL_AUDIT)
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \
(_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME)
/* work to do on any return to u-space */
-#define _TIF_ALLWORK_MASK (_TIF_WORK_MASK | _TIF_WORK_SYSCALL_EXIT)
+#define _TIF_ALLWORK_MASK (_TIF_NOHZ | _TIF_WORK_MASK | \
+ _TIF_WORK_SYSCALL_EXIT)
#endif /* __KERNEL__ */
diff --git a/arch/mips/include/asm/xtalk/xtalk.h b/arch/mips/include/asm/xtalk/xtalk.h
index 680e7ef..26d2ed1 100644
--- a/arch/mips/include/asm/xtalk/xtalk.h
+++ b/arch/mips/include/asm/xtalk/xtalk.h
@@ -47,6 +47,15 @@
#define XIO_PORT(x) ((xwidgetnum_t)(((x)&XIO_PORT_BITS) >> XIO_PORT_SHIFT))
#define XIO_PACK(p, o) ((((uint64_t)(p))<<XIO_PORT_SHIFT) | ((o)&XIO_ADDR_BITS))
+#ifdef CONFIG_PCI
+extern int bridge_probe(nasid_t nasid, int widget, int masterwid);
+#else
+static inline int bridge_probe(nasid_t nasid, int widget, int masterwid)
+{
+ return 0;
+}
+#endif
+
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_XTALK_XTALK_H */
diff --git a/arch/mips/include/uapi/asm/fcntl.h b/arch/mips/include/uapi/asm/fcntl.h
index 0bda78f..6ca432f 100644
--- a/arch/mips/include/uapi/asm/fcntl.h
+++ b/arch/mips/include/uapi/asm/fcntl.h
@@ -5,9 +5,10 @@
*
* Copyright (C) 1995, 96, 97, 98, 99, 2003, 05 Ralf Baechle
*/
-#ifndef _ASM_FCNTL_H
-#define _ASM_FCNTL_H
+#ifndef _UAPI_ASM_FCNTL_H
+#define _UAPI_ASM_FCNTL_H
+#include <asm/sgidefs.h>
#define O_APPEND 0x0008
#define O_DSYNC 0x0010 /* used to be O_SYNC, see below */
@@ -55,14 +56,15 @@
* contain all the same fields as struct flock.
*/
-#ifdef CONFIG_32BIT
+#if _MIPS_SIM != _MIPS_SIM_ABI64
+
#include <linux/types.h>
struct flock {
short l_type;
short l_whence;
- off_t l_start;
- off_t l_len;
+ __kernel_off_t l_start;
+ __kernel_off_t l_len;
long l_sysid;
__kernel_pid_t l_pid;
long pad[4];
@@ -70,8 +72,8 @@
#define HAVE_ARCH_STRUCT_FLOCK
-#endif /* CONFIG_32BIT */
+#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
#include <asm-generic/fcntl.h>
-#endif /* _ASM_FCNTL_H */
+#endif /* _UAPI_ASM_FCNTL_H */
diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
index 0f4aec2..e5a676e 100644
--- a/arch/mips/include/uapi/asm/inst.h
+++ b/arch/mips/include/uapi/asm/inst.h
@@ -409,10 +409,11 @@
enum mm_16c_minor_op {
mm_lwm16_op = 0x04,
mm_swm16_op = 0x05,
- mm_jr16_op = 0x18,
- mm_jrc_op = 0x1a,
- mm_jalr16_op = 0x1c,
- mm_jalrs16_op = 0x1e,
+ mm_jr16_op = 0x0c,
+ mm_jrc_op = 0x0d,
+ mm_jalr16_op = 0x0e,
+ mm_jalrs16_op = 0x0f,
+ mm_jraddiusp_op = 0x18,
};
/*
diff --git a/arch/mips/include/uapi/asm/msgbuf.h b/arch/mips/include/uapi/asm/msgbuf.h
index 0d6c7f1..df849e8 100644
--- a/arch/mips/include/uapi/asm/msgbuf.h
+++ b/arch/mips/include/uapi/asm/msgbuf.h
@@ -14,25 +14,25 @@
struct msqid64_ds {
struct ipc64_perm msg_perm;
-#if defined(CONFIG_32BIT) && !defined(CONFIG_CPU_LITTLE_ENDIAN)
+#if !defined(__mips64) && defined(__MIPSEB__)
unsigned long __unused1;
#endif
__kernel_time_t msg_stime; /* last msgsnd time */
-#if defined(CONFIG_32BIT) && defined(CONFIG_CPU_LITTLE_ENDIAN)
+#if !defined(__mips64) && defined(__MIPSEL__)
unsigned long __unused1;
#endif
-#if defined(CONFIG_32BIT) && !defined(CONFIG_CPU_LITTLE_ENDIAN)
+#if !defined(__mips64) && defined(__MIPSEB__)
unsigned long __unused2;
#endif
__kernel_time_t msg_rtime; /* last msgrcv time */
-#if defined(CONFIG_32BIT) && defined(CONFIG_CPU_LITTLE_ENDIAN)
+#if !defined(__mips64) && defined(__MIPSEL__)
unsigned long __unused2;
#endif
-#if defined(CONFIG_32BIT) && !defined(CONFIG_CPU_LITTLE_ENDIAN)
+#if !defined(__mips64) && defined(__MIPSEB__)
unsigned long __unused3;
#endif
__kernel_time_t msg_ctime; /* last change time */
-#if defined(CONFIG_32BIT) && defined(CONFIG_CPU_LITTLE_ENDIAN)
+#if !defined(__mips64) && defined(__MIPSEL__)
unsigned long __unused3;
#endif
unsigned long msg_cbytes; /* current number of bytes on queue */
diff --git a/arch/mips/include/uapi/asm/resource.h b/arch/mips/include/uapi/asm/resource.h
index 87cb308..b26439d 100644
--- a/arch/mips/include/uapi/asm/resource.h
+++ b/arch/mips/include/uapi/asm/resource.h
@@ -26,7 +26,7 @@
* but we keep the old value on MIPS32,
* for compatibility:
*/
-#ifdef CONFIG_32BIT
+#ifndef __mips64
# define RLIM_INFINITY 0x7fffffffUL
#endif
diff --git a/arch/mips/include/uapi/asm/siginfo.h b/arch/mips/include/uapi/asm/siginfo.h
index 6a87141..b7a2306 100644
--- a/arch/mips/include/uapi/asm/siginfo.h
+++ b/arch/mips/include/uapi/asm/siginfo.h
@@ -25,10 +25,10 @@
/*
* Careful to keep union _sifields from shifting ...
*/
-#ifdef CONFIG_32BIT
+#if __SIZEOF_LONG__ == 4
#define __ARCH_SI_PREAMBLE_SIZE (3 * sizeof(int))
#endif
-#ifdef CONFIG_64BIT
+#if __SIZEOF_LONG__ == 8
#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
#endif
diff --git a/arch/mips/include/uapi/asm/swab.h b/arch/mips/include/uapi/asm/swab.h
index 97c2f81..ac9a8f9 100644
--- a/arch/mips/include/uapi/asm/swab.h
+++ b/arch/mips/include/uapi/asm/swab.h
@@ -13,7 +13,7 @@
#define __SWAB_64_THRU_32__
-#ifdef CONFIG_CPU_MIPSR2
+#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2)
static inline __attribute_const__ __u16 __arch_swab16(__u16 x)
{
@@ -39,10 +39,10 @@
#define __arch_swab32 __arch_swab32
/*
- * Having already checked for CONFIG_CPU_MIPSR2, enable the
- * optimized version for 64-bit kernel on r2 CPUs.
+ * Having already checked for MIPS R2, enable the optimized version for
+ * 64-bit kernel on r2 CPUs.
*/
-#ifdef CONFIG_64BIT
+#ifdef __mips64
static inline __attribute_const__ __u64 __arch_swab64(__u64 x)
{
__asm__(
@@ -54,6 +54,6 @@
return x;
}
#define __arch_swab64 __arch_swab64
-#endif /* CONFIG_64BIT */
-#endif /* CONFIG_CPU_MIPSR2 */
+#endif /* __mips64 */
+#endif /* MIPS R2 or newer */
#endif /* _ASM_SWAB_H */
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index 0845091..0c2e853 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -82,6 +82,9 @@
OFFSET(TASK_FLAGS, task_struct, flags);
OFFSET(TASK_MM, task_struct, mm);
OFFSET(TASK_PID, task_struct, pid);
+#if defined(CONFIG_CC_STACKPROTECTOR)
+ OFFSET(TASK_STACK_CANARY, task_struct, stack_canary);
+#endif
DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
BLANK();
}
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
index 46c2ad0..4d78bf4 100644
--- a/arch/mips/kernel/branch.c
+++ b/arch/mips/kernel/branch.c
@@ -467,5 +467,4 @@
printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
force_sig(SIGBUS, current);
return -EFAULT;
-
}
diff --git a/arch/mips/kernel/cpu-bugs64.c b/arch/mips/kernel/cpu-bugs64.c
index de3c25f..0c61df2 100644
--- a/arch/mips/kernel/cpu-bugs64.c
+++ b/arch/mips/kernel/cpu-bugs64.c
@@ -6,6 +6,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/context_tracking.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
@@ -171,8 +172,12 @@
asmlinkage void __init do_daddi_ov(struct pt_regs *regs)
{
+ enum ctx_state prev_state;
+
+ prev_state = exception_enter();
daddi_ov = 1;
regs->cp0_epc += 4;
+ exception_exit(prev_state);
}
static inline void check_daddi(void)
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index c6568bf..c7b1b3c 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -146,8 +146,7 @@
case MIPS_CPU_ISA_IV:
c->isa_level |= MIPS_CPU_ISA_IV;
case MIPS_CPU_ISA_III:
- c->isa_level |= MIPS_CPU_ISA_I | MIPS_CPU_ISA_II |
- MIPS_CPU_ISA_III;
+ c->isa_level |= MIPS_CPU_ISA_II | MIPS_CPU_ISA_III;
break;
case MIPS_CPU_ISA_M32R2:
@@ -156,8 +155,6 @@
c->isa_level |= MIPS_CPU_ISA_M32R1;
case MIPS_CPU_ISA_II:
c->isa_level |= MIPS_CPU_ISA_II;
- case MIPS_CPU_ISA_I:
- c->isa_level |= MIPS_CPU_ISA_I;
break;
}
}
@@ -272,9 +269,6 @@
c->options |= MIPS_CPU_ULRI;
if (config3 & MIPS_CONF3_ISA)
c->options |= MIPS_CPU_MICROMIPS;
-#ifdef CONFIG_CPU_MICROMIPS
- write_c0_config3(read_c0_config3() | MIPS_CONF3_ISA_OE);
-#endif
if (config3 & MIPS_CONF3_VZ)
c->ases |= MIPS_ASE_VZ;
@@ -332,7 +326,6 @@
case PRID_IMP_R2000:
c->cputype = CPU_R2000;
__cpu_name[cpu] = "R2000";
- set_isa(c, MIPS_CPU_ISA_I);
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
MIPS_CPU_NOFPUEX;
if (__cpu_has_fpu())
@@ -352,7 +345,6 @@
c->cputype = CPU_R3000;
__cpu_name[cpu] = "R3000";
}
- set_isa(c, MIPS_CPU_ISA_I);
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
MIPS_CPU_NOFPUEX;
if (__cpu_has_fpu())
@@ -455,7 +447,6 @@
break;
#endif
case PRID_IMP_TX39:
- set_isa(c, MIPS_CPU_ISA_I);
c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE;
if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) {
@@ -959,6 +950,7 @@
set_isa(c, MIPS_CPU_ISA_M64R1);
c->tlbsize = ((read_c0_config1() >> 25) & 0x3f) + 1;
}
+ c->kscratch_mask = 0xf;
}
#ifdef CONFIG_64BIT
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index c61cdae..0999123 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -28,45 +28,6 @@
#include <kernel-entry-init.h>
/*
- * inputs are the text nasid in t1, data nasid in t2.
- */
- .macro MAPPED_KERNEL_SETUP_TLB
-#ifdef CONFIG_MAPPED_KERNEL
- /*
- * This needs to read the nasid - assume 0 for now.
- * Drop in 0xffffffffc0000000 in tlbhi, 0+VG in tlblo_0,
- * 0+DVG in tlblo_1.
- */
- dli t0, 0xffffffffc0000000
- dmtc0 t0, CP0_ENTRYHI
- li t0, 0x1c000 # Offset of text into node memory
- dsll t1, NASID_SHFT # Shift text nasid into place
- dsll t2, NASID_SHFT # Same for data nasid
- or t1, t1, t0 # Physical load address of kernel text
- or t2, t2, t0 # Physical load address of kernel data
- dsrl t1, 12 # 4K pfn
- dsrl t2, 12 # 4K pfn
- dsll t1, 6 # Get pfn into place
- dsll t2, 6 # Get pfn into place
- li t0, ((_PAGE_GLOBAL|_PAGE_VALID| _CACHE_CACHABLE_COW) >> 6)
- or t0, t0, t1
- mtc0 t0, CP0_ENTRYLO0 # physaddr, VG, cach exlwr
- li t0, ((_PAGE_GLOBAL|_PAGE_VALID| _PAGE_DIRTY|_CACHE_CACHABLE_COW) >> 6)
- or t0, t0, t2
- mtc0 t0, CP0_ENTRYLO1 # physaddr, DVG, cach exlwr
- li t0, 0x1ffe000 # MAPPED_KERN_TLBMASK, TLBPGMASK_16M
- mtc0 t0, CP0_PAGEMASK
- li t0, 0 # KMAP_INX
- mtc0 t0, CP0_INDEX
- li t0, 1
- mtc0 t0, CP0_WIRED
- tlbwi
-#else
- mtc0 zero, CP0_WIRED
-#endif
- .endm
-
- /*
* For the moment disable interrupts, mark the kernel mode and
* set ST0_KX so that the CPU does not spit fire when using
* 64-bit addresses. A full initialization of the CPU's status
diff --git a/arch/mips/kernel/irq-gic.c b/arch/mips/kernel/irq-gic.c
index c01b307..5b5ddb2 100644
--- a/arch/mips/kernel/irq-gic.c
+++ b/arch/mips/kernel/irq-gic.c
@@ -219,16 +219,15 @@
/* Assumption : cpumask refers to a single CPU */
spin_lock_irqsave(&gic_lock, flags);
- for (;;) {
- /* Re-route this IRQ */
- GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp));
- /* Update the pcpu_masks */
- for (i = 0; i < NR_CPUS; i++)
- clear_bit(irq, pcpu_masks[i].pcpu_mask);
- set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask);
+ /* Re-route this IRQ */
+ GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp));
- }
+ /* Update the pcpu_masks */
+ for (i = 0; i < NR_CPUS; i++)
+ clear_bit(irq, pcpu_masks[i].pcpu_mask);
+ set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask);
+
cpumask_copy(d->affinity, cpumask);
spin_unlock_irqrestore(&gic_lock, flags);
diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S
index 33d0671..a03e93c 100644
--- a/arch/mips/kernel/mcount.S
+++ b/arch/mips/kernel/mcount.S
@@ -168,15 +168,11 @@
#endif
/* arg3: Get frame pointer of current stack */
-#ifdef CONFIG_FRAME_POINTER
- move a2, fp
-#else /* ! CONFIG_FRAME_POINTER */
#ifdef CONFIG_64BIT
PTR_LA a2, PT_SIZE(sp)
#else
PTR_LA a2, (PT_SIZE+8)(sp)
#endif
-#endif
jal prepare_ftrace_return
nop
diff --git a/arch/mips/kernel/octeon_switch.S b/arch/mips/kernel/octeon_switch.S
index 0e23343..4204d76 100644
--- a/arch/mips/kernel/octeon_switch.S
+++ b/arch/mips/kernel/octeon_switch.S
@@ -40,33 +40,6 @@
cpu_save_nonscratch a0
LONG_S ra, THREAD_REG31(a0)
- /* check if we need to save COP2 registers */
- PTR_L t2, TASK_THREAD_INFO(a0)
- LONG_L t0, ST_OFF(t2)
- bbit0 t0, 30, 1f
-
- /* Disable COP2 in the stored process state */
- li t1, ST0_CU2
- xor t0, t1
- LONG_S t0, ST_OFF(t2)
-
- /* Enable COP2 so we can save it */
- mfc0 t0, CP0_STATUS
- or t0, t1
- mtc0 t0, CP0_STATUS
-
- /* Save COP2 */
- daddu a0, THREAD_CP2
- jal octeon_cop2_save
- dsubu a0, THREAD_CP2
-
- /* Disable COP2 now that we are done */
- mfc0 t0, CP0_STATUS
- li t1, ST0_CU2
- xor t0, t1
- mtc0 t0, CP0_STATUS
-
-1:
#if CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
/* Check if we need to store CVMSEG state */
mfc0 t0, $11,7 /* CvmMemCtl */
@@ -98,6 +71,13 @@
mtc0 t0, $11,7 /* CvmMemCtl */
#endif
3:
+
+#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+ PTR_L t8, __stack_chk_guard
+ LONG_L t9, TASK_STACK_CANARY(a1)
+ LONG_S t9, 0(t8)
+#endif
+
/*
* The order of restoring the registers takes care of the race
* updating $28, $29 and kernelsp without disabling ints.
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index acb3437..8c58d8a 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -66,9 +66,7 @@
seq_printf(m, "]\n");
}
if (cpu_has_mips_r) {
- seq_printf(m, "isa\t\t\t:");
- if (cpu_has_mips_1)
- seq_printf(m, "%s", " mips1");
+ seq_printf(m, "isa\t\t\t: mips1");
if (cpu_has_mips_2)
seq_printf(m, "%s", " mips2");
if (cpu_has_mips_3)
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index c6a041d..ddc7610 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -201,9 +201,12 @@
return 1;
}
-/*
- *
- */
+#ifdef CONFIG_CC_STACKPROTECTOR
+#include <linux/stackprotector.h>
+unsigned long __stack_chk_guard __read_mostly;
+EXPORT_SYMBOL(__stack_chk_guard);
+#endif
+
struct mips_frame_info {
void *func;
unsigned long func_size;
diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c
index 5712bb5..7e95404 100644
--- a/arch/mips/kernel/prom.c
+++ b/arch/mips/kernel/prom.c
@@ -30,7 +30,7 @@
if (name == NULL)
return;
- strncpy(mips_machine_name, name, sizeof(mips_machine_name));
+ strlcpy(mips_machine_name, name, sizeof(mips_machine_name));
pr_info("MIPS: machine is %s\n", mips_get_machine_name());
}
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 9c6299c..8ae1ebe 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -15,6 +15,7 @@
* binaries.
*/
#include <linux/compiler.h>
+#include <linux/context_tracking.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -534,6 +535,8 @@
*/
asmlinkage void syscall_trace_enter(struct pt_regs *regs)
{
+ user_exit();
+
/* do the secure computing check first */
secure_computing_strict(regs->regs[2]);
@@ -570,6 +573,13 @@
*/
asmlinkage void syscall_trace_leave(struct pt_regs *regs)
{
+ /*
+ * We may come here right after calling schedule_user()
+ * or do_notify_resume(), in which case we can be in RCU
+ * user mode.
+ */
+ user_exit();
+
audit_syscall_exit(regs);
if (!(current->ptrace & PT_PTRACED))
@@ -592,4 +602,6 @@
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
+
+ user_enter();
}
diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S
index 5266c6e..38af83f 100644
--- a/arch/mips/kernel/r2300_switch.S
+++ b/arch/mips/kernel/r2300_switch.S
@@ -65,6 +65,13 @@
fpu_save_single a0, t0 # clobbers t0
1:
+
+#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+ PTR_L t8, __stack_chk_guard
+ LONG_L t9, TASK_STACK_CANARY(a1)
+ LONG_S t9, 0(t8)
+#endif
+
/*
* The order of restoring the registers takes care of the race
* updating $28, $29 and kernelsp without disabling ints.
diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S
index 5e51219..921238a 100644
--- a/arch/mips/kernel/r4k_switch.S
+++ b/arch/mips/kernel/r4k_switch.S
@@ -68,6 +68,12 @@
# clobbers t1
1:
+#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
+ PTR_L t8, __stack_chk_guard
+ LONG_L t9, TASK_STACK_CANARY(a1)
+ LONG_S t9, 0(t8)
+#endif
+
/*
* The order of restoring the registers takes care of the race
* updating $28, $29 and kernelsp without disabling ints.
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c
index 6fa198d..d763f11 100644
--- a/arch/mips/kernel/rtlx.c
+++ b/arch/mips/kernel/rtlx.c
@@ -437,7 +437,6 @@
size_t count, loff_t * ppos)
{
int minor = iminor(file_inode(file));
- struct rtlx_channel *rt = &rtlx->channel[minor];
/* any space left... */
if (!rtlx_write_poll(minor)) {
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index e9127ec..e774bb1 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -52,7 +52,7 @@
stack_done:
lw t0, TI_FLAGS($28) # syscall tracing enabled?
- li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
+ li t1, _TIF_WORK_SYSCALL_ENTRY
and t0, t1
bnez t0, syscall_trace_entry # -> yes
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index 97a5909..be6627e 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -54,7 +54,7 @@
sd a3, PT_R26(sp) # save a3 for syscall restarting
- li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
+ li t1, _TIF_WORK_SYSCALL_ENTRY
LONG_L t0, TI_FLAGS($28) # syscall tracing enabled?
and t0, t1, t0
bnez t0, syscall_trace_entry
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index edcb659..cab1507 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -47,7 +47,7 @@
sd a3, PT_R26(sp) # save a3 for syscall restarting
- li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
+ li t1, _TIF_WORK_SYSCALL_ENTRY
LONG_L t0, TI_FLAGS($28) # syscall tracing enabled?
and t0, t1, t0
bnez t0, n32_syscall_trace_entry
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 74f485d..37605dc 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -81,7 +81,7 @@
PTR 4b, bad_stack
.previous
- li t1, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
+ li t1, _TIF_WORK_SYSCALL_ENTRY
LONG_L t0, TI_FLAGS($28) # syscall tracing enabled?
and t0, t1, t0
bnez t0, trace_a_syscall
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index fd3ef2c..2f285ab 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -8,6 +8,7 @@
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/cache.h>
+#include <linux/context_tracking.h>
#include <linux/irqflags.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -573,6 +574,8 @@
{
local_irq_enable();
+ user_exit();
+
/* deal with pending signal delivery */
if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs);
@@ -581,6 +584,8 @@
clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs);
}
+
+ user_enter();
}
#ifdef CONFIG_SMP
diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
index 8e393b8..aea6c08 100644
--- a/arch/mips/kernel/smp-bmips.c
+++ b/arch/mips/kernel/smp-bmips.c
@@ -63,7 +63,7 @@
static void __init bmips_smp_setup(void)
{
- int i;
+ int i, cpu = 1, boot_cpu = 0;
#if defined(CONFIG_CPU_BMIPS4350) || defined(CONFIG_CPU_BMIPS4380)
/* arbitration priority */
@@ -72,13 +72,22 @@
/* NBK and weak order flags */
set_c0_brcm_config_0(0x30000);
+ /* Find out if we are running on TP0 or TP1 */
+ boot_cpu = !!(read_c0_brcm_cmt_local() & (1 << 31));
+
/*
* MIPS interrupts 0,1 (SW INT 0,1) cross over to the other thread
* MIPS interrupt 2 (HW INT 0) is the CPU0 L1 controller output
* MIPS interrupt 3 (HW INT 1) is the CPU1 L1 controller output
+ *
+ * If booting from TP1, leave the existing CMT interrupt routing
+ * such that TP0 responds to SW1 and TP1 responds to SW0.
*/
- change_c0_brcm_cmt_intr(0xf8018000,
- (0x02 << 27) | (0x03 << 15));
+ if (boot_cpu == 0)
+ change_c0_brcm_cmt_intr(0xf8018000,
+ (0x02 << 27) | (0x03 << 15));
+ else
+ change_c0_brcm_cmt_intr(0xf8018000, (0x1d << 27));
/* single core, 2 threads (2 pipelines) */
max_cpus = 2;
@@ -106,9 +115,15 @@
if (!board_ebase_setup)
board_ebase_setup = &bmips_ebase_setup;
+ __cpu_number_map[boot_cpu] = 0;
+ __cpu_logical_map[0] = boot_cpu;
+
for (i = 0; i < max_cpus; i++) {
- __cpu_number_map[i] = 1;
- __cpu_logical_map[i] = 1;
+ if (i != boot_cpu) {
+ __cpu_number_map[i] = cpu;
+ __cpu_logical_map[cpu] = i;
+ cpu++;
+ }
set_cpu_possible(i, 1);
set_cpu_present(i, 1);
}
@@ -157,7 +172,9 @@
bmips_send_ipi_single(cpu, 0);
else {
#if defined(CONFIG_CPU_BMIPS4350) || defined(CONFIG_CPU_BMIPS4380)
- set_c0_brcm_cmt_ctrl(0x01);
+ /* Reset slave TP1 if booting from TP0 */
+ if (cpu_logical_map(cpu) == 0)
+ set_c0_brcm_cmt_ctrl(0x01);
#elif defined(CONFIG_CPU_BMIPS5000)
if (cpu & 0x01)
write_c0_brcm_action(ACTION_BOOT_THREAD(cpu));
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index a75ae40..0903d70 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -13,6 +13,7 @@
*/
#include <linux/bug.h>
#include <linux/compiler.h>
+#include <linux/context_tracking.h>
#include <linux/kexec.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -264,7 +265,7 @@
printk("Status: %08x ", (uint32_t) regs->cp0_status);
- if (current_cpu_data.isa_level == MIPS_CPU_ISA_I) {
+ if (cpu_has_3kex) {
if (regs->cp0_status & ST0_KUO)
printk("KUo ");
if (regs->cp0_status & ST0_IEO)
@@ -277,7 +278,7 @@
printk("KUc ");
if (regs->cp0_status & ST0_IEC)
printk("IEc ");
- } else {
+ } else if (cpu_has_4kex) {
if (regs->cp0_status & ST0_KX)
printk("KX ");
if (regs->cp0_status & ST0_SX)
@@ -423,7 +424,9 @@
const struct exception_table_entry *fixup = NULL;
int data = regs->cp0_cause & 4;
int action = MIPS_BE_FATAL;
+ enum ctx_state prev_state;
+ prev_state = exception_enter();
/* XXX For now. Fixme, this searches the wrong table ... */
if (data && !user_mode(regs))
fixup = search_dbe_tables(exception_epc(regs));
@@ -436,11 +439,11 @@
switch (action) {
case MIPS_BE_DISCARD:
- return;
+ goto out;
case MIPS_BE_FIXUP:
if (fixup) {
regs->cp0_epc = fixup->nextinsn;
- return;
+ goto out;
}
break;
default:
@@ -455,10 +458,13 @@
field, regs->cp0_epc, field, regs->regs[31]);
if (notify_die(DIE_OOPS, "bus error", regs, 0, regs_to_trapnr(regs), SIGBUS)
== NOTIFY_STOP)
- return;
+ goto out;
die_if_kernel("Oops", regs);
force_sig(SIGBUS, current);
+
+out:
+ exception_exit(prev_state);
}
/*
@@ -673,8 +679,10 @@
asmlinkage void do_ov(struct pt_regs *regs)
{
+ enum ctx_state prev_state;
siginfo_t info;
+ prev_state = exception_enter();
die_if_kernel("Integer overflow", regs);
info.si_code = FPE_INTOVF;
@@ -682,6 +690,7 @@
info.si_errno = 0;
info.si_addr = (void __user *) regs->cp0_epc;
force_sig_info(SIGFPE, &info, current);
+ exception_exit(prev_state);
}
int process_fpemu_return(int sig, void __user *fault_addr)
@@ -713,11 +722,13 @@
*/
asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
{
+ enum ctx_state prev_state;
siginfo_t info = {0};
+ prev_state = exception_enter();
if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs), SIGFPE)
== NOTIFY_STOP)
- return;
+ goto out;
die_if_kernel("FP exception in kernel code", regs);
if (fcr31 & FPU_CSR_UNI_X) {
@@ -753,7 +764,7 @@
/* If something went wrong, signal */
process_fpemu_return(sig, fault_addr);
- return;
+ goto out;
} else if (fcr31 & FPU_CSR_INV_X)
info.si_code = FPE_FLTINV;
else if (fcr31 & FPU_CSR_DIV_X)
@@ -770,6 +781,9 @@
info.si_errno = 0;
info.si_addr = (void __user *) regs->cp0_epc;
force_sig_info(SIGFPE, &info, current);
+
+out:
+ exception_exit(prev_state);
}
static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
@@ -835,9 +849,11 @@
asmlinkage void do_bp(struct pt_regs *regs)
{
unsigned int opcode, bcode;
+ enum ctx_state prev_state;
unsigned long epc;
u16 instr[2];
+ prev_state = exception_enter();
if (get_isa16_mode(regs->cp0_epc)) {
/* Calculate EPC. */
epc = exception_epc(regs);
@@ -852,7 +868,7 @@
goto out_sigsegv;
bcode = (instr[0] >> 6) & 0x3f;
do_trap_or_bp(regs, bcode, "Break");
- return;
+ goto out;
}
} else {
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
@@ -876,12 +892,12 @@
switch (bcode) {
case BRK_KPROBE_BP:
if (notify_die(DIE_BREAK, "debug", regs, bcode, regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
- return;
+ goto out;
else
break;
case BRK_KPROBE_SSTEPBP:
if (notify_die(DIE_SSTEPBP, "single_step", regs, bcode, regs_to_trapnr(regs), SIGTRAP) == NOTIFY_STOP)
- return;
+ goto out;
else
break;
default:
@@ -889,18 +905,24 @@
}
do_trap_or_bp(regs, bcode, "Break");
+
+out:
+ exception_exit(prev_state);
return;
out_sigsegv:
force_sig(SIGSEGV, current);
+ goto out;
}
asmlinkage void do_tr(struct pt_regs *regs)
{
u32 opcode, tcode = 0;
+ enum ctx_state prev_state;
u16 instr[2];
unsigned long epc = msk_isa16_mode(exception_epc(regs));
+ prev_state = exception_enter();
if (get_isa16_mode(regs->cp0_epc)) {
if (__get_user(instr[0], (u16 __user *)(epc + 0)) ||
__get_user(instr[1], (u16 __user *)(epc + 2)))
@@ -918,10 +940,14 @@
}
do_trap_or_bp(regs, tcode, "Trap");
+
+out:
+ exception_exit(prev_state);
return;
out_sigsegv:
force_sig(SIGSEGV, current);
+ goto out;
}
asmlinkage void do_ri(struct pt_regs *regs)
@@ -929,17 +955,19 @@
unsigned int __user *epc = (unsigned int __user *)exception_epc(regs);
unsigned long old_epc = regs->cp0_epc;
unsigned long old31 = regs->regs[31];
+ enum ctx_state prev_state;
unsigned int opcode = 0;
int status = -1;
+ prev_state = exception_enter();
if (notify_die(DIE_RI, "RI Fault", regs, 0, regs_to_trapnr(regs), SIGILL)
== NOTIFY_STOP)
- return;
+ goto out;
die_if_kernel("Reserved instruction in kernel code", regs);
if (unlikely(compute_return_epc(regs) < 0))
- return;
+ goto out;
if (get_isa16_mode(regs->cp0_epc)) {
unsigned short mmop[2] = { 0 };
@@ -974,6 +1002,9 @@
regs->regs[31] = old31;
force_sig(status, current);
}
+
+out:
+ exception_exit(prev_state);
}
/*
@@ -1025,21 +1056,16 @@
{
struct pt_regs *regs = data;
- switch (action) {
- default:
- die_if_kernel("Unhandled kernel unaligned access or invalid "
+ die_if_kernel("COP2: Unhandled kernel unaligned access or invalid "
"instruction", regs);
- /* Fall through */
-
- case CU2_EXCEPTION:
- force_sig(SIGILL, current);
- }
+ force_sig(SIGILL, current);
return NOTIFY_OK;
}
asmlinkage void do_cpu(struct pt_regs *regs)
{
+ enum ctx_state prev_state;
unsigned int __user *epc;
unsigned long old_epc, old31;
unsigned int opcode;
@@ -1047,10 +1073,12 @@
int status;
unsigned long __maybe_unused flags;
- die_if_kernel("do_cpu invoked from kernel context!", regs);
-
+ prev_state = exception_enter();
cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;
+ if (cpid != 2)
+ die_if_kernel("do_cpu invoked from kernel context!", regs);
+
switch (cpid) {
case 0:
epc = (unsigned int __user *)exception_epc(regs);
@@ -1060,7 +1088,7 @@
status = -1;
if (unlikely(compute_return_epc(regs) < 0))
- return;
+ goto out;
if (get_isa16_mode(regs->cp0_epc)) {
unsigned short mmop[2] = { 0 };
@@ -1093,7 +1121,7 @@
force_sig(status, current);
}
- return;
+ goto out;
case 3:
/*
@@ -1131,19 +1159,26 @@
mt_ase_fp_affinity();
}
- return;
+ goto out;
case 2:
raw_notifier_call_chain(&cu2_chain, CU2_EXCEPTION, regs);
- return;
+ goto out;
}
force_sig(SIGILL, current);
+
+out:
+ exception_exit(prev_state);
}
asmlinkage void do_mdmx(struct pt_regs *regs)
{
+ enum ctx_state prev_state;
+
+ prev_state = exception_enter();
force_sig(SIGILL, current);
+ exception_exit(prev_state);
}
/*
@@ -1151,8 +1186,10 @@
*/
asmlinkage void do_watch(struct pt_regs *regs)
{
+ enum ctx_state prev_state;
u32 cause;
+ prev_state = exception_enter();
/*
* Clear WP (bit 22) bit of cause register so we don't loop
* forever.
@@ -1174,13 +1211,16 @@
mips_clear_watch_registers();
local_irq_enable();
}
+ exception_exit(prev_state);
}
asmlinkage void do_mcheck(struct pt_regs *regs)
{
const int field = 2 * sizeof(unsigned long);
int multi_match = regs->cp0_status & ST0_TS;
+ enum ctx_state prev_state;
+ prev_state = exception_enter();
show_regs(regs);
if (multi_match) {
@@ -1202,6 +1242,7 @@
panic("Caught Machine Check exception - %scaused by multiple "
"matching entries in the TLB.",
(multi_match) ? "" : "not ");
+ exception_exit(prev_state);
}
asmlinkage void do_mt(struct pt_regs *regs)
@@ -1627,7 +1668,6 @@
}
extern void tlb_init(void);
-extern void flush_tlb_handlers(void);
/*
* Timer interrupt
@@ -1837,6 +1877,15 @@
ebase += (read_c0_ebase() & 0x3ffff000);
}
+ if (cpu_has_mmips) {
+ unsigned int config3 = read_c0_config3();
+
+ if (IS_ENABLED(CONFIG_CPU_MICROMIPS))
+ write_c0_config3(config3 | MIPS_CONF3_ISA_OE);
+ else
+ write_c0_config3(config3 & ~MIPS_CONF3_ISA_OE);
+ }
+
if (board_ebase_setup)
board_ebase_setup();
per_cpu_trap_init(true);
@@ -1956,7 +2005,6 @@
set_handler(0x080, &except_vec3_generic, 0x80);
local_flush_icache_range(ebase, ebase + 0x400);
- flush_tlb_handlers();
sort_extable(__start___dbe_table, __stop___dbe_table);
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 203d885..c369a5d 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -72,6 +72,7 @@
* A store crossing a page boundary might be executed only partially.
* Undo the partial store in this case.
*/
+#include <linux/context_tracking.h>
#include <linux/mm.h>
#include <linux/signal.h>
#include <linux/smp.h>
@@ -684,7 +685,8 @@
/* Recode table from 16-bit STORE register notation to 32-bit GPR. */
const int reg16to32st[] = { 0, 17, 2, 3, 4, 5, 6, 7 };
-void emulate_load_store_microMIPS(struct pt_regs *regs, void __user * addr)
+static void emulate_load_store_microMIPS(struct pt_regs *regs,
+ void __user *addr)
{
unsigned long value;
unsigned int res;
@@ -1548,11 +1550,14 @@
("Unhandled kernel unaligned access or invalid instruction", regs);
force_sig(SIGILL, current);
}
+
asmlinkage void do_ade(struct pt_regs *regs)
{
+ enum ctx_state prev_state;
unsigned int __user *pc;
mm_segment_t seg;
+ prev_state = exception_enter();
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS,
1, regs, regs->cp0_badvaddr);
/*
@@ -1628,6 +1633,7 @@
/*
* XXX On return from the signal handler we should advance the epc
*/
+ exception_exit(prev_state);
}
#ifdef CONFIG_DEBUG_FS
diff --git a/arch/mips/kernel/watch.c b/arch/mips/kernel/watch.c
index 7726f61..cbdc4de 100644
--- a/arch/mips/kernel/watch.c
+++ b/arch/mips/kernel/watch.c
@@ -111,6 +111,7 @@
* disable the register.
*/
write_c0_watchlo0(7);
+ back_to_back_c0_hazard();
t = read_c0_watchlo0();
write_c0_watchlo0(0);
c->watch_reg_masks[0] = t & 7;
@@ -121,12 +122,14 @@
c->watch_reg_use_cnt = 1;
t = read_c0_watchhi0();
write_c0_watchhi0(t | 0xff8);
+ back_to_back_c0_hazard();
t = read_c0_watchhi0();
c->watch_reg_masks[0] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;
write_c0_watchlo1(7);
+ back_to_back_c0_hazard();
t = read_c0_watchlo1();
write_c0_watchlo1(0);
c->watch_reg_masks[1] = t & 7;
@@ -135,12 +138,14 @@
c->watch_reg_use_cnt = 2;
t = read_c0_watchhi1();
write_c0_watchhi1(t | 0xff8);
+ back_to_back_c0_hazard();
t = read_c0_watchhi1();
c->watch_reg_masks[1] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;
write_c0_watchlo2(7);
+ back_to_back_c0_hazard();
t = read_c0_watchlo2();
write_c0_watchlo2(0);
c->watch_reg_masks[2] = t & 7;
@@ -149,12 +154,14 @@
c->watch_reg_use_cnt = 3;
t = read_c0_watchhi2();
write_c0_watchhi2(t | 0xff8);
+ back_to_back_c0_hazard();
t = read_c0_watchhi2();
c->watch_reg_masks[2] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;
write_c0_watchlo3(7);
+ back_to_back_c0_hazard();
t = read_c0_watchlo3();
write_c0_watchlo3(0);
c->watch_reg_masks[3] = t & 7;
@@ -163,6 +170,7 @@
c->watch_reg_use_cnt = 4;
t = read_c0_watchhi3();
write_c0_watchhi3(t | 0xff8);
+ back_to_back_c0_hazard();
t = read_c0_watchhi3();
c->watch_reg_masks[3] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c
index 9f9e875..49c4603 100644
--- a/arch/mips/lantiq/prom.c
+++ b/arch/mips/lantiq/prom.c
@@ -112,7 +112,7 @@
if (!of_have_populated_dt())
panic("device tree not present");
- strncpy(of_ids[0].compatible, soc_info.compatible,
+ strlcpy(of_ids[0].compatible, soc_info.compatible,
sizeof(of_ids[0].compatible));
strncpy(of_ids[1].compatible, "simple-bus",
sizeof(of_ids[1].compatible));
diff --git a/arch/mips/lasat/sysctl.c b/arch/mips/lasat/sysctl.c
index f27694f..3b7f65c 100644
--- a/arch/mips/lasat/sysctl.c
+++ b/arch/mips/lasat/sysctl.c
@@ -39,7 +39,7 @@
/* And the same for proc */
-int proc_dolasatstring(ctl_table *table, int write,
+int proc_dolasatstring(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
@@ -54,7 +54,7 @@
}
/* proc function to write EEPROM after changing int entry */
-int proc_dolasatint(ctl_table *table, int write,
+int proc_dolasatint(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
@@ -72,7 +72,7 @@
static int rtctmp;
/* proc function to read/write RealTime Clock */
-int proc_dolasatrtc(ctl_table *table, int write,
+int proc_dolasatrtc(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct timespec ts;
@@ -97,7 +97,7 @@
#endif
#ifdef CONFIG_INET
-int proc_lasat_ip(ctl_table *table, int write,
+int proc_lasat_ip(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
unsigned int ip;
@@ -157,7 +157,7 @@
}
#endif
-int proc_lasat_prid(ctl_table *table, int write,
+int proc_lasat_prid(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
@@ -176,7 +176,7 @@
extern int lasat_boot_to_service;
-static ctl_table lasat_table[] = {
+static struct ctl_table lasat_table[] = {
{
.procname = "cpu-hz",
.data = &lasat_board_info.li_cpu_hz,
@@ -262,7 +262,7 @@
{}
};
-static ctl_table lasat_root_table[] = {
+static struct ctl_table lasat_root_table[] = {
{
.procname = "lasat",
.mode = 0555,
diff --git a/arch/mips/loongson/common/cs5536/cs5536_isa.c b/arch/mips/loongson/common/cs5536/cs5536_isa.c
index a6eb2e8..924be39 100644
--- a/arch/mips/loongson/common/cs5536/cs5536_isa.c
+++ b/arch/mips/loongson/common/cs5536/cs5536_isa.c
@@ -13,6 +13,7 @@
* option) any later version.
*/
+#include <linux/pci.h>
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
@@ -314,3 +315,16 @@
return conf_data;
}
+
+/*
+ * The mfgpt timer interrupt is running early, so we must keep the south bridge
+ * mmio always enabled. Otherwise we may race with the PCI configuration which
+ * may temporarily disable it. When that happens and the timer interrupt fires,
+ * we are not able to clear it and the system will hang.
+ */
+static void cs5536_isa_mmio_always_on(struct pci_dev *dev)
+{
+ dev->mmio_always_on = 1;
+}
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
+ PCI_CLASS_BRIDGE_ISA, 8, cs5536_isa_mmio_always_on);
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index f03771900..e773659 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -471,6 +471,9 @@
unsigned int fcr31;
unsigned int bit;
+ if (!cpu_has_mmips)
+ return 0;
+
switch (insn.mm_i_format.opcode) {
case mm_pool32a_op:
if ((insn.mm_i_format.simmediate & MM_POOL32A_MINOR_MASK) ==
diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile
index e87aae1..7f4f93a 100644
--- a/arch/mips/mm/Makefile
+++ b/arch/mips/mm/Makefile
@@ -4,7 +4,7 @@
obj-y += cache.o dma-default.o extable.o fault.o \
gup.o init.o mmap.o page.o page-funcs.o \
- tlbex.o tlbex-fault.o uasm-mips.o
+ tlbex.o tlbex-fault.o tlb-funcs.o uasm-mips.o
obj-$(CONFIG_32BIT) += ioremap.o pgtable-32.o
obj-$(CONFIG_64BIT) += pgtable-64.o
diff --git a/arch/mips/mm/cerr-sb1.c b/arch/mips/mm/cerr-sb1.c
index 576add3..ee5c1ff 100644
--- a/arch/mips/mm/cerr-sb1.c
+++ b/arch/mips/mm/cerr-sb1.c
@@ -182,11 +182,7 @@
#ifdef CONFIG_SIBYTE_BW_TRACE
/* Freeze the trace buffer now */
-#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
- csr_out32(M_BCM1480_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
-#else
csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
-#endif
printk("Trace buffer frozen\n");
#endif
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index caf92ec..aaccf1c 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -246,6 +246,9 @@
if (!plat_device_is_coherent(dev))
__dma_sync(sg_page(sg), sg->offset, sg->length,
direction);
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+ sg->dma_length = sg->length;
+#endif
sg->dma_address = plat_map_dma_mem_page(dev, sg_page(sg)) +
sg->offset;
}
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 0fead53..85df1cd 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 1995 - 2000 by Ralf Baechle
*/
+#include <linux/context_tracking.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -32,8 +33,8 @@
* and the problem, and then passes it off to one of the appropriate
* routines.
*/
-asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long write,
- unsigned long address)
+static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,
+ unsigned long address)
{
struct vm_area_struct * vma = NULL;
struct task_struct *tsk = current;
@@ -312,3 +313,13 @@
}
#endif
}
+
+asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
+ unsigned long write, unsigned long address)
+{
+ enum ctx_state prev_state;
+
+ prev_state = exception_enter();
+ __do_page_fault(regs, write, address);
+ exception_exit(prev_state);
+}
diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c
index 7e5fe27..f1baadd 100644
--- a/arch/mips/mm/mmap.c
+++ b/arch/mips/mm/mmap.c
@@ -158,11 +158,9 @@
if (mmap_is_legacy()) {
mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base(random_factor);
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c
index 4eb8dcf..2c0bd58 100644
--- a/arch/mips/mm/page.c
+++ b/arch/mips/mm/page.c
@@ -232,7 +232,7 @@
uasm_i_cache(buf, Create_Dirty_Excl_D, off, A0);
}
- }
+ }
}
extern u32 __clear_page_start;
diff --git a/arch/mips/mm/tlb-funcs.S b/arch/mips/mm/tlb-funcs.S
new file mode 100644
index 0000000..30a494d
--- /dev/null
+++ b/arch/mips/mm/tlb-funcs.S
@@ -0,0 +1,37 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Micro-assembler generated tlb handler functions.
+ *
+ * Copyright (C) 2013 Broadcom Corporation.
+ *
+ * Based on mm/page-funcs.c
+ * Copyright (C) 2012 MIPS Technologies, Inc.
+ * Copyright (C) 2012 Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+
+#define FASTPATH_SIZE 128
+
+LEAF(tlbmiss_handler_setup_pgd)
+ .space 16 * 4
+END(tlbmiss_handler_setup_pgd)
+EXPORT(tlbmiss_handler_setup_pgd_end)
+
+LEAF(handle_tlbm)
+ .space FASTPATH_SIZE * 4
+END(handle_tlbm)
+EXPORT(handle_tlbm_end)
+
+LEAF(handle_tlbs)
+ .space FASTPATH_SIZE * 4
+END(handle_tlbs)
+EXPORT(handle_tlbs_end)
+
+LEAF(handle_tlbl)
+ .space FASTPATH_SIZE * 4
+END(handle_tlbl)
+EXPORT(handle_tlbl_end)
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index afeef93..9ab0f90 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -305,6 +305,17 @@
static unsigned int kscratch_used_mask __cpuinitdata;
+static inline int __maybe_unused c0_kscratch(void)
+{
+ switch (current_cpu_type()) {
+ case CPU_XLP:
+ case CPU_XLR:
+ return 22;
+ default:
+ return 31;
+ }
+}
+
static int __cpuinit allocate_kscratch(void)
{
int r;
@@ -334,9 +345,9 @@
int smp_processor_id_sel;
int smp_processor_id_shift;
- if (scratch_reg > 0) {
+ if (scratch_reg >= 0) {
/* Save in CPU local C0_KScratch? */
- UASM_i_MTC0(p, 1, 31, scratch_reg);
+ UASM_i_MTC0(p, 1, c0_kscratch(), scratch_reg);
r.r1 = K0;
r.r2 = K1;
r.r3 = 1;
@@ -384,8 +395,8 @@
static void __cpuinit build_restore_work_registers(u32 **p)
{
- if (scratch_reg > 0) {
- UASM_i_MFC0(p, 1, 31, scratch_reg);
+ if (scratch_reg >= 0) {
+ UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg);
return;
}
/* K0 already points to save area, restore $1 and $2 */
@@ -673,8 +684,8 @@
uasm_i_mtc0(p, 0, C0_PAGEMASK);
uasm_il_b(p, r, lid);
}
- if (scratch_reg > 0)
- UASM_i_MFC0(p, 1, 31, scratch_reg);
+ if (scratch_reg >= 0)
+ UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg);
else
UASM_i_LW(p, 1, scratchpad_offset(0), 0);
} else {
@@ -817,7 +828,7 @@
#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
if (pgd_reg != -1) {
/* pgd is in pgd_reg */
- UASM_i_MFC0(p, ptr, 31, pgd_reg);
+ UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
} else {
/*
* &pgd << 11 stored in CONTEXT [23..63].
@@ -929,8 +940,8 @@
uasm_i_jr(p, ptr);
if (mode == refill_scratch) {
- if (scratch_reg > 0)
- UASM_i_MFC0(p, 1, 31, scratch_reg);
+ if (scratch_reg >= 0)
+ UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg);
else
UASM_i_LW(p, 1, scratchpad_offset(0), 0);
} else {
@@ -961,7 +972,7 @@
uasm_i_srl(p, ptr, ptr, 19);
#else
/*
- * smp_processor_id() << 3 is stored in CONTEXT.
+ * smp_processor_id() << 2 is stored in CONTEXT.
*/
uasm_i_mfc0(p, ptr, C0_CONTEXT);
UASM_i_LA_mostly(p, tmp, pgdc);
@@ -1096,7 +1107,7 @@
static struct mips_huge_tlb_info __cpuinit
build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l,
struct uasm_reloc **r, unsigned int tmp,
- unsigned int ptr, int c0_scratch)
+ unsigned int ptr, int c0_scratch_reg)
{
struct mips_huge_tlb_info rv;
unsigned int even, odd;
@@ -1110,12 +1121,12 @@
UASM_i_MFC0(p, tmp, C0_BADVADDR);
if (pgd_reg != -1)
- UASM_i_MFC0(p, ptr, 31, pgd_reg);
+ UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
else
UASM_i_MFC0(p, ptr, C0_CONTEXT);
- if (c0_scratch >= 0)
- UASM_i_MTC0(p, scratch, 31, c0_scratch);
+ if (c0_scratch_reg >= 0)
+ UASM_i_MTC0(p, scratch, c0_kscratch(), c0_scratch_reg);
else
UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
@@ -1130,14 +1141,14 @@
}
} else {
if (pgd_reg != -1)
- UASM_i_MFC0(p, ptr, 31, pgd_reg);
+ UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
else
UASM_i_MFC0(p, ptr, C0_CONTEXT);
UASM_i_MFC0(p, tmp, C0_BADVADDR);
- if (c0_scratch >= 0)
- UASM_i_MTC0(p, scratch, 31, c0_scratch);
+ if (c0_scratch_reg >= 0)
+ UASM_i_MTC0(p, scratch, c0_kscratch(), c0_scratch_reg);
else
UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
@@ -1242,8 +1253,8 @@
}
UASM_i_MTC0(p, odd, C0_ENTRYLO1); /* load it */
- if (c0_scratch >= 0) {
- UASM_i_MFC0(p, scratch, 31, c0_scratch);
+ if (c0_scratch_reg >= 0) {
+ UASM_i_MFC0(p, scratch, c0_kscratch(), c0_scratch_reg);
build_tlb_write_entry(p, l, r, tlb_random);
uasm_l_leave(l, *p);
rv.restore_scratch = 1;
@@ -1286,7 +1297,7 @@
memset(relocs, 0, sizeof(relocs));
memset(final_handler, 0, sizeof(final_handler));
- if ((scratch_reg > 0 || scratchpad_available()) && use_bbit_insns()) {
+ if ((scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) {
htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
scratch_reg);
vmalloc_mode = refill_scratch;
@@ -1444,27 +1455,25 @@
dump_handler("r4000_tlb_refill", (u32 *)ebase, 64);
}
-/*
- * 128 instructions for the fastpath handler is generous and should
- * never be exceeded.
- */
-#define FASTPATH_SIZE 128
+extern u32 handle_tlbl[], handle_tlbl_end[];
+extern u32 handle_tlbs[], handle_tlbs_end[];
+extern u32 handle_tlbm[], handle_tlbm_end[];
-u32 handle_tlbl[FASTPATH_SIZE] __cacheline_aligned;
-u32 handle_tlbs[FASTPATH_SIZE] __cacheline_aligned;
-u32 handle_tlbm[FASTPATH_SIZE] __cacheline_aligned;
#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
-u32 tlbmiss_handler_setup_pgd_array[16] __cacheline_aligned;
+extern u32 tlbmiss_handler_setup_pgd[], tlbmiss_handler_setup_pgd_end[];
static void __cpuinit build_r4000_setup_pgd(void)
{
const int a0 = 4;
const int a1 = 5;
u32 *p = tlbmiss_handler_setup_pgd_array;
+ const int tlbmiss_handler_setup_pgd_size =
+ tlbmiss_handler_setup_pgd_end - tlbmiss_handler_setup_pgd;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
- memset(tlbmiss_handler_setup_pgd_array, 0, sizeof(tlbmiss_handler_setup_pgd_array));
+ memset(tlbmiss_handler_setup_pgd, 0, tlbmiss_handler_setup_pgd_size *
+ sizeof(tlbmiss_handler_setup_pgd[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -1490,17 +1499,17 @@
} else {
/* PGD in c0_KScratch */
uasm_i_jr(&p, 31);
- UASM_i_MTC0(&p, a0, 31, pgd_reg);
+ UASM_i_MTC0(&p, a0, c0_kscratch(), pgd_reg);
}
- if (p - tlbmiss_handler_setup_pgd_array > ARRAY_SIZE(tlbmiss_handler_setup_pgd_array))
- panic("tlbmiss_handler_setup_pgd_array space exceeded");
- uasm_resolve_relocs(relocs, labels);
- pr_debug("Wrote tlbmiss_handler_setup_pgd_array (%u instructions).\n",
- (unsigned int)(p - tlbmiss_handler_setup_pgd_array));
+ if (p >= tlbmiss_handler_setup_pgd_end)
+ panic("tlbmiss_handler_setup_pgd space exceeded");
- dump_handler("tlbmiss_handler",
- tlbmiss_handler_setup_pgd_array,
- ARRAY_SIZE(tlbmiss_handler_setup_pgd_array));
+ uasm_resolve_relocs(relocs, labels);
+ pr_debug("Wrote tlbmiss_handler_setup_pgd (%u instructions).\n",
+ (unsigned int)(p - tlbmiss_handler_setup_pgd));
+
+ dump_handler("tlbmiss_handler", tlbmiss_handler_setup_pgd,
+ tlbmiss_handler_setup_pgd_size);
}
#endif
@@ -1745,10 +1754,11 @@
static void __cpuinit build_r3000_tlb_load_handler(void)
{
u32 *p = handle_tlbl;
+ const int handle_tlbl_size = handle_tlbl_end - handle_tlbl;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
- memset(handle_tlbl, 0, sizeof(handle_tlbl));
+ memset(handle_tlbl, 0, handle_tlbl_size * sizeof(handle_tlbl[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -1762,23 +1772,24 @@
uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff);
uasm_i_nop(&p);
- if ((p - handle_tlbl) > FASTPATH_SIZE)
+ if (p >= handle_tlbl_end)
panic("TLB load handler fastpath space exceeded");
uasm_resolve_relocs(relocs, labels);
pr_debug("Wrote TLB load handler fastpath (%u instructions).\n",
(unsigned int)(p - handle_tlbl));
- dump_handler("r3000_tlb_load", handle_tlbl, ARRAY_SIZE(handle_tlbl));
+ dump_handler("r3000_tlb_load", handle_tlbl, handle_tlbl_size);
}
static void __cpuinit build_r3000_tlb_store_handler(void)
{
u32 *p = handle_tlbs;
+ const int handle_tlbs_size = handle_tlbs_end - handle_tlbs;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
- memset(handle_tlbs, 0, sizeof(handle_tlbs));
+ memset(handle_tlbs, 0, handle_tlbs_size * sizeof(handle_tlbs[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -1792,23 +1803,24 @@
uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
uasm_i_nop(&p);
- if ((p - handle_tlbs) > FASTPATH_SIZE)
+ if (p >= handle_tlbs)
panic("TLB store handler fastpath space exceeded");
uasm_resolve_relocs(relocs, labels);
pr_debug("Wrote TLB store handler fastpath (%u instructions).\n",
(unsigned int)(p - handle_tlbs));
- dump_handler("r3000_tlb_store", handle_tlbs, ARRAY_SIZE(handle_tlbs));
+ dump_handler("r3000_tlb_store", handle_tlbs, handle_tlbs_size);
}
static void __cpuinit build_r3000_tlb_modify_handler(void)
{
u32 *p = handle_tlbm;
+ const int handle_tlbm_size = handle_tlbm_end - handle_tlbm;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
- memset(handle_tlbm, 0, sizeof(handle_tlbm));
+ memset(handle_tlbm, 0, handle_tlbm_size * sizeof(handle_tlbm[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -1822,14 +1834,14 @@
uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
uasm_i_nop(&p);
- if ((p - handle_tlbm) > FASTPATH_SIZE)
+ if (p >= handle_tlbm_end)
panic("TLB modify handler fastpath space exceeded");
uasm_resolve_relocs(relocs, labels);
pr_debug("Wrote TLB modify handler fastpath (%u instructions).\n",
(unsigned int)(p - handle_tlbm));
- dump_handler("r3000_tlb_modify", handle_tlbm, ARRAY_SIZE(handle_tlbm));
+ dump_handler("r3000_tlb_modify", handle_tlbm, handle_tlbm_size);
}
#endif /* CONFIG_MIPS_PGD_C0_CONTEXT */
@@ -1893,11 +1905,12 @@
static void __cpuinit build_r4000_tlb_load_handler(void)
{
u32 *p = handle_tlbl;
+ const int handle_tlbl_size = handle_tlbl_end - handle_tlbl;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
struct work_registers wr;
- memset(handle_tlbl, 0, sizeof(handle_tlbl));
+ memset(handle_tlbl, 0, handle_tlbl_size * sizeof(handle_tlbl[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -1935,6 +1948,19 @@
uasm_i_nop(&p);
uasm_i_tlbr(&p);
+
+ switch (current_cpu_type()) {
+ default:
+ if (cpu_has_mips_r2) {
+ uasm_i_ehb(&p);
+
+ case CPU_CAVIUM_OCTEON:
+ case CPU_CAVIUM_OCTEON_PLUS:
+ case CPU_CAVIUM_OCTEON2:
+ break;
+ }
+ }
+
/* Examine entrylo 0 or 1 based on ptr. */
if (use_bbit_insns()) {
uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8);
@@ -1989,6 +2015,19 @@
uasm_i_nop(&p);
uasm_i_tlbr(&p);
+
+ switch (current_cpu_type()) {
+ default:
+ if (cpu_has_mips_r2) {
+ uasm_i_ehb(&p);
+
+ case CPU_CAVIUM_OCTEON:
+ case CPU_CAVIUM_OCTEON_PLUS:
+ case CPU_CAVIUM_OCTEON2:
+ break;
+ }
+ }
+
/* Examine entrylo 0 or 1 based on ptr. */
if (use_bbit_insns()) {
uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8);
@@ -2036,24 +2075,25 @@
uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff);
uasm_i_nop(&p);
- if ((p - handle_tlbl) > FASTPATH_SIZE)
+ if (p >= handle_tlbl_end)
panic("TLB load handler fastpath space exceeded");
uasm_resolve_relocs(relocs, labels);
pr_debug("Wrote TLB load handler fastpath (%u instructions).\n",
(unsigned int)(p - handle_tlbl));
- dump_handler("r4000_tlb_load", handle_tlbl, ARRAY_SIZE(handle_tlbl));
+ dump_handler("r4000_tlb_load", handle_tlbl, handle_tlbl_size);
}
static void __cpuinit build_r4000_tlb_store_handler(void)
{
u32 *p = handle_tlbs;
+ const int handle_tlbs_size = handle_tlbs_end - handle_tlbs;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
struct work_registers wr;
- memset(handle_tlbs, 0, sizeof(handle_tlbs));
+ memset(handle_tlbs, 0, handle_tlbs_size * sizeof(handle_tlbs[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -2090,24 +2130,25 @@
uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
uasm_i_nop(&p);
- if ((p - handle_tlbs) > FASTPATH_SIZE)
+ if (p >= handle_tlbs_end)
panic("TLB store handler fastpath space exceeded");
uasm_resolve_relocs(relocs, labels);
pr_debug("Wrote TLB store handler fastpath (%u instructions).\n",
(unsigned int)(p - handle_tlbs));
- dump_handler("r4000_tlb_store", handle_tlbs, ARRAY_SIZE(handle_tlbs));
+ dump_handler("r4000_tlb_store", handle_tlbs, handle_tlbs_size);
}
static void __cpuinit build_r4000_tlb_modify_handler(void)
{
u32 *p = handle_tlbm;
+ const int handle_tlbm_size = handle_tlbm_end - handle_tlbm;
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
struct work_registers wr;
- memset(handle_tlbm, 0, sizeof(handle_tlbm));
+ memset(handle_tlbm, 0, handle_tlbm_size * sizeof(handle_tlbm[0]));
memset(labels, 0, sizeof(labels));
memset(relocs, 0, sizeof(relocs));
@@ -2145,14 +2186,28 @@
uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
uasm_i_nop(&p);
- if ((p - handle_tlbm) > FASTPATH_SIZE)
+ if (p >= handle_tlbm_end)
panic("TLB modify handler fastpath space exceeded");
uasm_resolve_relocs(relocs, labels);
pr_debug("Wrote TLB modify handler fastpath (%u instructions).\n",
(unsigned int)(p - handle_tlbm));
- dump_handler("r4000_tlb_modify", handle_tlbm, ARRAY_SIZE(handle_tlbm));
+ dump_handler("r4000_tlb_modify", handle_tlbm, handle_tlbm_size);
+}
+
+static void __cpuinit flush_tlb_handlers(void)
+{
+ local_flush_icache_range((unsigned long)handle_tlbl,
+ (unsigned long)handle_tlbl_end);
+ local_flush_icache_range((unsigned long)handle_tlbs,
+ (unsigned long)handle_tlbs_end);
+ local_flush_icache_range((unsigned long)handle_tlbm,
+ (unsigned long)handle_tlbm_end);
+#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
+ local_flush_icache_range((unsigned long)tlbmiss_handler_setup_pgd,
+ (unsigned long)tlbmiss_handler_setup_pgd_end);
+#endif
}
void __cpuinit build_tlb_refill_handler(void)
@@ -2187,6 +2242,7 @@
build_r3000_tlb_load_handler();
build_r3000_tlb_store_handler();
build_r3000_tlb_modify_handler();
+ flush_tlb_handlers();
run_once++;
}
#else
@@ -2214,23 +2270,10 @@
build_r4000_tlb_modify_handler();
if (!cpu_has_local_ebase)
build_r4000_tlb_refill_handler();
+ flush_tlb_handlers();
run_once++;
}
if (cpu_has_local_ebase)
build_r4000_tlb_refill_handler();
}
}
-
-void __cpuinit flush_tlb_handlers(void)
-{
- local_flush_icache_range((unsigned long)handle_tlbl,
- (unsigned long)handle_tlbl + sizeof(handle_tlbl));
- local_flush_icache_range((unsigned long)handle_tlbs,
- (unsigned long)handle_tlbs + sizeof(handle_tlbs));
- local_flush_icache_range((unsigned long)handle_tlbm,
- (unsigned long)handle_tlbm + sizeof(handle_tlbm));
-#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
- local_flush_icache_range((unsigned long)tlbmiss_handler_setup_pgd_array,
- (unsigned long)tlbmiss_handler_setup_pgd_array + sizeof(handle_tlbm));
-#endif
-}
diff --git a/arch/mips/mti-malta/Makefile b/arch/mips/mti-malta/Makefile
index 0388fc8..72fdedb 100644
--- a/arch/mips/mti-malta/Makefile
+++ b/arch/mips/mti-malta/Makefile
@@ -10,7 +10,6 @@
malta-reset.o malta-setup.o malta-time.o
obj-$(CONFIG_EARLY_PRINTK) += malta-console.o
-obj-$(CONFIG_PCI) += malta-pci.o
# FIXME FIXME FIXME
obj-$(CONFIG_MIPS_MT_SMTC) += malta-smtc.o
diff --git a/arch/mips/mti-malta/malta-int.c b/arch/mips/mti-malta/malta-int.c
index 0a1339a..c69da37 100644
--- a/arch/mips/mti-malta/malta-int.c
+++ b/arch/mips/mti-malta/malta-int.c
@@ -422,8 +422,10 @@
*/
int __init gcmp_probe(unsigned long addr, unsigned long size)
{
- if (mips_revision_sconid != MIPS_REVISION_SCON_ROCIT) {
+ if ((mips_revision_sconid != MIPS_REVISION_SCON_ROCIT) &&
+ (mips_revision_sconid != MIPS_REVISION_SCON_GT64120)) {
gcmp_present = 0;
+ pr_debug("GCMP NOT present\n");
return gcmp_present;
}
diff --git a/arch/mips/mti-malta/malta-reset.c b/arch/mips/mti-malta/malta-reset.c
index 3294205..d627d4b 100644
--- a/arch/mips/mti-malta/malta-reset.c
+++ b/arch/mips/mti-malta/malta-reset.c
@@ -1,33 +1,18 @@
/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
* Carsten Langgaard, carstenl@mips.com
* Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
- *
- * ########################################################################
- *
- * This program is free software; you can distribute 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 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.
- *
- * ########################################################################
- *
- * Reset the MIPS boards.
- *
*/
-#include <linux/init.h>
+#include <linux/io.h>
#include <linux/pm.h>
-#include <asm/io.h>
#include <asm/reboot.h>
-#include <asm/mips-boards/generic.h>
+
+#define SOFTRES_REG 0x1f000500
+#define GORESET 0x42
static void mips_machine_restart(char *command)
{
@@ -45,7 +30,6 @@
__raw_writel(GORESET, softres_reg);
}
-
static int __init mips_reboot_setup(void)
{
_machine_restart = mips_machine_restart;
@@ -54,5 +38,4 @@
return 0;
}
-
arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/mti-sead3/sead3-reset.c b/arch/mips/mti-sead3/sead3-reset.c
index 20475c5..e6fb244 100644
--- a/arch/mips/mti-sead3/sead3-reset.c
+++ b/arch/mips/mti-sead3/sead3-reset.c
@@ -9,7 +9,9 @@
#include <linux/pm.h>
#include <asm/reboot.h>
-#include <asm/mips-boards/generic.h>
+
+#define SOFTRES_REG 0x1f000050
+#define GORESET 0x4d
static void mips_machine_restart(char *command)
{
@@ -35,5 +37,4 @@
return 0;
}
-
arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/netlogic/Kconfig b/arch/mips/netlogic/Kconfig
index e0873a3..2447bf9 100644
--- a/arch/mips/netlogic/Kconfig
+++ b/arch/mips/netlogic/Kconfig
@@ -51,4 +51,15 @@
config NLM_COMMON
bool
+config IOMMU_HELPER
+ bool
+
+config NEED_SG_DMA_LENGTH
+ bool
+
+config SWIOTLB
+ def_bool y
+ select NEED_SG_DMA_LENGTH
+ select IOMMU_HELPER
+
endif
diff --git a/arch/mips/netlogic/common/Makefile b/arch/mips/netlogic/common/Makefile
index 291372a..362739d 100644
--- a/arch/mips/netlogic/common/Makefile
+++ b/arch/mips/netlogic/common/Makefile
@@ -1,3 +1,5 @@
obj-y += irq.o time.o
+obj-y += nlm-dma.o
+obj-y += reset.o
obj-$(CONFIG_SMP) += smp.o smpboot.o
obj-$(CONFIG_EARLY_PRINTK) += earlycons.o
diff --git a/arch/mips/netlogic/common/irq.c b/arch/mips/netlogic/common/irq.c
index 9f84c60..73facb2 100644
--- a/arch/mips/netlogic/common/irq.c
+++ b/arch/mips/netlogic/common/irq.c
@@ -253,13 +253,12 @@
node = nlm_nodeid();
eirr = read_c0_eirr_and_eimr();
-
- i = __ilog2_u64(eirr);
- if (i == -1)
+ if (eirr == 0)
return;
+ i = __ffs64(eirr);
/* per-CPU IRQs don't need translation */
- if (eirr & PERCPU_IRQ_MASK) {
+ if (i < PIC_IRQ_BASE) {
do_IRQ(i);
return;
}
diff --git a/arch/mips/netlogic/common/nlm-dma.c b/arch/mips/netlogic/common/nlm-dma.c
new file mode 100644
index 0000000..f3d4ae8
--- /dev/null
+++ b/arch/mips/netlogic/common/nlm-dma.c
@@ -0,0 +1,107 @@
+/*
+* Copyright (C) 2003-2013 Broadcom Corporation
+* 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 Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/bootmem.h>
+#include <linux/export.h>
+#include <linux/swiotlb.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/bootinfo.h>
+
+static char *nlm_swiotlb;
+
+static void *nlm_dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+{
+ void *ret;
+
+ if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
+ return ret;
+
+ /* ignore region specifiers */
+ gfp &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
+
+#ifdef CONFIG_ZONE_DMA32
+ if (dev->coherent_dma_mask <= DMA_BIT_MASK(32))
+ gfp |= __GFP_DMA32;
+#endif
+
+ /* Don't invoke OOM killer */
+ gfp |= __GFP_NORETRY;
+
+ return swiotlb_alloc_coherent(dev, size, dma_handle, gfp);
+}
+
+static void nlm_dma_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs)
+{
+ int order = get_order(size);
+
+ if (dma_release_from_coherent(dev, order, vaddr))
+ return;
+
+ swiotlb_free_coherent(dev, size, vaddr, dma_handle);
+}
+
+struct dma_map_ops nlm_swiotlb_dma_ops = {
+ .alloc = nlm_dma_alloc_coherent,
+ .free = nlm_dma_free_coherent,
+ .map_page = swiotlb_map_page,
+ .unmap_page = swiotlb_unmap_page,
+ .map_sg = swiotlb_map_sg_attrs,
+ .unmap_sg = swiotlb_unmap_sg_attrs,
+ .sync_single_for_cpu = swiotlb_sync_single_for_cpu,
+ .sync_single_for_device = swiotlb_sync_single_for_device,
+ .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
+ .sync_sg_for_device = swiotlb_sync_sg_for_device,
+ .mapping_error = swiotlb_dma_mapping_error,
+ .dma_supported = swiotlb_dma_supported
+};
+
+void __init plat_swiotlb_setup(void)
+{
+ size_t swiotlbsize;
+ unsigned long swiotlb_nslabs;
+
+ swiotlbsize = 1 << 20; /* 1 MB for now */
+ swiotlb_nslabs = swiotlbsize >> IO_TLB_SHIFT;
+ swiotlb_nslabs = ALIGN(swiotlb_nslabs, IO_TLB_SEGSIZE);
+ swiotlbsize = swiotlb_nslabs << IO_TLB_SHIFT;
+
+ nlm_swiotlb = alloc_bootmem_low_pages(swiotlbsize);
+ swiotlb_init_with_tbl(nlm_swiotlb, swiotlb_nslabs, 1);
+}
diff --git a/arch/mips/netlogic/common/reset.S b/arch/mips/netlogic/common/reset.S
new file mode 100644
index 0000000..adb1828
--- /dev/null
+++ b/arch/mips/netlogic/common/reset.S
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2003-2013 Broadcom Corporation.
+ * 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 Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/init.h>
+
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/asmmacro.h>
+#include <asm/addrspace.h>
+
+#include <asm/netlogic/common.h>
+
+#include <asm/netlogic/xlp-hal/iomap.h>
+#include <asm/netlogic/xlp-hal/xlp.h>
+#include <asm/netlogic/xlp-hal/sys.h>
+#include <asm/netlogic/xlp-hal/cpucontrol.h>
+
+#define CP0_EBASE $15
+#define SYS_CPU_COHERENT_BASE(node) CKSEG1ADDR(XLP_DEFAULT_IO_BASE) + \
+ XLP_IO_SYS_OFFSET(node) + XLP_IO_PCI_HDRSZ + \
+ SYS_CPU_NONCOHERENT_MODE * 4
+
+/* Enable XLP features and workarounds in the LSU */
+.macro xlp_config_lsu
+ li t0, LSU_DEFEATURE
+ mfcr t1, t0
+
+ lui t2, 0xc080 /* SUE, Enable Unaligned Access, L2HPE */
+ or t1, t1, t2
+ mtcr t1, t0
+
+ li t0, ICU_DEFEATURE
+ mfcr t1, t0
+ ori t1, 0x1000 /* Enable Icache partitioning */
+ mtcr t1, t0
+
+ li t0, SCHED_DEFEATURE
+ lui t1, 0x0100 /* Disable BRU accepting ALU ops */
+ mtcr t1, t0
+.endm
+
+/*
+ * Low level flush for L1D cache on XLP, the normal cache ops does
+ * not do the complete and correct cache flush.
+ */
+.macro xlp_flush_l1_dcache
+ li t0, LSU_DEBUG_DATA0
+ li t1, LSU_DEBUG_ADDR
+ li t2, 0 /* index */
+ li t3, 0x1000 /* loop count */
+1:
+ sll v0, t2, 5
+ mtcr zero, t0
+ ori v1, v0, 0x3 /* way0 | write_enable | write_active */
+ mtcr v1, t1
+2:
+ mfcr v1, t1
+ andi v1, 0x1 /* wait for write_active == 0 */
+ bnez v1, 2b
+ nop
+ mtcr zero, t0
+ ori v1, v0, 0x7 /* way1 | write_enable | write_active */
+ mtcr v1, t1
+3:
+ mfcr v1, t1
+ andi v1, 0x1 /* wait for write_active == 0 */
+ bnez v1, 3b
+ nop
+ addi t2, 1
+ bne t3, t2, 1b
+ nop
+.endm
+
+/*
+ * nlm_reset_entry will be copied to the reset entry point for
+ * XLR and XLP. The XLP cores start here when they are woken up. This
+ * is also the NMI entry point.
+ *
+ * We use scratch reg 6/7 to save k0/k1 and check for NMI first.
+ *
+ * The data corresponding to reset/NMI is stored at RESET_DATA_PHYS
+ * location, this will have the thread mask (used when core is woken up)
+ * and the current NMI handler in case we reached here for an NMI.
+ *
+ * When a core or thread is newly woken up, it marks itself ready and
+ * loops in a 'wait'. When the CPU really needs waking up, we send an NMI
+ * IPI to it, with the NMI handler set to prom_boot_secondary_cpus
+ */
+ .set noreorder
+ .set noat
+ .set arch=xlr /* for mfcr/mtcr, XLR is sufficient */
+
+FEXPORT(nlm_reset_entry)
+ dmtc0 k0, $22, 6
+ dmtc0 k1, $22, 7
+ mfc0 k0, CP0_STATUS
+ li k1, 0x80000
+ and k1, k0, k1
+ beqz k1, 1f /* go to real reset entry */
+ nop
+ li k1, CKSEG1ADDR(RESET_DATA_PHYS) /* NMI */
+ ld k0, BOOT_NMI_HANDLER(k1)
+ jr k0
+ nop
+
+1: /* Entry point on core wakeup */
+ mfc0 t0, CP0_EBASE, 1
+ mfc0 t1, CP0_EBASE, 1
+ srl t1, 5
+ andi t1, 0x3 /* t1 <- node */
+ li t2, 0x40000
+ mul t3, t2, t1 /* t3 = node * 0x40000 */
+ srl t0, t0, 2
+ and t0, t0, 0x7 /* t0 <- core */
+ li t1, 0x1
+ sll t0, t1, t0
+ nor t0, t0, zero /* t0 <- ~(1 << core) */
+ li t2, SYS_CPU_COHERENT_BASE(0)
+ add t2, t2, t3 /* t2 <- SYS offset for node */
+ lw t1, 0(t2)
+ and t1, t1, t0
+ sw t1, 0(t2)
+
+ /* read back to ensure complete */
+ lw t1, 0(t2)
+ sync
+
+ /* Configure LSU on Non-0 Cores. */
+ xlp_config_lsu
+ /* FALL THROUGH */
+
+/*
+ * Wake up sibling threads from the initial thread in
+ * a core.
+ */
+EXPORT(nlm_boot_siblings)
+ /* core L1D flush before enable threads */
+ xlp_flush_l1_dcache
+ /* Enable hw threads by writing to MAP_THREADMODE of the core */
+ li t0, CKSEG1ADDR(RESET_DATA_PHYS)
+ lw t1, BOOT_THREAD_MODE(t0) /* t1 <- thread mode */
+ li t0, ((CPU_BLOCKID_MAP << 8) | MAP_THREADMODE)
+ mfcr t2, t0
+ or t2, t2, t1
+ mtcr t2, t0
+
+ /*
+ * The new hardware thread starts at the next instruction
+ * For all the cases other than core 0 thread 0, we will
+ * jump to the secondary wait function.
+ */
+ mfc0 v0, CP0_EBASE, 1
+ andi v0, 0x3ff /* v0 <- node/core */
+
+ beqz v0, 4f /* boot cpu (cpuid == 0)? */
+ nop
+
+ /* setup status reg */
+ move t1, zero
+#ifdef CONFIG_64BIT
+ ori t1, ST0_KX
+#endif
+ mtc0 t1, CP0_STATUS
+
+ /* mark CPU ready, careful here, previous mtcr trashed registers */
+ li t3, CKSEG1ADDR(RESET_DATA_PHYS)
+ ADDIU t1, t3, BOOT_CPU_READY
+ sll v1, v0, 2
+ PTR_ADDU t1, v1
+ li t2, 1
+ sw t2, 0(t1)
+ /* Wait until NMI hits */
+3: wait
+ b 3b
+ nop
+
+ /*
+ * For the boot CPU, we have to restore registers and
+ * return
+ */
+4: dmfc0 t0, $4, 2 /* restore SP from UserLocal */
+ li t1, 0xfadebeef
+ dmtc0 t1, $4, 2 /* restore SP from UserLocal */
+ PTR_SUBU sp, t0, PT_SIZE
+ RESTORE_ALL
+ jr ra
+ nop
+EXPORT(nlm_reset_entry_end)
+
+LEAF(nlm_init_boot_cpu)
+#ifdef CONFIG_CPU_XLP
+ xlp_config_lsu
+#endif
+ jr ra
+ nop
+END(nlm_init_boot_cpu)
diff --git a/arch/mips/netlogic/common/smp.c b/arch/mips/netlogic/common/smp.c
index ffba524..885d293 100644
--- a/arch/mips/netlogic/common/smp.c
+++ b/arch/mips/netlogic/common/smp.c
@@ -145,7 +145,6 @@
* Boot all other cpus in the system, initialize them, and bring them into
* the boot function
*/
-int nlm_cpu_ready[NR_CPUS];
unsigned long nlm_next_gp;
unsigned long nlm_next_sp;
static cpumask_t phys_cpu_present_mask;
@@ -168,6 +167,7 @@
{
unsigned int boot_cpu;
int num_cpus, i, ncore;
+ volatile u32 *cpu_ready = nlm_get_boot_data(BOOT_CPU_READY);
char buf[64];
boot_cpu = hard_smp_processor_id();
@@ -181,10 +181,10 @@
num_cpus = 1;
for (i = 0; i < NR_CPUS; i++) {
/*
- * nlm_cpu_ready array is not set for the boot_cpu,
+ * cpu_ready array is not set for the boot_cpu,
* it is only set for ASPs (see smpboot.S)
*/
- if (nlm_cpu_ready[i]) {
+ if (cpu_ready[i]) {
cpumask_set_cpu(i, &phys_cpu_present_mask);
__cpu_number_map[i] = num_cpus;
__cpu_logical_map[num_cpus] = i;
@@ -254,21 +254,15 @@
int __cpuinit nlm_wakeup_secondary_cpus(void)
{
- unsigned long reset_vec;
- char *reset_data;
+ u32 *reset_data;
int threadmode;
- /* Update reset entry point with CPU init code */
- reset_vec = CKSEG1ADDR(RESET_VEC_PHYS);
- memcpy((void *)reset_vec, (void *)nlm_reset_entry,
- (nlm_reset_entry_end - nlm_reset_entry));
-
/* verify the mask and setup core config variables */
threadmode = nlm_parse_cpumask(&nlm_cpumask);
/* Setup CPU init parameters */
- reset_data = (char *)CKSEG1ADDR(RESET_DATA_PHYS);
- *(int *)(reset_data + BOOT_THREAD_MODE) = threadmode;
+ reset_data = nlm_get_boot_data(BOOT_THREAD_MODE);
+ *reset_data = threadmode;
#ifdef CONFIG_CPU_XLP
xlp_wakeup_secondary_cpus();
diff --git a/arch/mips/netlogic/common/smpboot.S b/arch/mips/netlogic/common/smpboot.S
index 0265174..528c46c 100644
--- a/arch/mips/netlogic/common/smpboot.S
+++ b/arch/mips/netlogic/common/smpboot.S
@@ -50,197 +50,12 @@
#include <asm/netlogic/xlp-hal/cpucontrol.h>
#define CP0_EBASE $15
-#define SYS_CPU_COHERENT_BASE(node) CKSEG1ADDR(XLP_DEFAULT_IO_BASE) + \
- XLP_IO_SYS_OFFSET(node) + XLP_IO_PCI_HDRSZ + \
- SYS_CPU_NONCOHERENT_MODE * 4
-
-#define XLP_AX_WORKAROUND /* enable Ax silicon workarounds */
-
-/* Enable XLP features and workarounds in the LSU */
-.macro xlp_config_lsu
- li t0, LSU_DEFEATURE
- mfcr t1, t0
-
- lui t2, 0xc080 /* SUE, Enable Unaligned Access, L2HPE */
- or t1, t1, t2
-#ifdef XLP_AX_WORKAROUND
- li t2, ~0xe /* S1RCM */
- and t1, t1, t2
-#endif
- mtcr t1, t0
-
- li t0, ICU_DEFEATURE
- mfcr t1, t0
- ori t1, 0x1000 /* Enable Icache partitioning */
- mtcr t1, t0
-
-
-#ifdef XLP_AX_WORKAROUND
- li t0, SCHED_DEFEATURE
- lui t1, 0x0100 /* Disable BRU accepting ALU ops */
- mtcr t1, t0
-#endif
-.endm
-
-/*
- * This is the code that will be copied to the reset entry point for
- * XLR and XLP. The XLP cores start here when they are woken up. This
- * is also the NMI entry point.
- */
-.macro xlp_flush_l1_dcache
- li t0, LSU_DEBUG_DATA0
- li t1, LSU_DEBUG_ADDR
- li t2, 0 /* index */
- li t3, 0x1000 /* loop count */
-1:
- sll v0, t2, 5
- mtcr zero, t0
- ori v1, v0, 0x3 /* way0 | write_enable | write_active */
- mtcr v1, t1
-2:
- mfcr v1, t1
- andi v1, 0x1 /* wait for write_active == 0 */
- bnez v1, 2b
- nop
- mtcr zero, t0
- ori v1, v0, 0x7 /* way1 | write_enable | write_active */
- mtcr v1, t1
-3:
- mfcr v1, t1
- andi v1, 0x1 /* wait for write_active == 0 */
- bnez v1, 3b
- nop
- addi t2, 1
- bne t3, t2, 1b
- nop
-.endm
-
-/*
- * The cores can come start when they are woken up. This is also the NMI
- * entry, so check that first.
- *
- * The data corresponding to reset/NMI is stored at RESET_DATA_PHYS
- * location, this will have the thread mask (used when core is woken up)
- * and the current NMI handler in case we reached here for an NMI.
- *
- * When a core or thread is newly woken up, it loops in a 'wait'. When
- * the CPU really needs waking up, we send an NMI to it, with the NMI
- * handler set to prom_boot_secondary_cpus
- */
.set noreorder
.set noat
- .set arch=xlr /* for mfcr/mtcr, XLR is sufficient */
-
-FEXPORT(nlm_reset_entry)
- dmtc0 k0, $22, 6
- dmtc0 k1, $22, 7
- mfc0 k0, CP0_STATUS
- li k1, 0x80000
- and k1, k0, k1
- beqz k1, 1f /* go to real reset entry */
- nop
- li k1, CKSEG1ADDR(RESET_DATA_PHYS) /* NMI */
- ld k0, BOOT_NMI_HANDLER(k1)
- jr k0
- nop
-
-1: /* Entry point on core wakeup */
- mfc0 t0, CP0_EBASE, 1
- mfc0 t1, CP0_EBASE, 1
- srl t1, 5
- andi t1, 0x3 /* t1 <- node */
- li t2, 0x40000
- mul t3, t2, t1 /* t3 = node * 0x40000 */
- srl t0, t0, 2
- and t0, t0, 0x7 /* t0 <- core */
- li t1, 0x1
- sll t0, t1, t0
- nor t0, t0, zero /* t0 <- ~(1 << core) */
- li t2, SYS_CPU_COHERENT_BASE(0)
- add t2, t2, t3 /* t2 <- SYS offset for node */
- lw t1, 0(t2)
- and t1, t1, t0
- sw t1, 0(t2)
-
- /* read back to ensure complete */
- lw t1, 0(t2)
- sync
-
- /* Configure LSU on Non-0 Cores. */
- xlp_config_lsu
- /* FALL THROUGH */
-
-/*
- * Wake up sibling threads from the initial thread in
- * a core.
- */
-EXPORT(nlm_boot_siblings)
- /* core L1D flush before enable threads */
- xlp_flush_l1_dcache
- /* Enable hw threads by writing to MAP_THREADMODE of the core */
- li t0, CKSEG1ADDR(RESET_DATA_PHYS)
- lw t1, BOOT_THREAD_MODE(t0) /* t1 <- thread mode */
- li t0, ((CPU_BLOCKID_MAP << 8) | MAP_THREADMODE)
- mfcr t2, t0
- or t2, t2, t1
- mtcr t2, t0
-
- /*
- * The new hardware thread starts at the next instruction
- * For all the cases other than core 0 thread 0, we will
- * jump to the secondary wait function.
- */
- mfc0 v0, CP0_EBASE, 1
- andi v0, 0x3ff /* v0 <- node/core */
-
- /* Init MMU in the first thread after changing THREAD_MODE
- * register (Ax Errata?)
- */
- andi v1, v0, 0x3 /* v1 <- thread id */
- bnez v1, 2f
- nop
-
- li t0, MMU_SETUP
- li t1, 0
- mtcr t1, t0
- _ehb
-
-2: beqz v0, 4f /* boot cpu (cpuid == 0)? */
- nop
-
- /* setup status reg */
- move t1, zero
-#ifdef CONFIG_64BIT
- ori t1, ST0_KX
-#endif
- mtc0 t1, CP0_STATUS
- /* mark CPU ready */
- PTR_LA t1, nlm_cpu_ready
- sll v1, v0, 2
- PTR_ADDU t1, v1
- li t2, 1
- sw t2, 0(t1)
- /* Wait until NMI hits */
-3: wait
- j 3b
- nop
-
- /*
- * For the boot CPU, we have to restore registers and
- * return
- */
-4: dmfc0 t0, $4, 2 /* restore SP from UserLocal */
- li t1, 0xfadebeef
- dmtc0 t1, $4, 2 /* restore SP from UserLocal */
- PTR_SUBU sp, t0, PT_SIZE
- RESTORE_ALL
- jr ra
- nop
-EXPORT(nlm_reset_entry_end)
+ .set arch=xlr /* for mfcr/mtcr, XLR is sufficient */
FEXPORT(xlp_boot_core0_siblings) /* "Master" cpu starts from here */
- xlp_config_lsu
dmtc0 sp, $4, 2 /* SP saved in UserLocal */
SAVE_ALL
sync
@@ -294,8 +109,9 @@
andi t2, t0, 0x3 /* thread num */
sll t0, 2 /* offset in cpu array */
- PTR_LA t1, nlm_cpu_ready /* mark CPU ready */
- PTR_ADDU t1, t0
+ li t3, CKSEG1ADDR(RESET_DATA_PHYS)
+ ADDIU t1, t3, BOOT_CPU_READY
+ ADDU t1, t0
li t3, 1
sw t3, 0(t1)
@@ -321,7 +137,7 @@
mtcr t1, t0 /* update core control */
1: wait
- j 1b
+ b 1b
nop
END(nlm_rmiboot_preboot)
__FINIT
diff --git a/arch/mips/netlogic/xlp/Makefile b/arch/mips/netlogic/xlp/Makefile
index a84d6ed..85ac4a8 100644
--- a/arch/mips/netlogic/xlp/Makefile
+++ b/arch/mips/netlogic/xlp/Makefile
@@ -1,3 +1,3 @@
-obj-y += setup.o nlm_hal.o
+obj-y += setup.o nlm_hal.o cop2-ex.o dt.o
obj-$(CONFIG_SMP) += wakeup.o
obj-$(CONFIG_USB) += usb-init.o
diff --git a/arch/mips/netlogic/xlp/cop2-ex.c b/arch/mips/netlogic/xlp/cop2-ex.c
new file mode 100644
index 0000000..52bc5de
--- /dev/null
+++ b/arch/mips/netlogic/xlp/cop2-ex.c
@@ -0,0 +1,118 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2013 Broadcom Corporation.
+ *
+ * based on arch/mips/cavium-octeon/cpu.c
+ * Copyright (C) 2009 Wind River Systems,
+ * written by Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <linux/init.h>
+#include <linux/irqflags.h>
+#include <linux/notifier.h>
+#include <linux/prefetch.h>
+#include <linux/sched.h>
+
+#include <asm/cop2.h>
+#include <asm/current.h>
+#include <asm/mipsregs.h>
+#include <asm/page.h>
+
+#include <asm/netlogic/mips-extns.h>
+
+/*
+ * 64 bit ops are done in inline assembly to support 32 bit
+ * compilation
+ */
+void nlm_cop2_save(struct nlm_cop2_state *r)
+{
+ asm volatile(
+ ".set push\n"
+ ".set noat\n"
+ "dmfc2 $1, $0, 0\n"
+ "sd $1, 0(%1)\n"
+ "dmfc2 $1, $0, 1\n"
+ "sd $1, 8(%1)\n"
+ "dmfc2 $1, $0, 2\n"
+ "sd $1, 16(%1)\n"
+ "dmfc2 $1, $0, 3\n"
+ "sd $1, 24(%1)\n"
+ "dmfc2 $1, $1, 0\n"
+ "sd $1, 0(%2)\n"
+ "dmfc2 $1, $1, 1\n"
+ "sd $1, 8(%2)\n"
+ "dmfc2 $1, $1, 2\n"
+ "sd $1, 16(%2)\n"
+ "dmfc2 $1, $1, 3\n"
+ "sd $1, 24(%2)\n"
+ ".set pop\n"
+ : "=m"(*r)
+ : "r"(r->tx), "r"(r->rx));
+
+ r->tx_msg_status = __read_32bit_c2_register($2, 0);
+ r->rx_msg_status = __read_32bit_c2_register($3, 0) & 0x0fffffff;
+}
+
+void nlm_cop2_restore(struct nlm_cop2_state *r)
+{
+ u32 rstat;
+
+ asm volatile(
+ ".set push\n"
+ ".set noat\n"
+ "ld $1, 0(%1)\n"
+ "dmtc2 $1, $0, 0\n"
+ "ld $1, 8(%1)\n"
+ "dmtc2 $1, $0, 1\n"
+ "ld $1, 16(%1)\n"
+ "dmtc2 $1, $0, 2\n"
+ "ld $1, 24(%1)\n"
+ "dmtc2 $1, $0, 3\n"
+ "ld $1, 0(%2)\n"
+ "dmtc2 $1, $1, 0\n"
+ "ld $1, 8(%2)\n"
+ "dmtc2 $1, $1, 1\n"
+ "ld $1, 16(%2)\n"
+ "dmtc2 $1, $1, 2\n"
+ "ld $1, 24(%2)\n"
+ "dmtc2 $1, $1, 3\n"
+ ".set pop\n"
+ : : "m"(*r), "r"(r->tx), "r"(r->rx));
+
+ __write_32bit_c2_register($2, 0, r->tx_msg_status);
+ rstat = __read_32bit_c2_register($3, 0) & 0xf0000000u;
+ __write_32bit_c2_register($3, 0, r->rx_msg_status | rstat);
+}
+
+static int nlm_cu2_call(struct notifier_block *nfb, unsigned long action,
+ void *data)
+{
+ unsigned long flags;
+ unsigned int status;
+
+ switch (action) {
+ case CU2_EXCEPTION:
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ break;
+ local_irq_save(flags);
+ KSTK_STATUS(current) |= ST0_CU2;
+ status = read_c0_status();
+ write_c0_status(status | ST0_CU2);
+ nlm_cop2_restore(&(current->thread.cp2));
+ write_c0_status(status & ~ST0_CU2);
+ local_irq_restore(flags);
+ pr_info("COP2 access enabled for pid %d (%s)\n",
+ current->pid, current->comm);
+ return NOTIFY_BAD; /* Don't call default notifier */
+ }
+
+ return NOTIFY_OK; /* Let default notifier send signals */
+}
+
+static int __init nlm_cu2_setup(void)
+{
+ return cu2_notifier(nlm_cu2_call, 0);
+}
+early_initcall(nlm_cu2_setup);
diff --git a/arch/mips/netlogic/xlp/dt.c b/arch/mips/netlogic/xlp/dt.c
new file mode 100644
index 0000000..a15cdbb
--- /dev/null
+++ b/arch/mips/netlogic/xlp/dt.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2003-2013 Broadcom Corporation.
+ * 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 Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bootmem.h>
+
+#include <linux/of_fdt.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
+extern u32 __dtb_xlp_evp_begin[], __dtb_xlp_svp_begin[], __dtb_start[];
+
+void __init *xlp_dt_init(void *fdtp)
+{
+ if (!fdtp) {
+ switch (current_cpu_data.processor_id & 0xff00) {
+#ifdef CONFIG_DT_XLP_SVP
+ case PRID_IMP_NETLOGIC_XLP3XX:
+ fdtp = __dtb_xlp_svp_begin;
+ break;
+#endif
+#ifdef CONFIG_DT_XLP_EVP
+ case PRID_IMP_NETLOGIC_XLP8XX:
+ fdtp = __dtb_xlp_evp_begin;
+ break;
+#endif
+ default:
+ /* Pick a built-in if any, and hope for the best */
+ fdtp = __dtb_start;
+ break;
+ }
+ }
+ initial_boot_params = fdtp;
+ return fdtp;
+}
+
+void __init device_tree_init(void)
+{
+ unsigned long base, size;
+
+ if (!initial_boot_params)
+ return;
+
+ base = virt_to_phys((void *)initial_boot_params);
+ size = be32_to_cpu(initial_boot_params->totalsize);
+
+ /* Before we do anything, lets reserve the dt blob */
+ reserve_bootmem(base, size, BOOTMEM_DEFAULT);
+
+ unflatten_device_tree();
+
+ /* free the space reserved for the dt blob */
+ free_bootmem(base, size);
+}
+
+static struct of_device_id __initdata xlp_ids[] = {
+ { .compatible = "simple-bus", },
+ {},
+};
+
+int __init xlp8xx_ds_publish_devices(void)
+{
+ if (!of_have_populated_dt())
+ return 0;
+ return of_platform_bus_probe(NULL, xlp_ids, NULL);
+}
+
+device_initcall(xlp8xx_ds_publish_devices);
diff --git a/arch/mips/netlogic/xlp/setup.c b/arch/mips/netlogic/xlp/setup.c
index eaa99d2..7b638f7 100644
--- a/arch/mips/netlogic/xlp/setup.c
+++ b/arch/mips/netlogic/xlp/setup.c
@@ -33,19 +33,13 @@
*/
#include <linux/kernel.h>
-#include <linux/serial_8250.h>
-#include <linux/pm.h>
-#include <linux/bootmem.h>
+#include <linux/of_fdt.h>
#include <asm/idle.h>
#include <asm/reboot.h>
#include <asm/time.h>
#include <asm/bootinfo.h>
-#include <linux/of_fdt.h>
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-
#include <asm/netlogic/haldefs.h>
#include <asm/netlogic/common.h>
@@ -57,7 +51,6 @@
struct nlm_soc_info nlm_nodes[NLM_NR_NODES];
cpumask_t nlm_cpumask = CPU_MASK_CPU0;
unsigned int nlm_threads_per_core;
-extern u32 __dtb_xlp_evp_begin[], __dtb_xlp_svp_begin[], __dtb_start[];
static void nlm_linux_exit(void)
{
@@ -68,41 +61,28 @@
cpu_wait();
}
+static void nlm_fixup_mem(void)
+{
+ const int pref_backup = 512;
+ int i;
+
+ for (i = 0; i < boot_mem_map.nr_map; i++) {
+ if (boot_mem_map.map[i].type != BOOT_MEM_RAM)
+ continue;
+ boot_mem_map.map[i].size -= pref_backup;
+ }
+}
+
void __init plat_mem_setup(void)
{
- void *fdtp;
-
panic_timeout = 5;
_machine_restart = (void (*)(char *))nlm_linux_exit;
_machine_halt = nlm_linux_exit;
pm_power_off = nlm_linux_exit;
- /*
- * If no FDT pointer is passed in, use the built-in FDT.
- * device_tree_init() does not handle CKSEG0 pointers in
- * 64-bit, so convert pointer.
- */
- fdtp = (void *)(long)fw_arg0;
- if (!fdtp) {
- switch (current_cpu_data.processor_id & 0xff00) {
-#ifdef CONFIG_DT_XLP_SVP
- case PRID_IMP_NETLOGIC_XLP3XX:
- fdtp = __dtb_xlp_svp_begin;
- break;
-#endif
-#ifdef CONFIG_DT_XLP_EVP
- case PRID_IMP_NETLOGIC_XLP8XX:
- fdtp = __dtb_xlp_evp_begin;
- break;
-#endif
- default:
- /* Pick a built-in if any, and hope for the best */
- fdtp = __dtb_start;
- break;
- }
- }
- fdtp = phys_to_virt(__pa(fdtp));
- early_init_devtree(fdtp);
+ /* memory and bootargs from DT */
+ early_init_devtree(initial_boot_params);
+ nlm_fixup_mem();
}
const char *get_system_type(void)
@@ -131,9 +111,19 @@
void __init prom_init(void)
{
+ void *reset_vec;
+
nlm_io_base = CKSEG1ADDR(XLP_DEFAULT_IO_BASE);
+ nlm_init_boot_cpu();
xlp_mmu_init();
nlm_node_init(0);
+ xlp_dt_init((void *)(long)fw_arg0);
+
+ /* Update reset entry point with CPU init code */
+ reset_vec = (void *)CKSEG1ADDR(RESET_VEC_PHYS);
+ memset(reset_vec, 0, RESET_VEC_SIZE);
+ memcpy(reset_vec, (void *)nlm_reset_entry,
+ (nlm_reset_entry_end - nlm_reset_entry));
#ifdef CONFIG_SMP
cpumask_setall(&nlm_cpumask);
@@ -145,36 +135,3 @@
register_smp_ops(&nlm_smp_ops);
#endif
}
-
-void __init device_tree_init(void)
-{
- unsigned long base, size;
-
- if (!initial_boot_params)
- return;
-
- base = virt_to_phys((void *)initial_boot_params);
- size = be32_to_cpu(initial_boot_params->totalsize);
-
- /* Before we do anything, lets reserve the dt blob */
- reserve_bootmem(base, size, BOOTMEM_DEFAULT);
-
- unflatten_device_tree();
-
- /* free the space reserved for the dt blob */
- free_bootmem(base, size);
-}
-
-static struct of_device_id __initdata xlp_ids[] = {
- { .compatible = "simple-bus", },
- {},
-};
-
-int __init xlp8xx_ds_publish_devices(void)
-{
- if (!of_have_populated_dt())
- return 0;
- return of_platform_bus_probe(NULL, xlp_ids, NULL);
-}
-
-device_initcall(xlp8xx_ds_publish_devices);
diff --git a/arch/mips/netlogic/xlp/wakeup.c b/arch/mips/netlogic/xlp/wakeup.c
index abb3e08..0cce37c 100644
--- a/arch/mips/netlogic/xlp/wakeup.c
+++ b/arch/mips/netlogic/xlp/wakeup.c
@@ -77,12 +77,28 @@
return count != 0;
}
+static int wait_for_cpus(int cpu, int bootcpu)
+{
+ volatile uint32_t *cpu_ready = nlm_get_boot_data(BOOT_CPU_READY);
+ int i, count, notready;
+
+ count = 0x20000000;
+ do {
+ notready = nlm_threads_per_core;
+ for (i = 0; i < nlm_threads_per_core; i++)
+ if (cpu_ready[cpu + i] || cpu == bootcpu)
+ --notready;
+ } while (notready != 0 && --count > 0);
+
+ return count != 0;
+}
+
static void xlp_enable_secondary_cores(const cpumask_t *wakeup_mask)
{
struct nlm_soc_info *nodep;
uint64_t syspcibase;
uint32_t syscoremask;
- int core, n, cpu, count, val;
+ int core, n, cpu;
for (n = 0; n < NLM_NR_NODES; n++) {
syspcibase = nlm_get_sys_pcibase(n);
@@ -122,11 +138,8 @@
/* core is up */
nodep->coremask |= 1u << core;
- /* spin until the first hw thread sets its ready */
- count = 0x20000000;
- do {
- val = *(volatile int *)&nlm_cpu_ready[cpu];
- } while (val == 0 && --count > 0);
+ /* spin until the hw threads sets their ready */
+ wait_for_cpus(cpu, 0);
}
}
}
@@ -138,6 +151,7 @@
* first wakeup core 0 threads
*/
xlp_boot_core0_siblings();
+ wait_for_cpus(0, 0);
/* now get other cores out of reset */
xlp_enable_secondary_cores(&nlm_cpumask);
diff --git a/arch/mips/netlogic/xlr/fmn.c b/arch/mips/netlogic/xlr/fmn.c
index 4d74f03..d428e84 100644
--- a/arch/mips/netlogic/xlr/fmn.c
+++ b/arch/mips/netlogic/xlr/fmn.c
@@ -74,13 +74,13 @@
struct nlm_fmn_msg msg;
uint32_t mflags, bkt_status;
- mflags = nlm_cop2_enable();
+ mflags = nlm_cop2_enable_irqsave();
/* Disable message ring interrupt */
nlm_fmn_setup_intr(irq, 0);
while (1) {
/* 8 bkts per core, [24:31] each bit represents one bucket
* Bit is Zero if bucket is not empty */
- bkt_status = (nlm_read_c2_status() >> 24) & 0xff;
+ bkt_status = (nlm_read_c2_status0() >> 24) & 0xff;
if (bkt_status == 0xff)
break;
for (bucket = 0; bucket < 8; bucket++) {
@@ -97,16 +97,16 @@
pr_warn("No msgring handler for stnid %d\n",
src_stnid);
else {
- nlm_cop2_restore(mflags);
+ nlm_cop2_disable_irqrestore(mflags);
hndlr->action(bucket, src_stnid, size, code,
&msg, hndlr->arg);
- mflags = nlm_cop2_enable();
+ mflags = nlm_cop2_enable_irqsave();
}
}
};
/* Enable message ring intr, to any thread in core */
nlm_fmn_setup_intr(irq, (1 << nlm_threads_per_core) - 1);
- nlm_cop2_restore(mflags);
+ nlm_cop2_disable_irqrestore(mflags);
return IRQ_HANDLED;
}
@@ -128,7 +128,7 @@
bucket_sizes = xlr_board_fmn_config.bucket_size;
cpu_fmn_info = &xlr_board_fmn_config.cpu[id];
- flags = nlm_cop2_enable();
+ flags = nlm_cop2_enable_irqsave();
/* Setup bucket sizes for the core. */
nlm_write_c2_bucksize(0, bucket_sizes[id * 8 + 0]);
@@ -166,7 +166,7 @@
/* enable FMN interrupts on this CPU */
nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1);
- nlm_cop2_restore(flags);
+ nlm_cop2_disable_irqrestore(flags);
}
@@ -198,7 +198,7 @@
/* setup irq only once */
setup_irq(IRQ_FMN, &fmn_irqaction);
- flags = nlm_cop2_enable();
+ flags = nlm_cop2_enable_irqsave();
nlm_fmn_setup_intr(IRQ_FMN, (1 << nlm_threads_per_core) - 1);
- nlm_cop2_restore(flags);
+ nlm_cop2_disable_irqrestore(flags);
}
diff --git a/arch/mips/netlogic/xlr/setup.c b/arch/mips/netlogic/xlr/setup.c
index 89c8c10..214d123 100644
--- a/arch/mips/netlogic/xlr/setup.c
+++ b/arch/mips/netlogic/xlr/setup.c
@@ -196,6 +196,7 @@
{
int *argv, *envp; /* passed as 32 bit ptrs */
struct psb_info *prom_infop;
+ void *reset_vec;
#ifdef CONFIG_SMP
int i;
#endif
@@ -208,6 +209,12 @@
nlm_prom_info = *prom_infop;
nlm_init_node();
+ /* Update reset entry point with CPU init code */
+ reset_vec = (void *)CKSEG1ADDR(RESET_VEC_PHYS);
+ memset(reset_vec, 0, RESET_VEC_SIZE);
+ memcpy(reset_vec, (void *)nlm_reset_entry,
+ (nlm_reset_entry_end - nlm_reset_entry));
+
nlm_early_serial_setup();
build_arcs_cmdline(argv);
prom_add_memory();
diff --git a/arch/mips/netlogic/xlr/wakeup.c b/arch/mips/netlogic/xlr/wakeup.c
index 3ebf741..c06e4c9 100644
--- a/arch/mips/netlogic/xlr/wakeup.c
+++ b/arch/mips/netlogic/xlr/wakeup.c
@@ -53,6 +53,7 @@
{
struct nlm_soc_info *nodep;
unsigned int i, j, boot_cpu;
+ volatile u32 *cpu_ready = nlm_get_boot_data(BOOT_CPU_READY);
/*
* In case of RMI boot, hit with NMI to get the cores
@@ -71,7 +72,7 @@
nodep->coremask = 1;
for (i = 1; i < NLM_CORES_PER_NODE; i++) {
for (j = 1000000; j > 0; j--) {
- if (nlm_cpu_ready[i * NLM_THREADS_PER_CORE])
+ if (cpu_ready[i * NLM_THREADS_PER_CORE])
break;
udelay(10);
}
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile
index 2cb1d31..c382042 100644
--- a/arch/mips/pci/Makefile
+++ b/arch/mips/pci/Makefile
@@ -29,7 +29,7 @@
obj-$(CONFIG_MIPS_COBALT) += fixup-cobalt.o
obj-$(CONFIG_LEMOTE_FULOONG2E) += fixup-fuloong2e.o ops-loongson2.o
obj-$(CONFIG_LEMOTE_MACH2F) += fixup-lemote2f.o ops-loongson2.o
-obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o
+obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o pci-malta.o
obj-$(CONFIG_PMC_MSP7120_GW) += fixup-pmcmsp.o ops-pmcmsp.o
obj-$(CONFIG_PMC_MSP7120_EVAL) += fixup-pmcmsp.o ops-pmcmsp.o
obj-$(CONFIG_PMC_MSP7120_FPGA) += fixup-pmcmsp.o ops-pmcmsp.o
@@ -52,12 +52,11 @@
obj-$(CONFIG_TOSHIBA_RBTX4938) += fixup-rbtx4938.o
obj-$(CONFIG_VICTOR_MPC30X) += fixup-mpc30x.o
obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capcella.o
-obj-$(CONFIG_WR_PPMC) += fixup-wrppmc.o
obj-$(CONFIG_MIKROTIK_RB532) += pci-rc32434.o ops-rc32434.o fixup-rc32434.o
-obj-$(CONFIG_CPU_CAVIUM_OCTEON) += pci-octeon.o pcie-octeon.o
+obj-$(CONFIG_CAVIUM_OCTEON_SOC) += pci-octeon.o pcie-octeon.o
obj-$(CONFIG_CPU_XLR) += pci-xlr.o
obj-$(CONFIG_CPU_XLP) += pci-xlp.o
ifdef CONFIG_PCI_MSI
-obj-$(CONFIG_CPU_CAVIUM_OCTEON) += msi-octeon.o
+obj-$(CONFIG_CAVIUM_OCTEON_SOC) += msi-octeon.o
endif
diff --git a/arch/mips/pci/fixup-wrppmc.c b/arch/mips/pci/fixup-wrppmc.c
deleted file mode 100644
index 29737ed..0000000
--- a/arch/mips/pci/fixup-wrppmc.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * fixup-wrppmc.c: PPMC board specific PCI fixup
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2006, Wind River Inc. Rongkai.zhan (rongkai.zhan@windriver.com)
- */
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <asm/gt64120.h>
-
-/* PCI interrupt pins */
-#define PCI_INTA 1
-#define PCI_INTB 2
-#define PCI_INTC 3
-#define PCI_INTD 4
-
-#define PCI_SLOT_MAXNR 32 /* Each PCI bus has 32 physical slots */
-
-static char pci_irq_tab[PCI_SLOT_MAXNR][5] __initdata = {
- /* 0 INTA INTB INTC INTD */
- [0] = {0, 0, 0, 0, 0}, /* Slot 0: GT64120 PCI bridge */
- [6] = {0, WRPPMC_PCI_INTA_IRQ, 0, 0, 0},
-};
-
-int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
-{
- return pci_irq_tab[slot][pin];
-}
-
-/* Do platform specific device initialization at pci_enable_device() time */
-int pcibios_plat_dev_init(struct pci_dev *dev)
-{
- return 0;
-}
diff --git a/arch/mips/pci/pci-bcm63xx.c b/arch/mips/pci/pci-bcm63xx.c
index 2eb9542..151d9b5 100644
--- a/arch/mips/pci/pci-bcm63xx.c
+++ b/arch/mips/pci/pci-bcm63xx.c
@@ -266,7 +266,7 @@
/* setup PCI to local bus access, used by PCI device to target
* local RAM while bus mastering */
bcm63xx_int_cfg_writel(0, PCI_BASE_ADDRESS_3);
- if (BCMCPU_IS_6358() || BCMCPU_IS_6368())
+ if (BCMCPU_IS_3368() || BCMCPU_IS_6358() || BCMCPU_IS_6368())
val = MPI_SP0_REMAP_ENABLE_MASK;
else
val = 0;
@@ -338,6 +338,7 @@
case BCM6328_CPU_ID:
case BCM6362_CPU_ID:
return bcm63xx_register_pcie();
+ case BCM3368_CPU_ID:
case BCM6348_CPU_ID:
case BCM6358_CPU_ID:
case BCM6368_CPU_ID:
diff --git a/arch/mips/pci/pci-ip27.c b/arch/mips/pci/pci-ip27.c
index 6eb65e4..7b2ac81 100644
--- a/arch/mips/pci/pci-ip27.c
+++ b/arch/mips/pci/pci-ip27.c
@@ -217,6 +217,7 @@
pci_disable_swapping(d);
}
+#ifdef CONFIG_NUMA
int pcibus_to_node(struct pci_bus *bus)
{
struct bridge_controller *bc = BRIDGE_CONTROLLER(bus);
@@ -224,6 +225,7 @@
return bc->nasid;
}
EXPORT_SYMBOL(pcibus_to_node);
+#endif /* CONFIG_NUMA */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
pci_fixup_ioc3);
diff --git a/arch/mips/mti-malta/malta-pci.c b/arch/mips/pci/pci-malta.c
similarity index 100%
rename from arch/mips/mti-malta/malta-pci.c
rename to arch/mips/pci/pci-malta.c
diff --git a/arch/mips/pmcs-msp71xx/Makefile b/arch/mips/pmcs-msp71xx/Makefile
index cefba77..9201c8b 100644
--- a/arch/mips/pmcs-msp71xx/Makefile
+++ b/arch/mips/pmcs-msp71xx/Makefile
@@ -3,7 +3,6 @@
#
obj-y += msp_prom.o msp_setup.o msp_irq.o \
msp_time.o msp_serial.o msp_elb.o
-obj-$(CONFIG_HAVE_GPIO_LIB) += gpio.o gpio_extended.o
obj-$(CONFIG_PMC_MSP7120_GW) += msp_hwbutton.o
obj-$(CONFIG_IRQ_MSP_SLP) += msp_irq_slp.o
obj-$(CONFIG_IRQ_MSP_CIC) += msp_irq_cic.o msp_irq_per.o
diff --git a/arch/mips/pmcs-msp71xx/gpio.c b/arch/mips/pmcs-msp71xx/gpio.c
deleted file mode 100644
index aaccbe5..0000000
--- a/arch/mips/pmcs-msp71xx/gpio.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Generic PMC MSP71xx GPIO handling. These base gpio are controlled by two
- * types of registers. The data register sets the output level when in output
- * mode and when in input mode will contain the value at the input. The config
- * register sets the various modes for each gpio.
- *
- * 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.
- *
- * @author Patrick Glass <patrickglass@gmail.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/gpio.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-
-#define MSP71XX_CFG_OFFSET(gpio) (4 * (gpio))
-#define CONF_MASK 0x0F
-#define MSP71XX_GPIO_INPUT 0x01
-#define MSP71XX_GPIO_OUTPUT 0x08
-
-#define MSP71XX_GPIO_BASE 0x0B8400000L
-
-#define to_msp71xx_gpio_chip(c) container_of(c, struct msp71xx_gpio_chip, chip)
-
-static spinlock_t gpio_lock;
-
-/*
- * struct msp71xx_gpio_chip - container for gpio chip and registers
- * @chip: chip structure for the specified gpio bank
- * @data_reg: register for reading and writing the gpio pin value
- * @config_reg: register to set the mode for the gpio pin bank
- * @out_drive_reg: register to set the output drive mode for the gpio pin bank
- */
-struct msp71xx_gpio_chip {
- struct gpio_chip chip;
- void __iomem *data_reg;
- void __iomem *config_reg;
- void __iomem *out_drive_reg;
-};
-
-/*
- * msp71xx_gpio_get() - return the chip's gpio value
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose value will be returned
- *
- * It will return 0 if gpio value is low and other if high.
- */
-static int msp71xx_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct msp71xx_gpio_chip *msp_chip = to_msp71xx_gpio_chip(chip);
-
- return __raw_readl(msp_chip->data_reg) & (1 << offset);
-}
-
-/*
- * msp71xx_gpio_set() - set the output value for the gpio
- * @chip: chip structure who controls the specified gpio
- * @offset: gpio whose value will be assigned
- * @value: logic level to assign to the gpio initially
- *
- * This will set the gpio bit specified to the desired value. It will set the
- * gpio pin low if value is 0 otherwise it will be high.
- */
-static void msp71xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct msp71xx_gpio_chip *msp_chip = to_msp71xx_gpio_chip(chip);
- unsigned long flags;
- u32 data;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- data = __raw_readl(msp_chip->data_reg);
- if (value)
- data |= (1 << offset);
- else
- data &= ~(1 << offset);
- __raw_writel(data, msp_chip->data_reg);
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-}
-
-/*
- * msp71xx_set_gpio_mode() - declare the mode for a gpio
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose value will be assigned
- * @mode: desired configuration for the gpio (see datasheet)
- *
- * It will set the gpio pin config to the @mode value passed in.
- */
-static int msp71xx_set_gpio_mode(struct gpio_chip *chip,
- unsigned offset, int mode)
-{
- struct msp71xx_gpio_chip *msp_chip = to_msp71xx_gpio_chip(chip);
- const unsigned bit_offset = MSP71XX_CFG_OFFSET(offset);
- unsigned long flags;
- u32 cfg;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- cfg = __raw_readl(msp_chip->config_reg);
- cfg &= ~(CONF_MASK << bit_offset);
- cfg |= (mode << bit_offset);
- __raw_writel(cfg, msp_chip->config_reg);
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- return 0;
-}
-
-/*
- * msp71xx_direction_output() - declare the direction mode for a gpio
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose value will be assigned
- * @value: logic level to assign to the gpio initially
- *
- * This call will set the mode for the @gpio to output. It will set the
- * gpio pin low if value is 0 otherwise it will be high.
- */
-static int msp71xx_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- msp71xx_gpio_set(chip, offset, value);
-
- return msp71xx_set_gpio_mode(chip, offset, MSP71XX_GPIO_OUTPUT);
-}
-
-/*
- * msp71xx_direction_input() - declare the direction mode for a gpio
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose to which the value will be assigned
- *
- * This call will set the mode for the @gpio to input.
- */
-static int msp71xx_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- return msp71xx_set_gpio_mode(chip, offset, MSP71XX_GPIO_INPUT);
-}
-
-/*
- * msp71xx_set_output_drive() - declare the output drive for the gpio line
- * @gpio: gpio pin whose output drive you wish to modify
- * @value: zero for active drain 1 for open drain drive
- *
- * This call will set the output drive mode for the @gpio to output.
- */
-int msp71xx_set_output_drive(unsigned gpio, int value)
-{
- unsigned long flags;
- u32 data;
-
- if (gpio > 15 || gpio < 0)
- return -EINVAL;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- data = __raw_readl((void __iomem *)(MSP71XX_GPIO_BASE + 0x190));
- if (value)
- data |= (1 << gpio);
- else
- data &= ~(1 << gpio);
- __raw_writel(data, (void __iomem *)(MSP71XX_GPIO_BASE + 0x190));
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- return 0;
-}
-EXPORT_SYMBOL(msp71xx_set_output_drive);
-
-#define MSP71XX_GPIO_BANK(name, dr, cr, base_gpio, num_gpio) \
-{ \
- .chip = { \
- .label = name, \
- .direction_input = msp71xx_direction_input, \
- .direction_output = msp71xx_direction_output, \
- .get = msp71xx_gpio_get, \
- .set = msp71xx_gpio_set, \
- .base = base_gpio, \
- .ngpio = num_gpio \
- }, \
- .data_reg = (void __iomem *)(MSP71XX_GPIO_BASE + dr), \
- .config_reg = (void __iomem *)(MSP71XX_GPIO_BASE + cr), \
- .out_drive_reg = (void __iomem *)(MSP71XX_GPIO_BASE + 0x190), \
-}
-
-/*
- * struct msp71xx_gpio_banks[] - container array of gpio banks
- * @chip: chip structure for the specified gpio bank
- * @data_reg: register for reading and writing the gpio pin value
- * @config_reg: register to set the mode for the gpio pin bank
- *
- * This array structure defines the gpio banks for the PMC MIPS Processor.
- * We specify the bank name, the data register, the config register, base
- * starting gpio number, and the number of gpios exposed by the bank.
- */
-static struct msp71xx_gpio_chip msp71xx_gpio_banks[] = {
-
- MSP71XX_GPIO_BANK("GPIO_1_0", 0x170, 0x180, 0, 2),
- MSP71XX_GPIO_BANK("GPIO_5_2", 0x174, 0x184, 2, 4),
- MSP71XX_GPIO_BANK("GPIO_9_6", 0x178, 0x188, 6, 4),
- MSP71XX_GPIO_BANK("GPIO_15_10", 0x17C, 0x18C, 10, 6),
-};
-
-void __init msp71xx_init_gpio(void)
-{
- int i;
-
- spin_lock_init(&gpio_lock);
-
- for (i = 0; i < ARRAY_SIZE(msp71xx_gpio_banks); i++)
- gpiochip_add(&msp71xx_gpio_banks[i].chip);
-}
diff --git a/arch/mips/pmcs-msp71xx/gpio_extended.c b/arch/mips/pmcs-msp71xx/gpio_extended.c
deleted file mode 100644
index 2a99f36..0000000
--- a/arch/mips/pmcs-msp71xx/gpio_extended.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Generic PMC MSP71xx EXTENDED (EXD) GPIO handling. The extended gpio is
- * a set of hardware registers that have no need for explicit locking as
- * it is handled by unique method of writing individual set/clr bits.
- *
- * 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.
- *
- * @author Patrick Glass <patrickglass@gmail.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/gpio.h>
-#include <linux/io.h>
-
-#define MSP71XX_DATA_OFFSET(gpio) (2 * (gpio))
-#define MSP71XX_READ_OFFSET(gpio) (MSP71XX_DATA_OFFSET(gpio) + 1)
-#define MSP71XX_CFG_OUT_OFFSET(gpio) (MSP71XX_DATA_OFFSET(gpio) + 16)
-#define MSP71XX_CFG_IN_OFFSET(gpio) (MSP71XX_CFG_OUT_OFFSET(gpio) + 1)
-
-#define MSP71XX_EXD_GPIO_BASE 0x0BC000000L
-
-#define to_msp71xx_exd_gpio_chip(c) \
- container_of(c, struct msp71xx_exd_gpio_chip, chip)
-
-/*
- * struct msp71xx_exd_gpio_chip - container for gpio chip and registers
- * @chip: chip structure for the specified gpio bank
- * @reg: register for control and data of gpio pin
- */
-struct msp71xx_exd_gpio_chip {
- struct gpio_chip chip;
- void __iomem *reg;
-};
-
-/*
- * msp71xx_exd_gpio_get() - return the chip's gpio value
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose value will be returned
- *
- * It will return 0 if gpio value is low and other if high.
- */
-static int msp71xx_exd_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct msp71xx_exd_gpio_chip *msp71xx_chip =
- to_msp71xx_exd_gpio_chip(chip);
- const unsigned bit = MSP71XX_READ_OFFSET(offset);
-
- return __raw_readl(msp71xx_chip->reg) & (1 << bit);
-}
-
-/*
- * msp71xx_exd_gpio_set() - set the output value for the gpio
- * @chip: chip structure who controls the specified gpio
- * @offset: gpio whose value will be assigned
- * @value: logic level to assign to the gpio initially
- *
- * This will set the gpio bit specified to the desired value. It will set the
- * gpio pin low if value is 0 otherwise it will be high.
- */
-static void msp71xx_exd_gpio_set(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct msp71xx_exd_gpio_chip *msp71xx_chip =
- to_msp71xx_exd_gpio_chip(chip);
- const unsigned bit = MSP71XX_DATA_OFFSET(offset);
-
- __raw_writel(1 << (bit + (value ? 1 : 0)), msp71xx_chip->reg);
-}
-
-/*
- * msp71xx_exd_direction_output() - declare the direction mode for a gpio
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose value will be assigned
- * @value: logic level to assign to the gpio initially
- *
- * This call will set the mode for the @gpio to output. It will set the
- * gpio pin low if value is 0 otherwise it will be high.
- */
-static int msp71xx_exd_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct msp71xx_exd_gpio_chip *msp71xx_chip =
- to_msp71xx_exd_gpio_chip(chip);
-
- msp71xx_exd_gpio_set(chip, offset, value);
- __raw_writel(1 << MSP71XX_CFG_OUT_OFFSET(offset), msp71xx_chip->reg);
- return 0;
-}
-
-/*
- * msp71xx_exd_direction_input() - declare the direction mode for a gpio
- * @chip: chip structure which controls the specified gpio
- * @offset: gpio whose to which the value will be assigned
- *
- * This call will set the mode for the @gpio to input.
- */
-static int msp71xx_exd_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct msp71xx_exd_gpio_chip *msp71xx_chip =
- to_msp71xx_exd_gpio_chip(chip);
-
- __raw_writel(1 << MSP71XX_CFG_IN_OFFSET(offset), msp71xx_chip->reg);
- return 0;
-}
-
-#define MSP71XX_EXD_GPIO_BANK(name, exd_reg, base_gpio, num_gpio) \
-{ \
- .chip = { \
- .label = name, \
- .direction_input = msp71xx_exd_direction_input, \
- .direction_output = msp71xx_exd_direction_output, \
- .get = msp71xx_exd_gpio_get, \
- .set = msp71xx_exd_gpio_set, \
- .base = base_gpio, \
- .ngpio = num_gpio, \
- }, \
- .reg = (void __iomem *)(MSP71XX_EXD_GPIO_BASE + exd_reg), \
-}
-
-/*
- * struct msp71xx_exd_gpio_banks[] - container array of gpio banks
- * @chip: chip structure for the specified gpio bank
- * @reg: register for reading and writing the gpio pin value
- *
- * This array structure defines the extended gpio banks for the
- * PMC MIPS Processor. We specify the bank name, the data/config
- * register,the base starting gpio number, and the number of
- * gpios exposed by the bank of gpios.
- */
-static struct msp71xx_exd_gpio_chip msp71xx_exd_gpio_banks[] = {
-
- MSP71XX_EXD_GPIO_BANK("GPIO_23_16", 0x188, 16, 8),
- MSP71XX_EXD_GPIO_BANK("GPIO_27_24", 0x18C, 24, 4),
-};
-
-void __init msp71xx_init_gpio_extended(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(msp71xx_exd_gpio_banks); i++)
- gpiochip_add(&msp71xx_exd_gpio_banks[i].chip);
-}
diff --git a/arch/mips/powertv/asic/asic_devices.c b/arch/mips/powertv/asic/asic_devices.c
index d38b095..9f64c23 100644
--- a/arch/mips/powertv/asic/asic_devices.c
+++ b/arch/mips/powertv/asic/asic_devices.c
@@ -529,17 +529,8 @@
*/
void platform_release_memory(void *ptr, int size)
{
- unsigned long addr;
- unsigned long end;
-
- addr = ((unsigned long)ptr + (PAGE_SIZE - 1)) & PAGE_MASK;
- end = ((unsigned long)ptr + size) & PAGE_MASK;
-
- for (; addr < end; addr += PAGE_SIZE) {
- ClearPageReserved(virt_to_page(__va(addr)));
- init_page_count(virt_to_page(__va(addr)));
- free_page((unsigned long)__va(addr));
- }
+ free_reserved_area((unsigned long)ptr, (unsigned long)(ptr + size),
+ -1, NULL);
}
EXPORT_SYMBOL(platform_release_memory);
diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c
index 6b5f340..f25ea5b 100644
--- a/arch/mips/ralink/of.c
+++ b/arch/mips/ralink/of.c
@@ -104,7 +104,7 @@
if (!of_have_populated_dt())
panic("device tree not present");
- strncpy(of_ids[0].compatible, soc_info.compatible, len);
+ strlcpy(of_ids[0].compatible, soc_info.compatible, len);
strncpy(of_ids[1].compatible, "palmbus", len);
if (of_platform_populate(NULL, of_ids, NULL, NULL))
diff --git a/arch/mips/sgi-ip27/Makefile b/arch/mips/sgi-ip27/Makefile
index 1f29e76..da8f681 100644
--- a/arch/mips/sgi-ip27/Makefile
+++ b/arch/mips/sgi-ip27/Makefile
@@ -7,4 +7,5 @@
ip27-xtalk.o
obj-$(CONFIG_EARLY_PRINTK) += ip27-console.o
+obj-$(CONFIG_PCI) += ip27-irq-pci.o
obj-$(CONFIG_SMP) += ip27-smp.o
diff --git a/arch/mips/sgi-ip27/ip27-irq-pci.c b/arch/mips/sgi-ip27/ip27-irq-pci.c
new file mode 100644
index 0000000..ec22ec5
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-irq-pci.c
@@ -0,0 +1,266 @@
+/*
+ * ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
+ *
+ * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 1999 - 2001 Kanoj Sarcar
+ */
+
+#undef DEBUG
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/smp.h>
+#include <linux/random.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+
+#include <asm/processor.h>
+#include <asm/pci/bridge.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/hub.h>
+#include <asm/sn/intr.h>
+
+/*
+ * Linux has a controller-independent x86 interrupt architecture.
+ * every controller has a 'controller-template', that is used
+ * by the main code to do the right thing. Each driver-visible
+ * interrupt source is transparently wired to the appropriate
+ * controller. Thus drivers need not be aware of the
+ * interrupt-controller.
+ *
+ * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
+ * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
+ * (IO-APICs assumed to be messaging to Pentium local-APICs)
+ *
+ * the code is designed to be easily extended with new/different
+ * interrupt controllers, without having to do assembly magic.
+ */
+
+extern struct bridge_controller *irq_to_bridge[];
+extern int irq_to_slot[];
+
+/*
+ * use these macros to get the encoded nasid and widget id
+ * from the irq value
+ */
+#define IRQ_TO_BRIDGE(i) irq_to_bridge[(i)]
+#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
+
+static inline int alloc_level(int cpu, int irq)
+{
+ struct hub_data *hub = hub_data(cpu_to_node(cpu));
+ struct slice_data *si = cpu_data[cpu].data;
+ int level;
+
+ level = find_first_zero_bit(hub->irq_alloc_mask, LEVELS_PER_SLICE);
+ if (level >= LEVELS_PER_SLICE)
+ panic("Cpu %d flooded with devices", cpu);
+
+ __set_bit(level, hub->irq_alloc_mask);
+ si->level_to_irq[level] = irq;
+
+ return level;
+}
+
+static inline int find_level(cpuid_t *cpunum, int irq)
+{
+ int cpu, i;
+
+ for_each_online_cpu(cpu) {
+ struct slice_data *si = cpu_data[cpu].data;
+
+ for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++)
+ if (si->level_to_irq[i] == irq) {
+ *cpunum = cpu;
+
+ return i;
+ }
+ }
+
+ panic("Could not identify cpu/level for irq %d", irq);
+}
+
+static int intr_connect_level(int cpu, int bit)
+{
+ nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
+ struct slice_data *si = cpu_data[cpu].data;
+
+ set_bit(bit, si->irq_enable_mask);
+
+ if (!cputoslice(cpu)) {
+ REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
+ REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
+ } else {
+ REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
+ REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
+ }
+
+ return 0;
+}
+
+static int intr_disconnect_level(int cpu, int bit)
+{
+ nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
+ struct slice_data *si = cpu_data[cpu].data;
+
+ clear_bit(bit, si->irq_enable_mask);
+
+ if (!cputoslice(cpu)) {
+ REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
+ REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
+ } else {
+ REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
+ REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
+ }
+
+ return 0;
+}
+
+/* Startup one of the (PCI ...) IRQs routes over a bridge. */
+static unsigned int startup_bridge_irq(struct irq_data *d)
+{
+ struct bridge_controller *bc;
+ bridgereg_t device;
+ bridge_t *bridge;
+ int pin, swlevel;
+ cpuid_t cpu;
+
+ pin = SLOT_FROM_PCI_IRQ(d->irq);
+ bc = IRQ_TO_BRIDGE(d->irq);
+ bridge = bc->base;
+
+ pr_debug("bridge_startup(): irq= 0x%x pin=%d\n", d->irq, pin);
+ /*
+ * "map" irq to a swlevel greater than 6 since the first 6 bits
+ * of INT_PEND0 are taken
+ */
+ swlevel = find_level(&cpu, d->irq);
+ bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (bc->nasid << 8));
+ bridge->b_int_enable |= (1 << pin);
+ bridge->b_int_enable |= 0x7ffffe00; /* more stuff in int_enable */
+
+ /*
+ * Enable sending of an interrupt clear packt to the hub on a high to
+ * low transition of the interrupt pin.
+ *
+ * IRIX sets additional bits in the address which are documented as
+ * reserved in the bridge docs.
+ */
+ bridge->b_int_mode |= (1UL << pin);
+
+ /*
+ * We assume the bridge to have a 1:1 mapping between devices
+ * (slots) and intr pins.
+ */
+ device = bridge->b_int_device;
+ device &= ~(7 << (pin*3));
+ device |= (pin << (pin*3));
+ bridge->b_int_device = device;
+
+ bridge->b_wid_tflush;
+
+ intr_connect_level(cpu, swlevel);
+
+ return 0; /* Never anything pending. */
+}
+
+/* Shutdown one of the (PCI ...) IRQs routes over a bridge. */
+static void shutdown_bridge_irq(struct irq_data *d)
+{
+ struct bridge_controller *bc = IRQ_TO_BRIDGE(d->irq);
+ bridge_t *bridge = bc->base;
+ int pin, swlevel;
+ cpuid_t cpu;
+
+ pr_debug("bridge_shutdown: irq 0x%x\n", d->irq);
+ pin = SLOT_FROM_PCI_IRQ(d->irq);
+
+ /*
+ * map irq to a swlevel greater than 6 since the first 6 bits
+ * of INT_PEND0 are taken
+ */
+ swlevel = find_level(&cpu, d->irq);
+ intr_disconnect_level(cpu, swlevel);
+
+ bridge->b_int_enable &= ~(1 << pin);
+ bridge->b_wid_tflush;
+}
+
+static inline void enable_bridge_irq(struct irq_data *d)
+{
+ cpuid_t cpu;
+ int swlevel;
+
+ swlevel = find_level(&cpu, d->irq); /* Criminal offence */
+ intr_connect_level(cpu, swlevel);
+}
+
+static inline void disable_bridge_irq(struct irq_data *d)
+{
+ cpuid_t cpu;
+ int swlevel;
+
+ swlevel = find_level(&cpu, d->irq); /* Criminal offence */
+ intr_disconnect_level(cpu, swlevel);
+}
+
+static struct irq_chip bridge_irq_type = {
+ .name = "bridge",
+ .irq_startup = startup_bridge_irq,
+ .irq_shutdown = shutdown_bridge_irq,
+ .irq_mask = disable_bridge_irq,
+ .irq_unmask = enable_bridge_irq,
+};
+
+void register_bridge_irq(unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, &bridge_irq_type, handle_level_irq);
+}
+
+int request_bridge_irq(struct bridge_controller *bc)
+{
+ int irq = allocate_irqno();
+ int swlevel, cpu;
+ nasid_t nasid;
+
+ if (irq < 0)
+ return irq;
+
+ /*
+ * "map" irq to a swlevel greater than 6 since the first 6 bits
+ * of INT_PEND0 are taken
+ */
+ cpu = bc->irq_cpu;
+ swlevel = alloc_level(cpu, irq);
+ if (unlikely(swlevel < 0)) {
+ free_irqno(irq);
+
+ return -EAGAIN;
+ }
+
+ /* Make sure it's not already pending when we connect it. */
+ nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
+ REMOTE_HUB_CLR_INTR(nasid, swlevel);
+
+ intr_connect_level(cpu, swlevel);
+
+ register_bridge_irq(irq);
+
+ return irq;
+}
diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c
index 2315cfe..3fbaef9 100644
--- a/arch/mips/sgi-ip27/ip27-irq.c
+++ b/arch/mips/sgi-ip27/ip27-irq.c
@@ -29,7 +29,6 @@
#include <asm/mipsregs.h>
#include <asm/processor.h>
-#include <asm/pci/bridge.h>
#include <asm/sn/addrs.h>
#include <asm/sn/agent.h>
#include <asm/sn/arch.h>
@@ -54,50 +53,6 @@
extern asmlinkage void ip27_irq(void);
-extern struct bridge_controller *irq_to_bridge[];
-extern int irq_to_slot[];
-
-/*
- * use these macros to get the encoded nasid and widget id
- * from the irq value
- */
-#define IRQ_TO_BRIDGE(i) irq_to_bridge[(i)]
-#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
-
-static inline int alloc_level(int cpu, int irq)
-{
- struct hub_data *hub = hub_data(cpu_to_node(cpu));
- struct slice_data *si = cpu_data[cpu].data;
- int level;
-
- level = find_first_zero_bit(hub->irq_alloc_mask, LEVELS_PER_SLICE);
- if (level >= LEVELS_PER_SLICE)
- panic("Cpu %d flooded with devices", cpu);
-
- __set_bit(level, hub->irq_alloc_mask);
- si->level_to_irq[level] = irq;
-
- return level;
-}
-
-static inline int find_level(cpuid_t *cpunum, int irq)
-{
- int cpu, i;
-
- for_each_online_cpu(cpu) {
- struct slice_data *si = cpu_data[cpu].data;
-
- for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++)
- if (si->level_to_irq[i] == irq) {
- *cpunum = cpu;
-
- return i;
- }
- }
-
- panic("Could not identify cpu/level for irq %d", irq);
-}
-
/*
* Find first bit set
*/
@@ -204,175 +159,6 @@
panic("CPU %d got a hub error interrupt", smp_processor_id());
}
-static int intr_connect_level(int cpu, int bit)
-{
- nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
- struct slice_data *si = cpu_data[cpu].data;
-
- set_bit(bit, si->irq_enable_mask);
-
- if (!cputoslice(cpu)) {
- REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
- REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
- } else {
- REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
- REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
- }
-
- return 0;
-}
-
-static int intr_disconnect_level(int cpu, int bit)
-{
- nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
- struct slice_data *si = cpu_data[cpu].data;
-
- clear_bit(bit, si->irq_enable_mask);
-
- if (!cputoslice(cpu)) {
- REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]);
- REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]);
- } else {
- REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]);
- REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]);
- }
-
- return 0;
-}
-
-/* Startup one of the (PCI ...) IRQs routes over a bridge. */
-static unsigned int startup_bridge_irq(struct irq_data *d)
-{
- struct bridge_controller *bc;
- bridgereg_t device;
- bridge_t *bridge;
- int pin, swlevel;
- cpuid_t cpu;
-
- pin = SLOT_FROM_PCI_IRQ(d->irq);
- bc = IRQ_TO_BRIDGE(d->irq);
- bridge = bc->base;
-
- pr_debug("bridge_startup(): irq= 0x%x pin=%d\n", d->irq, pin);
- /*
- * "map" irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- swlevel = find_level(&cpu, d->irq);
- bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (bc->nasid << 8));
- bridge->b_int_enable |= (1 << pin);
- bridge->b_int_enable |= 0x7ffffe00; /* more stuff in int_enable */
-
- /*
- * Enable sending of an interrupt clear packt to the hub on a high to
- * low transition of the interrupt pin.
- *
- * IRIX sets additional bits in the address which are documented as
- * reserved in the bridge docs.
- */
- bridge->b_int_mode |= (1UL << pin);
-
- /*
- * We assume the bridge to have a 1:1 mapping between devices
- * (slots) and intr pins.
- */
- device = bridge->b_int_device;
- device &= ~(7 << (pin*3));
- device |= (pin << (pin*3));
- bridge->b_int_device = device;
-
- bridge->b_wid_tflush;
-
- intr_connect_level(cpu, swlevel);
-
- return 0; /* Never anything pending. */
-}
-
-/* Shutdown one of the (PCI ...) IRQs routes over a bridge. */
-static void shutdown_bridge_irq(struct irq_data *d)
-{
- struct bridge_controller *bc = IRQ_TO_BRIDGE(d->irq);
- bridge_t *bridge = bc->base;
- int pin, swlevel;
- cpuid_t cpu;
-
- pr_debug("bridge_shutdown: irq 0x%x\n", d->irq);
- pin = SLOT_FROM_PCI_IRQ(d->irq);
-
- /*
- * map irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- swlevel = find_level(&cpu, d->irq);
- intr_disconnect_level(cpu, swlevel);
-
- bridge->b_int_enable &= ~(1 << pin);
- bridge->b_wid_tflush;
-}
-
-static inline void enable_bridge_irq(struct irq_data *d)
-{
- cpuid_t cpu;
- int swlevel;
-
- swlevel = find_level(&cpu, d->irq); /* Criminal offence */
- intr_connect_level(cpu, swlevel);
-}
-
-static inline void disable_bridge_irq(struct irq_data *d)
-{
- cpuid_t cpu;
- int swlevel;
-
- swlevel = find_level(&cpu, d->irq); /* Criminal offence */
- intr_disconnect_level(cpu, swlevel);
-}
-
-static struct irq_chip bridge_irq_type = {
- .name = "bridge",
- .irq_startup = startup_bridge_irq,
- .irq_shutdown = shutdown_bridge_irq,
- .irq_mask = disable_bridge_irq,
- .irq_unmask = enable_bridge_irq,
-};
-
-void register_bridge_irq(unsigned int irq)
-{
- irq_set_chip_and_handler(irq, &bridge_irq_type, handle_level_irq);
-}
-
-int request_bridge_irq(struct bridge_controller *bc)
-{
- int irq = allocate_irqno();
- int swlevel, cpu;
- nasid_t nasid;
-
- if (irq < 0)
- return irq;
-
- /*
- * "map" irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- cpu = bc->irq_cpu;
- swlevel = alloc_level(cpu, irq);
- if (unlikely(swlevel < 0)) {
- free_irqno(irq);
-
- return -EAGAIN;
- }
-
- /* Make sure it's not already pending when we connect it. */
- nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu));
- REMOTE_HUB_CLR_INTR(nasid, swlevel);
-
- intr_connect_level(cpu, swlevel);
-
- register_bridge_irq(irq);
-
- return irq;
-}
-
asmlinkage void plat_irq_dispatch(void)
{
unsigned long pending = read_c0_cause() & read_c0_status();
diff --git a/arch/mips/sibyte/Kconfig b/arch/mips/sibyte/Kconfig
index 01cc1a7..5fbd360 100644
--- a/arch/mips/sibyte/Kconfig
+++ b/arch/mips/sibyte/Kconfig
@@ -147,7 +147,8 @@
config SIBYTE_BUS_WATCHER
bool "Support for Bus Watcher statistics"
- depends on SIBYTE_SB1xxx_SOC
+ depends on SIBYTE_SB1xxx_SOC && \
+ (SIBYTE_BCM112X || SIBYTE_SB1250)
help
Handle and keep statistics on the bus error interrupts (COR_ECC,
BAD_ECC, IO_BUS).
diff --git a/arch/mips/sibyte/Platform b/arch/mips/sibyte/Platform
index d03a075..af11733 100644
--- a/arch/mips/sibyte/Platform
+++ b/arch/mips/sibyte/Platform
@@ -13,7 +13,6 @@
-I$(srctree)/arch/mips/include/asm/mach-sibyte \
-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
-platform-$(CONFIG_SIBYTE_SB1250) += sibyte/
cflags-$(CONFIG_SIBYTE_SB1250) += \
-I$(srctree)/arch/mips/include/asm/mach-sibyte \
-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
@@ -31,7 +30,8 @@
# Sibyte BCM91120C (CRhine) board
# Sibyte BCM91125C (CRhone) board
# Sibyte BCM91125E (Rhone) board
-# Sibyte SWARM board
+# Sibyte BCM91250A (SWARM) board
+# Sibyte BCM91250C2 (LittleSur) board
# Sibyte BCM91x80 (BigSur) board
#
load-$(CONFIG_SIBYTE_CARMEL) := 0xffffffff80100000
@@ -41,3 +41,4 @@
load-$(CONFIG_SIBYTE_SENTOSA) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_SWARM) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_BIGSUR) := 0xffffffff80100000
+load-$(CONFIG_SIBYTE_LITTLESUR) := 0xffffffff80100000
diff --git a/arch/mips/sibyte/common/Makefile b/arch/mips/sibyte/common/Makefile
index 36aa700..b3d6bf2 100644
--- a/arch/mips/sibyte/common/Makefile
+++ b/arch/mips/sibyte/common/Makefile
@@ -1,3 +1,4 @@
obj-y := cfe.o
+obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o
obj-$(CONFIG_SIBYTE_CFE_CONSOLE) += cfe_console.o
obj-$(CONFIG_SIBYTE_TBPROF) += sb_tbprof.o
diff --git a/arch/mips/sibyte/sb1250/bus_watcher.c b/arch/mips/sibyte/common/bus_watcher.c
similarity index 93%
rename from arch/mips/sibyte/sb1250/bus_watcher.c
rename to arch/mips/sibyte/common/bus_watcher.c
index 8871e33..5581844 100644
--- a/arch/mips/sibyte/sb1250/bus_watcher.c
+++ b/arch/mips/sibyte/common/bus_watcher.c
@@ -37,6 +37,9 @@
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_int.h>
#include <asm/sibyte/sb1250_scd.h>
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+#include <asm/sibyte/bcm1480_regs.h>
+#endif
struct bw_stats_struct {
@@ -81,9 +84,15 @@
#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
/* Destructive read, clears register and interrupt */
status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
-#else
+#elif defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
/* Use non-destructive register */
status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
+#elif defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+ /* Use non-destructive register */
+ /* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */
+ status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG));
+#else
+#error bus watcher being built for unknown Sibyte SOC!
#endif
if (!(status & 0x7fffffff)) {
printk("Using last values reaped by bus watcher driver\n");
@@ -175,9 +184,6 @@
#ifdef CONFIG_SIBYTE_BW_TRACE
int i;
#endif
-#ifndef CONFIG_PROC_FS
- char bw_buf[1024];
-#endif
#ifdef CONFIG_SIBYTE_BW_TRACE
csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c
index 2188b39a..059e28c 100644
--- a/arch/mips/sibyte/common/sb_tbprof.c
+++ b/arch/mips/sibyte/common/sb_tbprof.c
@@ -27,6 +27,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/errno.h>
diff --git a/arch/mips/sibyte/sb1250/Makefile b/arch/mips/sibyte/sb1250/Makefile
index d3d969d..cdc4c56 100644
--- a/arch/mips/sibyte/sb1250/Makefile
+++ b/arch/mips/sibyte/sb1250/Makefile
@@ -1,4 +1,3 @@
obj-y := setup.o irq.o time.o
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o
diff --git a/arch/mips/sni/pcimt.c b/arch/mips/sni/pcimt.c
index cec4b8c..12336c2 100644
--- a/arch/mips/sni/pcimt.c
+++ b/arch/mips/sni/pcimt.c
@@ -185,6 +185,7 @@
extern struct pci_ops sni_pcimt_ops;
+#ifdef CONFIG_PCI
static struct pci_controller sni_controller = {
.pci_ops = &sni_pcimt_ops,
.mem_resource = &sni_mem_resource,
@@ -193,6 +194,7 @@
.io_offset = 0x00000000UL,
.io_map_base = SNI_PORT_BASE
};
+#endif
static void enable_pcimt_irq(struct irq_data *d)
{
diff --git a/arch/mips/sni/pcit.c b/arch/mips/sni/pcit.c
index 7cddd03..05bb516 100644
--- a/arch/mips/sni/pcit.c
+++ b/arch/mips/sni/pcit.c
@@ -128,13 +128,6 @@
}
};
-static struct resource sni_mem_resource = {
- .start = 0x18000000UL,
- .end = 0x1fbfffffUL,
- .name = "PCIT PCI MEM",
- .flags = IORESOURCE_MEM
-};
-
static void __init sni_pcit_resource_init(void)
{
int i;
@@ -147,6 +140,14 @@
extern struct pci_ops sni_pcit_ops;
+#ifdef CONFIG_PCI
+static struct resource sni_mem_resource = {
+ .start = 0x18000000UL,
+ .end = 0x1fbfffffUL,
+ .name = "PCIT PCI MEM",
+ .flags = IORESOURCE_MEM
+};
+
static struct pci_controller sni_pcit_controller = {
.pci_ops = &sni_pcit_ops,
.mem_resource = &sni_mem_resource,
@@ -155,6 +156,7 @@
.io_offset = 0x00000000UL,
.io_map_base = SNI_PORT_BASE
};
+#endif /* CONFIG_PCI */
static void enable_pcit_irq(struct irq_data *d)
{
diff --git a/arch/mips/wrppmc/Makefile b/arch/mips/wrppmc/Makefile
deleted file mode 100644
index 307cc69..0000000
--- a/arch/mips/wrppmc/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-#
-# This file is subject to the terms and conditions of the GNU General Public
-# License. See the file "COPYING" in the main directory of this archive
-# for more details.
-#
-# Copyright 2006 Wind River System, Inc.
-# Author: Rongkai.Zhan <rongkai.zhan@windriver.com>
-#
-# Makefile for the Wind River MIPS 4Kc PPMC Eval Board
-#
-
-obj-y += irq.o pci.o reset.o serial.o setup.o time.o
diff --git a/arch/mips/wrppmc/Platform b/arch/mips/wrppmc/Platform
deleted file mode 100644
index dc78b25..0000000
--- a/arch/mips/wrppmc/Platform
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Wind River PPMC Board (4KC + GT64120)
-#
-platform-$(CONFIG_WR_PPMC) += wrppmc/
-cflags-$(CONFIG_WR_PPMC) += \
- -I$(srctree)/arch/mips/include/asm/mach-wrppmc
-load-$(CONFIG_WR_PPMC) += 0xffffffff80100000
diff --git a/arch/mips/wrppmc/irq.c b/arch/mips/wrppmc/irq.c
deleted file mode 100644
index f237bf4..0000000
--- a/arch/mips/wrppmc/irq.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * irq.c: GT64120 Interrupt Controller
- *
- * Copyright (C) 2006, Wind River System Inc.
- * Author: Rongkai.Zhan, <rongkai.zhan@windriver.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/hardirq.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-
-#include <asm/gt64120.h>
-#include <asm/irq_cpu.h>
-#include <asm/mipsregs.h>
-
-asmlinkage void plat_irq_dispatch(void)
-{
- unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
-
- if (pending & STATUSF_IP7)
- do_IRQ(WRPPMC_MIPS_TIMER_IRQ); /* CPU Compare/Count internal timer */
- else if (pending & STATUSF_IP6)
- do_IRQ(WRPPMC_UART16550_IRQ); /* UART 16550 port */
- else if (pending & STATUSF_IP3)
- do_IRQ(WRPPMC_PCI_INTA_IRQ); /* PCI INT_A */
- else
- spurious_interrupt();
-}
-
-/**
- * Initialize GT64120 Interrupt Controller
- */
-void gt64120_init_pic(void)
-{
- /* clear CPU Interrupt Cause Registers */
- GT_WRITE(GT_INTRCAUSE_OFS, (0x1F << 21));
- GT_WRITE(GT_HINTRCAUSE_OFS, 0x00);
-
- /* Disable all interrupts from GT64120 bridge chip */
- GT_WRITE(GT_INTRMASK_OFS, 0x00);
- GT_WRITE(GT_HINTRMASK_OFS, 0x00);
- GT_WRITE(GT_PCI0_ICMASK_OFS, 0x00);
- GT_WRITE(GT_PCI0_HICMASK_OFS, 0x00);
-}
-
-void __init arch_init_irq(void)
-{
- /* IRQ 0 - 7 are for MIPS common irq_cpu controller */
- mips_cpu_irq_init();
-
- gt64120_init_pic();
-}
diff --git a/arch/mips/wrppmc/pci.c b/arch/mips/wrppmc/pci.c
deleted file mode 100644
index 8b8a0e1..0000000
--- a/arch/mips/wrppmc/pci.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * pci.c: GT64120 PCI support.
- *
- * Copyright (C) 2006, Wind River System Inc. Rongkai.Zhan <rongkai.zhan@windriver.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-
-#include <asm/gt64120.h>
-
-extern struct pci_ops gt64xxx_pci0_ops;
-
-static struct resource pci0_io_resource = {
- .name = "pci_0 io",
- .start = GT_PCI_IO_BASE,
- .end = GT_PCI_IO_BASE + GT_PCI_IO_SIZE - 1,
- .flags = IORESOURCE_IO,
-};
-
-static struct resource pci0_mem_resource = {
- .name = "pci_0 memory",
- .start = GT_PCI_MEM_BASE,
- .end = GT_PCI_MEM_BASE + GT_PCI_MEM_SIZE - 1,
- .flags = IORESOURCE_MEM,
-};
-
-static struct pci_controller hose_0 = {
- .pci_ops = >64xxx_pci0_ops,
- .io_resource = &pci0_io_resource,
- .mem_resource = &pci0_mem_resource,
-};
-
-static int __init gt64120_pci_init(void)
-{
- (void) GT_READ(GT_PCI0_CMD_OFS); /* Huh??? -- Ralf */
- (void) GT_READ(GT_PCI0_BARE_OFS);
-
- /* reset the whole PCI I/O space range */
- ioport_resource.start = GT_PCI_IO_BASE;
- ioport_resource.end = GT_PCI_IO_BASE + GT_PCI_IO_SIZE - 1;
-
- register_pci_controller(&hose_0);
- return 0;
-}
-
-arch_initcall(gt64120_pci_init);
diff --git a/arch/mips/wrppmc/reset.c b/arch/mips/wrppmc/reset.c
deleted file mode 100644
index 80beb18..0000000
--- a/arch/mips/wrppmc/reset.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1997 Ralf Baechle
- */
-#include <linux/irqflags.h>
-#include <linux/kernel.h>
-
-#include <asm/cacheflush.h>
-#include <asm/idle.h>
-#include <asm/mipsregs.h>
-#include <asm/processor.h>
-
-void wrppmc_machine_restart(char *command)
-{
- /*
- * Ouch, we're still alive ... This time we take the silver bullet ...
- * ... and find that we leave the hardware in a state in which the
- * kernel in the flush locks up somewhen during of after the PCI
- * detection stuff.
- */
- local_irq_disable();
- set_c0_status(ST0_BEV | ST0_ERL);
- change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED);
- flush_cache_all();
- write_c0_wired(0);
- __asm__ __volatile__("jr\t%0"::"r"(0xbfc00000));
-}
-
-void wrppmc_machine_halt(void)
-{
- local_irq_disable();
-
- printk(KERN_NOTICE "You can safely turn off the power\n");
- while (1) {
- if (cpu_wait)
- cpu_wait();
- }
-}
diff --git a/arch/mips/wrppmc/serial.c b/arch/mips/wrppmc/serial.c
deleted file mode 100644
index 83f0f7d..0000000
--- a/arch/mips/wrppmc/serial.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Registration of WRPPMC UART platform device.
- *
- * Copyright (C) 2007 Yoichi Yuasa <yuasa@linux-mips.org>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-#include <linux/serial_8250.h>
-
-#include <asm/gt64120.h>
-
-static struct resource wrppmc_uart_resource[] __initdata = {
- {
- .start = WRPPMC_UART16550_BASE,
- .end = WRPPMC_UART16550_BASE + 7,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = WRPPMC_UART16550_IRQ,
- .end = WRPPMC_UART16550_IRQ,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct plat_serial8250_port wrppmc_serial8250_port[] = {
- {
- .irq = WRPPMC_UART16550_IRQ,
- .uartclk = WRPPMC_UART16550_CLOCK,
- .iotype = UPIO_MEM,
- .flags = UPF_IOREMAP | UPF_SKIP_TEST,
- .mapbase = WRPPMC_UART16550_BASE,
- },
- {},
-};
-
-static __init int wrppmc_uart_add(void)
-{
- struct platform_device *pdev;
- int retval;
-
- pdev = platform_device_alloc("serial8250", -1);
- if (!pdev)
- return -ENOMEM;
-
- pdev->id = PLAT8250_DEV_PLATFORM;
- pdev->dev.platform_data = wrppmc_serial8250_port;
-
- retval = platform_device_add_resources(pdev, wrppmc_uart_resource,
- ARRAY_SIZE(wrppmc_uart_resource));
- if (retval)
- goto err_free_device;
-
- retval = platform_device_add(pdev);
- if (retval)
- goto err_free_device;
-
- return 0;
-
-err_free_device:
- platform_device_put(pdev);
-
- return retval;
-}
-device_initcall(wrppmc_uart_add);
diff --git a/arch/mips/wrppmc/setup.c b/arch/mips/wrppmc/setup.c
deleted file mode 100644
index ca65c84..0000000
--- a/arch/mips/wrppmc/setup.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * setup.c: Setup pointers to hardware dependent routines.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1996, 1997, 2004 by Ralf Baechle (ralf@linux-mips.org)
- * Copyright (C) 2006, Wind River System Inc. Rongkai.zhan <rongkai.zhan@windriver.com>
- */
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/pm.h>
-
-#include <asm/io.h>
-#include <asm/bootinfo.h>
-#include <asm/reboot.h>
-#include <asm/time.h>
-#include <asm/gt64120.h>
-
-unsigned long gt64120_base = KSEG1ADDR(0x14000000);
-
-#ifdef WRPPMC_EARLY_DEBUG
-
-static volatile unsigned char * wrppmc_led = \
- (volatile unsigned char *)KSEG1ADDR(WRPPMC_LED_BASE);
-
-/*
- * PPMC LED control register:
- * -) bit[0] controls DS1 LED (1 - OFF, 0 - ON)
- * -) bit[1] controls DS2 LED (1 - OFF, 0 - ON)
- * -) bit[2] controls DS4 LED (1 - OFF, 0 - ON)
- */
-void wrppmc_led_on(int mask)
-{
- unsigned char value = *wrppmc_led;
-
- value &= (0xF8 | mask);
- *wrppmc_led = value;
-}
-
-/* If mask = 0, turn off all LEDs */
-void wrppmc_led_off(int mask)
-{
- unsigned char value = *wrppmc_led;
-
- value |= (0x7 & mask);
- *wrppmc_led = value;
-}
-
-/*
- * We assume that bootloader has initialized UART16550 correctly
- */
-void __init wrppmc_early_putc(char ch)
-{
- static volatile unsigned char *wrppmc_uart = \
- (volatile unsigned char *)KSEG1ADDR(WRPPMC_UART16550_BASE);
- unsigned char value;
-
- /* Wait until Transmit-Holding-Register is empty */
- while (1) {
- value = *(wrppmc_uart + 5);
- if (value & 0x20)
- break;
- }
-
- *wrppmc_uart = ch;
-}
-
-void __init wrppmc_early_printk(const char *fmt, ...)
-{
- static char pbuf[256] = {'\0', };
- char *ch = pbuf;
- va_list args;
- unsigned int i;
-
- memset(pbuf, 0, 256);
- va_start(args, fmt);
- i = vsprintf(pbuf, fmt, args);
- va_end(args);
-
- /* Print the string */
- while (*ch != '\0') {
- wrppmc_early_putc(*ch);
- /* if print '\n', also print '\r' */
- if (*ch++ == '\n')
- wrppmc_early_putc('\r');
- }
-}
-#endif /* WRPPMC_EARLY_DEBUG */
-
-void __init prom_free_prom_memory(void)
-{
-}
-
-void __init plat_mem_setup(void)
-{
- extern void wrppmc_machine_restart(char *command);
- extern void wrppmc_machine_halt(void);
-
- _machine_restart = wrppmc_machine_restart;
- _machine_halt = wrppmc_machine_halt;
- pm_power_off = wrppmc_machine_halt;
-
- /* This makes the operations of 'in/out[bwl]' to the
- * physical address ( < KSEG0) can work via KSEG1
- */
- set_io_port_base(KSEG1);
-}
-
-const char *get_system_type(void)
-{
- return "Wind River PPMC (GT64120)";
-}
-
-/*
- * Initializes basic routines and structures pointers, memory size (as
- * given by the bios and saves the command line.
- */
-void __init prom_init(void)
-{
- add_memory_region(WRPPMC_SDRAM_SCS0_BASE, WRPPMC_SDRAM_SCS0_SIZE, BOOT_MEM_RAM);
- add_memory_region(WRPPMC_BOOTROM_BASE, WRPPMC_BOOTROM_SIZE, BOOT_MEM_ROM_DATA);
-
- wrppmc_early_printk("prom_init: GT64120 SDRAM Bank 0: 0x%x - 0x%08lx\n",
- WRPPMC_SDRAM_SCS0_BASE, (WRPPMC_SDRAM_SCS0_BASE + WRPPMC_SDRAM_SCS0_SIZE));
-}
diff --git a/arch/mips/wrppmc/time.c b/arch/mips/wrppmc/time.c
deleted file mode 100644
index 668dbd5..0000000
--- a/arch/mips/wrppmc/time.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * time.c: MIPS CPU Count/Compare timer hookup
- *
- * Author: Mark.Zhan, <rongkai.zhan@windriver.com>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1996, 1997, 2004 by Ralf Baechle (ralf@linux-mips.org)
- * Copyright (C) 2006, Wind River System Inc.
- */
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-
-#include <asm/gt64120.h>
-#include <asm/time.h>
-
-#define WRPPMC_CPU_CLK_FREQ 40000000 /* 40MHZ */
-
-/*
- * Estimate CPU frequency. Sets mips_hpt_frequency as a side-effect
- *
- * NOTE: We disable all GT64120 timers, and use MIPS processor internal
- * timer as the source of kernel clock tick.
- */
-void __init plat_time_init(void)
-{
- /* Disable GT64120 timers */
- GT_WRITE(GT_TC_CONTROL_OFS, 0x00);
- GT_WRITE(GT_TC0_OFS, 0x00);
- GT_WRITE(GT_TC1_OFS, 0x00);
- GT_WRITE(GT_TC2_OFS, 0x00);
- GT_WRITE(GT_TC3_OFS, 0x00);
-
- /* Use MIPS compare/count internal timer */
- mips_hpt_frequency = WRPPMC_CPU_CLK_FREQ;
-}
diff --git a/arch/powerpc/mm/mmap.c b/arch/powerpc/mm/mmap.c
index 67a42ed..cb8bdbe 100644
--- a/arch/powerpc/mm/mmap.c
+++ b/arch/powerpc/mm/mmap.c
@@ -92,10 +92,8 @@
if (mmap_is_legacy()) {
mm->mmap_base = TASK_UNMAPPED_BASE;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base();
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c
index 13c3f0e..d1821b8 100644
--- a/arch/powerpc/perf/power7-pmu.c
+++ b/arch/powerpc/perf/power7-pmu.c
@@ -60,7 +60,7 @@
#define PME_PM_LD_REF_L1 0xc880
#define PME_PM_LD_MISS_L1 0x400f0
#define PME_PM_BRU_FIN 0x10068
-#define PME_PM_BRU_MPRED 0x400f6
+#define PME_PM_BR_MPRED 0x400f6
#define PME_PM_CMPLU_STALL_FXU 0x20014
#define PME_PM_CMPLU_STALL_DIV 0x40014
@@ -349,7 +349,7 @@
[PERF_COUNT_HW_CACHE_REFERENCES] = PME_PM_LD_REF_L1,
[PERF_COUNT_HW_CACHE_MISSES] = PME_PM_LD_MISS_L1,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = PME_PM_BRU_FIN,
- [PERF_COUNT_HW_BRANCH_MISSES] = PME_PM_BRU_MPRED,
+ [PERF_COUNT_HW_BRANCH_MISSES] = PME_PM_BR_MPRED,
};
#define C(x) PERF_COUNT_HW_CACHE_##x
@@ -405,7 +405,7 @@
GENERIC_EVENT_ATTR(cache-references, LD_REF_L1);
GENERIC_EVENT_ATTR(cache-misses, LD_MISS_L1);
GENERIC_EVENT_ATTR(branch-instructions, BRU_FIN);
-GENERIC_EVENT_ATTR(branch-misses, BRU_MPRED);
+GENERIC_EVENT_ATTR(branch-misses, BR_MPRED);
POWER_EVENT_ATTR(CYC, CYC);
POWER_EVENT_ATTR(GCT_NOSLOT_CYC, GCT_NOSLOT_CYC);
@@ -414,7 +414,7 @@
POWER_EVENT_ATTR(LD_REF_L1, LD_REF_L1);
POWER_EVENT_ATTR(LD_MISS_L1, LD_MISS_L1);
POWER_EVENT_ATTR(BRU_FIN, BRU_FIN)
-POWER_EVENT_ATTR(BRU_MPRED, BRU_MPRED);
+POWER_EVENT_ATTR(BR_MPRED, BR_MPRED);
POWER_EVENT_ATTR(CMPLU_STALL_FXU, CMPLU_STALL_FXU);
POWER_EVENT_ATTR(CMPLU_STALL_DIV, CMPLU_STALL_DIV);
@@ -449,7 +449,7 @@
GENERIC_EVENT_PTR(LD_REF_L1),
GENERIC_EVENT_PTR(LD_MISS_L1),
GENERIC_EVENT_PTR(BRU_FIN),
- GENERIC_EVENT_PTR(BRU_MPRED),
+ GENERIC_EVENT_PTR(BR_MPRED),
POWER_EVENT_PTR(CYC),
POWER_EVENT_PTR(GCT_NOSLOT_CYC),
@@ -458,7 +458,7 @@
POWER_EVENT_PTR(LD_REF_L1),
POWER_EVENT_PTR(LD_MISS_L1),
POWER_EVENT_PTR(BRU_FIN),
- POWER_EVENT_PTR(BRU_MPRED),
+ POWER_EVENT_PTR(BR_MPRED),
POWER_EVENT_PTR(CMPLU_STALL_FXU),
POWER_EVENT_PTR(CMPLU_STALL_DIV),
diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c
index 06bafec..4002329 100644
--- a/arch/s390/mm/mmap.c
+++ b/arch/s390/mm/mmap.c
@@ -91,11 +91,9 @@
if (mmap_is_legacy()) {
mm->mmap_base = TASK_UNMAPPED_BASE;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base();
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
@@ -176,11 +174,9 @@
if (mmap_is_legacy()) {
mm->mmap_base = TASK_UNMAPPED_BASE;
mm->get_unmapped_area = s390_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base();
mm->get_unmapped_area = s390_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/sparc/include/asm/leon.h b/arch/sparc/include/asm/leon.h
index b836e92..c2f6ff6 100644
--- a/arch/sparc/include/asm/leon.h
+++ b/arch/sparc/include/asm/leon.h
@@ -108,7 +108,7 @@
{
u32 cctrl;
__asm__ __volatile__("lda [%%g0] 2, %0\n\t" : "=r"(cctrl));
- return (cctrl >> 23) & 1;
+ return ((cctrl >> 23) & 1) && ((cctrl >> 17) & 1);
};
static inline void sparc_leon3_disable_cache(void)
diff --git a/arch/sparc/kernel/asm-offsets.c b/arch/sparc/kernel/asm-offsets.c
index 961b87f..f76389a 100644
--- a/arch/sparc/kernel/asm-offsets.c
+++ b/arch/sparc/kernel/asm-offsets.c
@@ -49,6 +49,8 @@
DEFINE(AOFF_task_thread, offsetof(struct task_struct, thread));
BLANK();
DEFINE(AOFF_mm_context, offsetof(struct mm_struct, context));
+ BLANK();
+ DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
/* DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); */
return 0;
diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c
index 5ef48da..11d460f 100644
--- a/arch/sparc/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
@@ -783,6 +783,16 @@
char *base, *p;
int msg_len, loops;
+ if (strlen(var) + strlen(value) + 2 >
+ sizeof(pkt) - sizeof(pkt.header)) {
+ printk(KERN_ERR PFX
+ "contents length: %zu, which more than max: %lu,"
+ "so could not set (%s) variable to (%s).\n",
+ strlen(var) + strlen(value) + 2,
+ sizeof(pkt) - sizeof(pkt.header), var, value);
+ return;
+ }
+
memset(&pkt, 0, sizeof(pkt));
pkt.header.data.tag.type = DS_DATA;
pkt.header.data.handle = cp->handle;
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index 2daaaa6..51561b8 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -290,7 +290,6 @@
sysctl_legacy_va_layout) {
mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
/* We know it's 32-bit */
unsigned long task_size = STACK_TOP32;
@@ -302,7 +301,6 @@
mm->mmap_base = PAGE_ALIGN(task_size - gap - random_factor);
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/sparc/mm/hypersparc.S b/arch/sparc/mm/hypersparc.S
index 44aad32..969f964 100644
--- a/arch/sparc/mm/hypersparc.S
+++ b/arch/sparc/mm/hypersparc.S
@@ -74,7 +74,7 @@
/* The things we do for performance... */
hypersparc_flush_cache_range:
- ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
#ifndef CONFIG_SMP
ld [%o0 + AOFF_mm_context], %g1
cmp %g1, -1
@@ -163,7 +163,7 @@
*/
/* Verified, my ass... */
hypersparc_flush_cache_page:
- ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
ld [%o0 + AOFF_mm_context], %g2
#ifndef CONFIG_SMP
cmp %g2, -1
@@ -284,7 +284,7 @@
sta %g5, [%g1] ASI_M_MMUREGS
hypersparc_flush_tlb_range:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
mov SRMMU_CTX_REG, %g1
ld [%o0 + AOFF_mm_context], %o3
lda [%g1] ASI_M_MMUREGS, %g5
@@ -307,7 +307,7 @@
sta %g5, [%g1] ASI_M_MMUREGS
hypersparc_flush_tlb_page:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
mov SRMMU_CTX_REG, %g1
ld [%o0 + AOFF_mm_context], %o3
andn %o1, (PAGE_SIZE - 1), %o1
diff --git a/arch/sparc/mm/swift.S b/arch/sparc/mm/swift.S
index c801c39..5d2b88d 100644
--- a/arch/sparc/mm/swift.S
+++ b/arch/sparc/mm/swift.S
@@ -105,7 +105,7 @@
.globl swift_flush_cache_range
swift_flush_cache_range:
- ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
sub %o2, %o1, %o2
sethi %hi(4096), %o3
cmp %o2, %o3
@@ -116,7 +116,7 @@
.globl swift_flush_cache_page
swift_flush_cache_page:
- ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
70:
ld [%o0 + AOFF_mm_context], %g2
cmp %g2, -1
@@ -219,7 +219,7 @@
.globl swift_flush_tlb_range
.globl swift_flush_tlb_all
swift_flush_tlb_range:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
swift_flush_tlb_mm:
ld [%o0 + AOFF_mm_context], %g2
cmp %g2, -1
@@ -233,7 +233,7 @@
.globl swift_flush_tlb_page
swift_flush_tlb_page:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
mov SRMMU_CTX_REG, %g1
ld [%o0 + AOFF_mm_context], %o3
andn %o1, (PAGE_SIZE - 1), %o1
diff --git a/arch/sparc/mm/tsunami.S b/arch/sparc/mm/tsunami.S
index 4e55e8f..bf10a34 100644
--- a/arch/sparc/mm/tsunami.S
+++ b/arch/sparc/mm/tsunami.S
@@ -24,7 +24,7 @@
/* Sliiick... */
tsunami_flush_cache_page:
tsunami_flush_cache_range:
- ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
tsunami_flush_cache_mm:
ld [%o0 + AOFF_mm_context], %g2
cmp %g2, -1
@@ -46,7 +46,7 @@
/* More slick stuff... */
tsunami_flush_tlb_range:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
tsunami_flush_tlb_mm:
ld [%o0 + AOFF_mm_context], %g2
cmp %g2, -1
@@ -65,7 +65,7 @@
/* This one can be done in a fine grained manner... */
tsunami_flush_tlb_page:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
mov SRMMU_CTX_REG, %g1
ld [%o0 + AOFF_mm_context], %o3
andn %o1, (PAGE_SIZE - 1), %o1
diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S
index bf8ee06..852257f 100644
--- a/arch/sparc/mm/viking.S
+++ b/arch/sparc/mm/viking.S
@@ -108,7 +108,7 @@
viking_flush_cache_page:
viking_flush_cache_range:
#ifndef CONFIG_SMP
- ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
#endif
viking_flush_cache_mm:
#ifndef CONFIG_SMP
@@ -148,7 +148,7 @@
#endif
viking_flush_tlb_range:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
mov SRMMU_CTX_REG, %g1
ld [%o0 + AOFF_mm_context], %o3
lda [%g1] ASI_M_MMUREGS, %g5
@@ -173,7 +173,7 @@
#endif
viking_flush_tlb_page:
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
mov SRMMU_CTX_REG, %g1
ld [%o0 + AOFF_mm_context], %o3
lda [%g1] ASI_M_MMUREGS, %g5
@@ -239,7 +239,7 @@
tst %g5
bne 3f
mov SRMMU_CTX_REG, %g1
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
ld [%o0 + AOFF_mm_context], %o3
lda [%g1] ASI_M_MMUREGS, %g5
sethi %hi(~((1 << SRMMU_PGDIR_SHIFT) - 1)), %o4
@@ -265,7 +265,7 @@
tst %g5
bne 2f
mov SRMMU_CTX_REG, %g1
- ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */
+ ld [%o0 + VMA_VM_MM], %o0
ld [%o0 + AOFF_mm_context], %o3
lda [%g1] ASI_M_MMUREGS, %g5
and %o1, PAGE_MASK, %o1
diff --git a/arch/tile/mm/mmap.c b/arch/tile/mm/mmap.c
index f96f4ce..d67d91e 100644
--- a/arch/tile/mm/mmap.c
+++ b/arch/tile/mm/mmap.c
@@ -66,10 +66,8 @@
if (!is_32bit || rlimit(RLIMIT_STACK) == RLIM_INFINITY) {
mm->mmap_base = TASK_UNMAPPED_BASE;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base(mm);
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c
index 52ff81c..bae3aba 100644
--- a/arch/x86/ia32/ia32_aout.c
+++ b/arch/x86/ia32/ia32_aout.c
@@ -308,8 +308,6 @@
(current->mm->start_data = N_DATADDR(ex));
current->mm->brk = ex.a_bss +
(current->mm->start_brk = N_BSSADDR(ex));
- current->mm->free_area_cache = TASK_UNMAPPED_BASE;
- current->mm->cached_hole_size = 0;
retval = setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT);
if (retval < 0) {
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 6b52980..29e3093 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -214,6 +214,13 @@
/* Interrupt Handler for core thermal thresholds */
extern int (*platform_thermal_notify)(__u64 msr_val);
+/* Interrupt Handler for package thermal thresholds */
+extern int (*platform_thermal_package_notify)(__u64 msr_val);
+
+/* Callback support of rate control, return true, if
+ * callback has rate control */
+extern bool (*platform_thermal_package_rate_control)(void);
+
#ifdef CONFIG_X86_THERMAL_VECTOR
extern void mcheck_intel_therm_init(void);
#else
diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c
index 98f2083..41e8e00 100644
--- a/arch/x86/kernel/cpu/mcheck/therm_throt.c
+++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c
@@ -55,12 +55,24 @@
struct _thermal_state package_power_limit;
struct _thermal_state core_thresh0;
struct _thermal_state core_thresh1;
+ struct _thermal_state pkg_thresh0;
+ struct _thermal_state pkg_thresh1;
};
/* Callback to handle core threshold interrupts */
int (*platform_thermal_notify)(__u64 msr_val);
EXPORT_SYMBOL(platform_thermal_notify);
+/* Callback to handle core package threshold_interrupts */
+int (*platform_thermal_package_notify)(__u64 msr_val);
+EXPORT_SYMBOL_GPL(platform_thermal_package_notify);
+
+/* Callback support of rate control, return true, if
+ * callback has rate control */
+bool (*platform_thermal_package_rate_control)(void);
+EXPORT_SYMBOL_GPL(platform_thermal_package_rate_control);
+
+
static DEFINE_PER_CPU(struct thermal_state, thermal_state);
static atomic_t therm_throt_en = ATOMIC_INIT(0);
@@ -195,19 +207,25 @@
return 0;
}
-static int thresh_event_valid(int event)
+static int thresh_event_valid(int level, int event)
{
struct _thermal_state *state;
unsigned int this_cpu = smp_processor_id();
struct thermal_state *pstate = &per_cpu(thermal_state, this_cpu);
u64 now = get_jiffies_64();
- state = (event == 0) ? &pstate->core_thresh0 : &pstate->core_thresh1;
+ if (level == PACKAGE_LEVEL)
+ state = (event == 0) ? &pstate->pkg_thresh0 :
+ &pstate->pkg_thresh1;
+ else
+ state = (event == 0) ? &pstate->core_thresh0 :
+ &pstate->core_thresh1;
if (time_before64(now, state->next_check))
return 0;
state->next_check = now + CHECK_INTERVAL;
+
return 1;
}
@@ -322,6 +340,39 @@
#endif /* CONFIG_SYSFS */
+static void notify_package_thresholds(__u64 msr_val)
+{
+ bool notify_thres_0 = false;
+ bool notify_thres_1 = false;
+
+ if (!platform_thermal_package_notify)
+ return;
+
+ /* lower threshold check */
+ if (msr_val & THERM_LOG_THRESHOLD0)
+ notify_thres_0 = true;
+ /* higher threshold check */
+ if (msr_val & THERM_LOG_THRESHOLD1)
+ notify_thres_1 = true;
+
+ if (!notify_thres_0 && !notify_thres_1)
+ return;
+
+ if (platform_thermal_package_rate_control &&
+ platform_thermal_package_rate_control()) {
+ /* Rate control is implemented in callback */
+ platform_thermal_package_notify(msr_val);
+ return;
+ }
+
+ /* lower threshold reached */
+ if (notify_thres_0 && thresh_event_valid(PACKAGE_LEVEL, 0))
+ platform_thermal_package_notify(msr_val);
+ /* higher threshold reached */
+ if (notify_thres_1 && thresh_event_valid(PACKAGE_LEVEL, 1))
+ platform_thermal_package_notify(msr_val);
+}
+
static void notify_thresholds(__u64 msr_val)
{
/* check whether the interrupt handler is defined;
@@ -331,10 +382,12 @@
return;
/* lower threshold reached */
- if ((msr_val & THERM_LOG_THRESHOLD0) && thresh_event_valid(0))
+ if ((msr_val & THERM_LOG_THRESHOLD0) &&
+ thresh_event_valid(CORE_LEVEL, 0))
platform_thermal_notify(msr_val);
/* higher threshold reached */
- if ((msr_val & THERM_LOG_THRESHOLD1) && thresh_event_valid(1))
+ if ((msr_val & THERM_LOG_THRESHOLD1) &&
+ thresh_event_valid(CORE_LEVEL, 1))
platform_thermal_notify(msr_val);
}
@@ -360,6 +413,8 @@
if (this_cpu_has(X86_FEATURE_PTS)) {
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
+ /* check violations of package thermal thresholds */
+ notify_package_thresholds(msr_val);
therm_throt_process(msr_val & PACKAGE_THERM_STATUS_PROCHOT,
THERMAL_THROTTLING_EVENT,
PACKAGE_LEVEL);
diff --git a/arch/x86/kernel/cpu/perf_event_amd_iommu.c b/arch/x86/kernel/cpu/perf_event_amd_iommu.c
index 0db655e..639d128 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_iommu.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_iommu.c
@@ -491,10 +491,8 @@
static __init int amd_iommu_pc_init(void)
{
/* Make sure the IOMMU PC resource is available */
- if (!amd_iommu_pc_supported()) {
- pr_err("perf: amd_iommu PMU not installed. No support!\n");
+ if (!amd_iommu_pc_supported())
return -ENODEV;
- }
_init_perf_amd_iommu(&__perf_iommu, "amd_iommu");
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 0920212..ba77ebc 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -111,7 +111,7 @@
*/
list_for_each_entry_rcu(a, &desc->head, list) {
u64 before, delta, whole_msecs;
- int decimal_msecs, thishandled;
+ int remainder_ns, decimal_msecs, thishandled;
before = local_clock();
thishandled = a->handler(type, regs);
@@ -123,8 +123,9 @@
continue;
nmi_longest_ns = delta;
- whole_msecs = do_div(delta, (1000 * 1000));
- decimal_msecs = do_div(delta, 1000) % 1000;
+ whole_msecs = delta;
+ remainder_ns = do_div(whole_msecs, (1000 * 1000));
+ decimal_msecs = remainder_ns / 1000;
printk_ratelimited(KERN_INFO
"INFO: NMI handler (%ps) took too long to run: "
"%lld.%03d msecs\n", a->handler, whole_msecs,
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index a7e1855..064d0be 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -3404,15 +3404,22 @@
var->limit = vmx_read_guest_seg_limit(vmx, seg);
var->selector = vmx_read_guest_seg_selector(vmx, seg);
ar = vmx_read_guest_seg_ar(vmx, seg);
+ var->unusable = (ar >> 16) & 1;
var->type = ar & 15;
var->s = (ar >> 4) & 1;
var->dpl = (ar >> 5) & 3;
- var->present = (ar >> 7) & 1;
+ /*
+ * Some userspaces do not preserve unusable property. Since usable
+ * segment has to be present according to VMX spec we can use present
+ * property to amend userspace bug by making unusable segment always
+ * nonpresent. vmx_segment_access_rights() already marks nonpresent
+ * segment as unusable.
+ */
+ var->present = !var->unusable;
var->avl = (ar >> 12) & 1;
var->l = (ar >> 13) & 1;
var->db = (ar >> 14) & 1;
var->g = (ar >> 15) & 1;
- var->unusable = (ar >> 16) & 1;
}
static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg)
diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 845df68..62c29a5 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -115,10 +115,8 @@
if (mmap_is_legacy()) {
mm->mmap_base = mmap_legacy_base();
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
} else {
mm->mmap_base = mmap_base();
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
- mm->unmap_area = arch_unmap_area_topdown;
}
}
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 0a1b95f..7ea6451 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -6,10 +6,12 @@
select ARCH_WANT_FRAME_POINTERS
select HAVE_IDE
select GENERIC_ATOMIC64
+ select GENERIC_CLOCKEVENTS
select HAVE_GENERIC_HARDIRQS
select VIRT_TO_BUS
select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES
+ select GENERIC_SCHED_CLOCK
select MODULES_USE_ELF_RELA
select GENERIC_PCI_IOMAP
select ARCH_WANT_IPC_PARSE_VERSION
@@ -17,6 +19,7 @@
select CLONE_BACKWARDS
select IRQ_DOMAIN
select HAVE_OPROFILE
+ select HAVE_FUNCTION_TRACER
help
Xtensa processors are 32-bit RISC machines designed by Tensilica
primarily for embedded systems. These processors are both
diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug
index a34010e..af7da74 100644
--- a/arch/xtensa/Kconfig.debug
+++ b/arch/xtensa/Kconfig.debug
@@ -2,6 +2,16 @@
source "lib/Kconfig.debug"
+config DEBUG_TLB_SANITY
+ bool "Debug TLB sanity"
+ depends on DEBUG_KERNEL
+ help
+ Enable this to turn on TLB sanity check on each entry to userspace.
+ This check can spot missing TLB invalidation/wrong PTE permissions/
+ premature page freeing.
+
+ If unsure, say N.
+
config LD_NO_RELAX
bool "Disable linker relaxation"
default n
diff --git a/arch/xtensa/boot/.gitignore b/arch/xtensa/boot/.gitignore
new file mode 100644
index 0000000..be76559
--- /dev/null
+++ b/arch/xtensa/boot/.gitignore
@@ -0,0 +1,3 @@
+uImage
+zImage.redboot
+*.dtb
diff --git a/arch/xtensa/boot/boot-elf/.gitignore b/arch/xtensa/boot/boot-elf/.gitignore
new file mode 100644
index 0000000..5ff8fbb
--- /dev/null
+++ b/arch/xtensa/boot/boot-elf/.gitignore
@@ -0,0 +1 @@
+boot.lds
diff --git a/arch/xtensa/boot/lib/.gitignore b/arch/xtensa/boot/lib/.gitignore
new file mode 100644
index 0000000..1629a61
--- /dev/null
+++ b/arch/xtensa/boot/lib/.gitignore
@@ -0,0 +1,3 @@
+inffast.c
+inflate.c
+inftrees.c
diff --git a/arch/xtensa/boot/lib/Makefile b/arch/xtensa/boot/lib/Makefile
index ad8952e..6868f2c 100644
--- a/arch/xtensa/boot/lib/Makefile
+++ b/arch/xtensa/boot/lib/Makefile
@@ -7,6 +7,13 @@
lib-y += $(zlib:.c=.o) zmem.o
ccflags-y := -Ilib/zlib_inflate
+ifdef CONFIG_FUNCTION_TRACER
+CFLAGS_REMOVE_inflate.o = -pg
+CFLAGS_REMOVE_zmem.o = -pg
+CFLAGS_REMOVE_inftrees.o = -pg
+CFLAGS_REMOVE_inffast.o = -pg
+endif
+
quiet_cmd_copy_zlib = COPY $@
cmd_copy_zlib = cat $< > $@
diff --git a/arch/xtensa/include/asm/bootparam.h b/arch/xtensa/include/asm/bootparam.h
index 0c25799..23392c5 100644
--- a/arch/xtensa/include/asm/bootparam.h
+++ b/arch/xtensa/include/asm/bootparam.h
@@ -20,7 +20,7 @@
#define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/
#define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */
#define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */
-#define BP_TAG_SERIAL_BAUSRATE 0x1004 /* baud rate of current console. */
+#define BP_TAG_SERIAL_BAUDRATE 0x1004 /* baud rate of current console. */
#define BP_TAG_SERIAL_PORT 0x1005 /* serial device of current console */
#define BP_TAG_FDT 0x1006 /* flat device tree addr */
diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h
index d9ab131..370b26f 100644
--- a/arch/xtensa/include/asm/cmpxchg.h
+++ b/arch/xtensa/include/asm/cmpxchg.h
@@ -93,6 +93,7 @@
((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\
(unsigned long)(n), sizeof(*(ptr))))
#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
+#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
/*
* xchg_u32
diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h
index 61fc5fa..3899610c 100644
--- a/arch/xtensa/include/asm/delay.h
+++ b/arch/xtensa/include/asm/delay.h
@@ -12,7 +12,7 @@
#ifndef _XTENSA_DELAY_H
#define _XTENSA_DELAY_H
-#include <asm/processor.h>
+#include <asm/timex.h>
#include <asm/param.h>
extern unsigned long loops_per_jiffy;
@@ -24,24 +24,17 @@
: "=r" (loops) : "0" (loops));
}
-static __inline__ u32 xtensa_get_ccount(void)
-{
- u32 ccount;
- asm volatile ("rsr %0, ccount\n" : "=r" (ccount));
- return ccount;
-}
-
/* For SMP/NUMA systems, change boot_cpu_data to something like
* local_cpu_data->... where local_cpu_data points to the current
* cpu. */
static __inline__ void udelay (unsigned long usecs)
{
- unsigned long start = xtensa_get_ccount();
+ unsigned long start = get_ccount();
unsigned long cycles = usecs * (loops_per_jiffy / (1000000UL / HZ));
/* Note: all variables are unsigned (can wrap around)! */
- while (((unsigned long)xtensa_get_ccount()) - start < cycles)
+ while (((unsigned long)get_ccount()) - start < cycles)
;
}
diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h
index 36dc7a6..73cc3f4 100644
--- a/arch/xtensa/include/asm/ftrace.h
+++ b/arch/xtensa/include/asm/ftrace.h
@@ -13,6 +13,7 @@
#include <asm/processor.h>
#define HAVE_ARCH_CALLER_ADDR
+#ifndef __ASSEMBLY__
#define CALLER_ADDR0 ({ unsigned long a0, a1; \
__asm__ __volatile__ ( \
"mov %0, a0\n" \
@@ -24,10 +25,22 @@
#define CALLER_ADDR1 return_address(1)
#define CALLER_ADDR2 return_address(2)
#define CALLER_ADDR3 return_address(3)
-#else
+#else /* CONFIG_FRAME_POINTER */
#define CALLER_ADDR1 (0)
#define CALLER_ADDR2 (0)
#define CALLER_ADDR3 (0)
-#endif
+#endif /* CONFIG_FRAME_POINTER */
+#endif /* __ASSEMBLY__ */
+
+#ifdef CONFIG_FUNCTION_TRACER
+
+#define MCOUNT_ADDR ((unsigned long)(_mcount))
+#define MCOUNT_INSN_SIZE 3
+
+#ifndef __ASSEMBLY__
+extern void _mcount(void);
+#define mcount _mcount
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_FUNCTION_TRACER */
#endif /* _XTENSA_FTRACE_H */
diff --git a/arch/xtensa/include/asm/pgtable.h b/arch/xtensa/include/asm/pgtable.h
index 8f017eb..0fdf5d0 100644
--- a/arch/xtensa/include/asm/pgtable.h
+++ b/arch/xtensa/include/asm/pgtable.h
@@ -5,7 +5,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * Copyright (C) 2001 - 2007 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
*/
#ifndef _XTENSA_PGTABLE_H
@@ -64,41 +64,82 @@
* Virtual memory area. We keep a distance to other memory regions to be
* on the safe side. We also use this area for cache aliasing.
*/
-
#define VMALLOC_START 0xC0000000
#define VMALLOC_END 0xC7FEFFFF
#define TLBTEMP_BASE_1 0xC7FF0000
#define TLBTEMP_BASE_2 0xC7FF8000
/*
- * Xtensa Linux config PTE layout (when present):
- * 31-12: PPN
- * 11-6: Software
- * 5-4: RING
- * 3-0: CA
+ * For the Xtensa architecture, the PTE layout is as follows:
*
- * Similar to the Alpha and MIPS ports, we need to keep track of the ref
- * and mod bits in software. We have a software "you can read
- * from this page" bit, and a hardware one which actually lets the
- * process read from the page. On the same token we have a software
- * writable bit and the real hardware one which actually lets the
- * process write to the page.
+ * 31------12 11 10-9 8-6 5-4 3-2 1-0
+ * +-----------------------------------------+
+ * | | Software | HARDWARE |
+ * | PPN | ADW | RI |Attribute|
+ * +-----------------------------------------+
+ * pte_none | MBZ | 01 | 11 | 00 |
+ * +-----------------------------------------+
+ * present | PPN | 0 | 00 | ADW | RI | CA | wx |
+ * +- - - - - - - - - - - - - - - - - - - - -+
+ * (PAGE_NONE)| PPN | 0 | 00 | ADW | 01 | 11 | 11 |
+ * +-----------------------------------------+
+ * swap | index | type | 01 | 11 | 00 |
+ * +- - - - - - - - - - - - - - - - - - - - -+
+ * file | file offset | 01 | 11 | 10 |
+ * +-----------------------------------------+
*
- * See further below for PTE layout for swapped-out pages.
+ * For T1050 hardware and earlier the layout differs for present and (PAGE_NONE)
+ * +-----------------------------------------+
+ * present | PPN | 0 | 00 | ADW | RI | CA | w1 |
+ * +-----------------------------------------+
+ * (PAGE_NONE)| PPN | 0 | 00 | ADW | 01 | 01 | 00 |
+ * +-----------------------------------------+
+ *
+ * Legend:
+ * PPN Physical Page Number
+ * ADW software: accessed (young) / dirty / writable
+ * RI ring (0=privileged, 1=user, 2 and 3 are unused)
+ * CA cache attribute: 00 bypass, 01 writeback, 10 writethrough
+ * (11 is invalid and used to mark pages that are not present)
+ * w page is writable (hw)
+ * x page is executable (hw)
+ * index swap offset / PAGE_SIZE (bit 11-31: 21 bits -> 8 GB)
+ * (note that the index is always non-zero)
+ * type swap type (5 bits -> 32 types)
+ * file offset 26-bit offset into the file, in increments of PAGE_SIZE
+ *
+ * Notes:
+ * - (PROT_NONE) is a special case of 'present' but causes an exception for
+ * any access (read, write, and execute).
+ * - 'multihit-exception' has the highest priority of all MMU exceptions,
+ * so the ring must be set to 'RING_USER' even for 'non-present' pages.
+ * - on older hardware, the exectuable flag was not supported and
+ * used as a 'valid' flag, so it needs to be always set.
+ * - we need to keep track of certain flags in software (dirty and young)
+ * to do this, we use write exceptions and have a separate software w-flag.
+ * - attribute value 1101 (and 1111 on T1050 and earlier) is reserved
*/
+#define _PAGE_ATTRIB_MASK 0xf
+
#define _PAGE_HW_EXEC (1<<0) /* hardware: page is executable */
#define _PAGE_HW_WRITE (1<<1) /* hardware: page is writable */
-#define _PAGE_FILE (1<<1) /* non-linear mapping, if !present */
-#define _PAGE_PROTNONE (3<<0) /* special case for VM_PROT_NONE */
-
-/* None of these cache modes include MP coherency: */
#define _PAGE_CA_BYPASS (0<<2) /* bypass, non-speculative */
#define _PAGE_CA_WB (1<<2) /* write-back */
#define _PAGE_CA_WT (2<<2) /* write-through */
#define _PAGE_CA_MASK (3<<2)
-#define _PAGE_INVALID (3<<2)
+#define _PAGE_CA_INVALID (3<<2)
+
+/* We use invalid attribute values to distinguish special pte entries */
+#if XCHAL_HW_VERSION_MAJOR < 2000
+#define _PAGE_HW_VALID 0x01 /* older HW needed this bit set */
+#define _PAGE_NONE 0x04
+#else
+#define _PAGE_HW_VALID 0x00
+#define _PAGE_NONE 0x0f
+#endif
+#define _PAGE_FILE (1<<1) /* file mapped page, only if !present */
#define _PAGE_USER (1<<4) /* user access (ring=1) */
@@ -108,19 +149,12 @@
#define _PAGE_DIRTY (1<<7) /* software: page dirty */
#define _PAGE_ACCESSED (1<<8) /* software: page accessed (read) */
-/* On older HW revisions, we always have to set bit 0 */
-#if XCHAL_HW_VERSION_MAJOR < 2000
-# define _PAGE_VALID (1<<0)
-#else
-# define _PAGE_VALID 0
-#endif
-
-#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
-#define _PAGE_PRESENT (_PAGE_VALID | _PAGE_CA_WB | _PAGE_ACCESSED)
-
#ifdef CONFIG_MMU
-#define PAGE_NONE __pgprot(_PAGE_INVALID | _PAGE_USER | _PAGE_PROTNONE)
+#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
+#define _PAGE_PRESENT (_PAGE_HW_VALID | _PAGE_CA_WB | _PAGE_ACCESSED)
+
+#define PAGE_NONE __pgprot(_PAGE_NONE | _PAGE_USER)
#define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER)
#define PAGE_COPY_EXEC __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_HW_EXEC)
#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER)
@@ -132,9 +166,9 @@
#define PAGE_KERNEL_EXEC __pgprot(_PAGE_PRESENT|_PAGE_HW_WRITE|_PAGE_HW_EXEC)
#if (DCACHE_WAY_SIZE > PAGE_SIZE)
-# define _PAGE_DIRECTORY (_PAGE_VALID | _PAGE_ACCESSED)
+# define _PAGE_DIRECTORY (_PAGE_HW_VALID | _PAGE_ACCESSED | _PAGE_CA_BYPASS)
#else
-# define _PAGE_DIRECTORY (_PAGE_VALID | _PAGE_ACCESSED | _PAGE_CA_WB)
+# define _PAGE_DIRECTORY (_PAGE_HW_VALID | _PAGE_ACCESSED | _PAGE_CA_WB)
#endif
#else /* no mmu */
@@ -202,12 +236,16 @@
/*
* pte status.
*/
-#define pte_none(pte) (pte_val(pte) == _PAGE_INVALID)
-#define pte_present(pte) \
- (((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_INVALID) \
- || ((pte_val(pte) & _PAGE_PROTNONE) == _PAGE_PROTNONE))
+# define pte_none(pte) (pte_val(pte) == (_PAGE_CA_INVALID | _PAGE_USER))
+#if XCHAL_HW_VERSION_MAJOR < 2000
+# define pte_present(pte) ((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_CA_INVALID)
+#else
+# define pte_present(pte) \
+ (((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_CA_INVALID) \
+ || ((pte_val(pte) & _PAGE_ATTRIB_MASK) == _PAGE_NONE))
+#endif
#define pte_clear(mm,addr,ptep) \
- do { update_pte(ptep, __pte(_PAGE_INVALID)); } while(0)
+ do { update_pte(ptep, __pte(_PAGE_CA_INVALID | _PAGE_USER)); } while (0)
#define pmd_none(pmd) (!pmd_val(pmd))
#define pmd_present(pmd) (pmd_val(pmd) & PAGE_MASK)
@@ -328,35 +366,23 @@
/*
- * Encode and decode a swap entry.
- *
- * Format of swap pte:
- * bit 0 MBZ
- * bit 1 page-file (must be zero)
- * bits 2 - 3 page hw access mode (must be 11: _PAGE_INVALID)
- * bits 4 - 5 ring protection (must be 01: _PAGE_USER)
- * bits 6 - 10 swap type (5 bits -> 32 types)
- * bits 11 - 31 swap offset / PAGE_SIZE (21 bits -> 8GB)
-
- * Format of file pte:
- * bit 0 MBZ
- * bit 1 page-file (must be one: _PAGE_FILE)
- * bits 2 - 3 page hw access mode (must be 11: _PAGE_INVALID)
- * bits 4 - 5 ring protection (must be 01: _PAGE_USER)
- * bits 6 - 31 file offset / PAGE_SIZE
+ * Encode and decode a swap and file entry.
*/
+#define SWP_TYPE_BITS 5
+#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS)
#define __swp_type(entry) (((entry).val >> 6) & 0x1f)
#define __swp_offset(entry) ((entry).val >> 11)
#define __swp_entry(type,offs) \
- ((swp_entry_t) {((type) << 6) | ((offs) << 11) | _PAGE_INVALID})
+ ((swp_entry_t){((type) << 6) | ((offs) << 11) | \
+ _PAGE_CA_INVALID | _PAGE_USER})
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
#define __swp_entry_to_pte(x) ((pte_t) { (x).val })
-#define PTE_FILE_MAX_BITS 28
-#define pte_to_pgoff(pte) (pte_val(pte) >> 4)
+#define PTE_FILE_MAX_BITS 26
+#define pte_to_pgoff(pte) (pte_val(pte) >> 6)
#define pgoff_to_pte(off) \
- ((pte_t) { ((off) << 4) | _PAGE_INVALID | _PAGE_FILE })
+ ((pte_t) { ((off) << 6) | _PAGE_CA_INVALID | _PAGE_FILE | _PAGE_USER })
#endif /* !defined (__ASSEMBLY__) */
diff --git a/arch/xtensa/include/asm/platform.h b/arch/xtensa/include/asm/platform.h
index ec098b6..32e98f2 100644
--- a/arch/xtensa/include/asm/platform.h
+++ b/arch/xtensa/include/asm/platform.h
@@ -30,11 +30,6 @@
extern void platform_setup (char **);
/*
- * platform_init_irq is called from init_IRQ.
- */
-extern void platform_init_irq (void);
-
-/*
* platform_restart is called to restart the system.
*/
extern void platform_restart (void);
diff --git a/arch/xtensa/include/asm/timex.h b/arch/xtensa/include/asm/timex.h
index 3d35e5d..69f9017 100644
--- a/arch/xtensa/include/asm/timex.h
+++ b/arch/xtensa/include/asm/timex.h
@@ -35,19 +35,11 @@
# error "Bad timer number for Linux configurations!"
#endif
-#define LINUX_TIMER_MASK (1L << LINUX_TIMER_INT)
-
-#define CLOCK_TICK_RATE 1193180 /* (everyone is using this value) */
-#define CLOCK_TICK_FACTOR 20 /* Factor of both 10^6 and CLOCK_TICK_RATE */
-
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
-extern unsigned long ccount_per_jiffy;
-extern unsigned long nsec_per_ccount;
-#define CCOUNT_PER_JIFFY ccount_per_jiffy
-#define NSEC_PER_CCOUNT nsec_per_ccount
+extern unsigned long ccount_freq;
+#define CCOUNT_PER_JIFFY (ccount_freq / HZ)
#else
#define CCOUNT_PER_JIFFY (CONFIG_XTENSA_CPU_CLOCK*(1000000UL/HZ))
-#define NSEC_PER_CCOUNT (1000UL / CONFIG_XTENSA_CPU_CLOCK)
#endif
diff --git a/arch/xtensa/kernel/.gitignore b/arch/xtensa/kernel/.gitignore
new file mode 100644
index 0000000..c5f676c
--- /dev/null
+++ b/arch/xtensa/kernel/.gitignore
@@ -0,0 +1 @@
+vmlinux.lds
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile
index 1e7fc87..f90265e 100644
--- a/arch/xtensa/kernel/Makefile
+++ b/arch/xtensa/kernel/Makefile
@@ -11,6 +11,7 @@
obj-$(CONFIG_KGDB) += xtensa-stub.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o
+obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
AFLAGS_head.o += -mtext-section-literals
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index 5082507..9298742 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -458,7 +458,7 @@
_bbsi.l a4, TIF_NEED_RESCHED, 3f
_bbsi.l a4, TIF_NOTIFY_RESUME, 2f
- _bbci.l a4, TIF_SIGPENDING, 4f
+ _bbci.l a4, TIF_SIGPENDING, 5f
2: l32i a4, a1, PT_DEPC
bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f
@@ -476,6 +476,13 @@
callx4 a4
j 1b
+5:
+#ifdef CONFIG_DEBUG_TLB_SANITY
+ l32i a4, a1, PT_DEPC
+ bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f
+ movi a4, check_tlb_sanity
+ callx4 a4
+#endif
4: /* Restore optional registers. */
load_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT
@@ -1792,10 +1799,15 @@
l32i a0, a0, 0
beqz a0, 2f
- /* Note that we assume _PAGE_WRITABLE_BIT is only set if pte is valid.*/
+ /*
+ * Note that we test _PAGE_WRITABLE_BIT only if PTE is present
+ * and is not PAGE_NONE. See pgtable.h for possible PTE layouts.
+ */
_PTE_OFFSET(a0, a1, a4)
l32i a4, a0, 0 # read pteval
+ movi a1, _PAGE_CA_INVALID
+ ball a4, a1, 2f
bbci.l a4, _PAGE_WRITABLE_BIT, 2f
movi a1, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_HW_WRITE
diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S
index ef12c0e..7d740eb 100644
--- a/arch/xtensa/kernel/head.S
+++ b/arch/xtensa/kernel/head.S
@@ -68,6 +68,15 @@
#ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX
initialize_mmu
+#if defined(CONFIG_MMU) && XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY
+ rsr a2, excsave1
+ movi a3, 0x08000000
+ bgeu a2, a3, 1f
+ movi a3, 0xd0000000
+ add a2, a2, a3
+ wsr a2, excsave1
+1:
+#endif
#endif
.end no-absolute-literals
diff --git a/arch/xtensa/kernel/mcount.S b/arch/xtensa/kernel/mcount.S
new file mode 100644
index 0000000..0eeda2e
--- /dev/null
+++ b/arch/xtensa/kernel/mcount.S
@@ -0,0 +1,50 @@
+/*
+ * arch/xtensa/kernel/mcount.S
+ *
+ * Xtensa specific mcount support
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2013 Tensilica Inc.
+ */
+
+#include <linux/linkage.h>
+#include <asm/ftrace.h>
+
+/*
+ * Entry condition:
+ *
+ * a2: a0 of the caller
+ */
+
+ENTRY(_mcount)
+
+ entry a1, 16
+
+ movi a4, ftrace_trace_function
+ l32i a4, a4, 0
+ movi a3, ftrace_stub
+ bne a3, a4, 1f
+ retw
+
+1: xor a7, a2, a1
+ movi a3, 0x3fffffff
+ and a7, a7, a3
+ xor a7, a7, a1
+
+ xor a6, a0, a1
+ and a6, a6, a3
+ xor a6, a6, a1
+ addi a6, a6, -MCOUNT_INSN_SIZE
+ callx4 a4
+
+ retw
+
+ENDPROC(_mcount)
+
+ENTRY(ftrace_stub)
+ entry a1, 16
+ retw
+ENDPROC(ftrace_stub)
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
index 126c188..5b34033 100644
--- a/arch/xtensa/kernel/pci.c
+++ b/arch/xtensa/kernel/pci.c
@@ -77,9 +77,9 @@
if (res->flags & IORESOURCE_IO) {
if (size > 0x100) {
- printk(KERN_ERR "PCI: I/O Region %s/%d too large"
- " (%ld bytes)\n", pci_name(dev),
- dev->resource - res, size);
+ pr_err("PCI: I/O Region %s/%d too large (%u bytes)\n",
+ pci_name(dev), dev->resource - res,
+ size);
}
if (start & 0x300)
@@ -174,7 +174,7 @@
struct pci_controller *pci_ctrl;
struct list_head resources;
struct pci_bus *bus;
- int next_busno = 0, i;
+ int next_busno = 0;
printk("PCI: Probing PCI hardware\n");
@@ -197,7 +197,7 @@
subsys_initcall(pcibios_init);
-void __init pcibios_fixup_bus(struct pci_bus *bus)
+void pcibios_fixup_bus(struct pci_bus *bus)
{
if (bus->parent) {
/* This is a subordinate bridge */
diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c
index 2bd6c35..1cf0082 100644
--- a/arch/xtensa/kernel/platform.c
+++ b/arch/xtensa/kernel/platform.c
@@ -29,7 +29,6 @@
*/
_F(void, setup, (char** cmd), { });
-_F(void, init_irq, (void), { });
_F(void, restart, (void), { while(1); });
_F(void, halt, (void), { while(1); });
_F(void, power_off, (void), { while(1); });
@@ -42,6 +41,6 @@
_F(void, calibrate_ccount, (void),
{
pr_err("ERROR: Cannot calibrate cpu frequency! Assuming 10MHz.\n");
- ccount_per_jiffy = 10 * (1000000UL/HZ);
+ ccount_freq = 10 * 1000000UL;
});
#endif
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
index 6dd25ec..42a8bba 100644
--- a/arch/xtensa/kernel/setup.c
+++ b/arch/xtensa/kernel/setup.c
@@ -152,8 +152,8 @@
{
meminfo_t* mi;
mi = (meminfo_t*)(tag->data);
- initrd_start = (void*)(mi->start);
- initrd_end = (void*)(mi->end);
+ initrd_start = __va(mi->start);
+ initrd_end = __va(mi->end);
return 0;
}
@@ -164,7 +164,7 @@
static int __init parse_tag_fdt(const bp_tag_t *tag)
{
- dtb_start = (void *)(tag->data[0]);
+ dtb_start = __va(tag->data[0]);
return 0;
}
@@ -256,7 +256,7 @@
static void __init copy_devtree(void)
{
void *alloc = early_init_dt_alloc_memory_arch(
- be32_to_cpu(initial_boot_params->totalsize), 0);
+ be32_to_cpu(initial_boot_params->totalsize), 8);
if (alloc) {
memcpy(alloc, initial_boot_params,
be32_to_cpu(initial_boot_params->totalsize));
diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c
index ffb4741..bdbb173 100644
--- a/arch/xtensa/kernel/time.c
+++ b/arch/xtensa/kernel/time.c
@@ -16,6 +16,7 @@
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -23,13 +24,13 @@
#include <linux/profile.h>
#include <linux/delay.h>
#include <linux/irqdomain.h>
+#include <linux/sched_clock.h>
#include <asm/timex.h>
#include <asm/platform.h>
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
-unsigned long ccount_per_jiffy; /* per 1/HZ */
-unsigned long nsec_per_ccount; /* nsec per ccount increment */
+unsigned long ccount_freq; /* ccount Hz */
#endif
static cycle_t ccount_read(struct clocksource *cs)
@@ -37,6 +38,11 @@
return (cycle_t)get_ccount();
}
+static u32 notrace ccount_sched_clock_read(void)
+{
+ return get_ccount();
+}
+
static struct clocksource ccount_clocksource = {
.name = "ccount",
.rating = 200,
@@ -44,29 +50,98 @@
.mask = CLOCKSOURCE_MASK(32),
};
+static int ccount_timer_set_next_event(unsigned long delta,
+ struct clock_event_device *dev);
+static void ccount_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt);
+static struct ccount_timer_t {
+ struct clock_event_device evt;
+ int irq_enabled;
+} ccount_timer = {
+ .evt = {
+ .name = "ccount_clockevent",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 300,
+ .set_next_event = ccount_timer_set_next_event,
+ .set_mode = ccount_timer_set_mode,
+ },
+};
+
+static int ccount_timer_set_next_event(unsigned long delta,
+ struct clock_event_device *dev)
+{
+ unsigned long flags, next;
+ int ret = 0;
+
+ local_irq_save(flags);
+ next = get_ccount() + delta;
+ set_linux_timer(next);
+ if (next - get_ccount() > delta)
+ ret = -ETIME;
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+static void ccount_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ struct ccount_timer_t *timer =
+ container_of(evt, struct ccount_timer_t, evt);
+
+ /*
+ * There is no way to disable the timer interrupt at the device level,
+ * only at the intenable register itself. Since enable_irq/disable_irq
+ * calls are nested, we need to make sure that these calls are
+ * balanced.
+ */
+ switch (mode) {
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ if (timer->irq_enabled) {
+ disable_irq(evt->irq);
+ timer->irq_enabled = 0;
+ }
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ case CLOCK_EVT_MODE_ONESHOT:
+ if (!timer->irq_enabled) {
+ enable_irq(evt->irq);
+ timer->irq_enabled = 1;
+ }
+ default:
+ break;
+ }
+}
+
static irqreturn_t timer_interrupt(int irq, void *dev_id);
static struct irqaction timer_irqaction = {
.handler = timer_interrupt,
- .flags = IRQF_DISABLED,
+ .flags = IRQF_TIMER,
.name = "timer",
+ .dev_id = &ccount_timer,
};
void __init time_init(void)
{
- unsigned int irq;
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
printk("Calibrating CPU frequency ");
platform_calibrate_ccount();
- printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ),
- (int)(ccount_per_jiffy/(10000/HZ))%100);
+ printk("%d.%02d MHz\n", (int)ccount_freq/1000000,
+ (int)(ccount_freq/10000)%100);
#endif
clocksource_register_hz(&ccount_clocksource, CCOUNT_PER_JIFFY * HZ);
- /* Initialize the linux timer interrupt. */
+ ccount_timer.evt.cpumask = cpumask_of(0);
+ ccount_timer.evt.irq = irq_create_mapping(NULL, LINUX_TIMER_INT);
+ if (WARN(!ccount_timer.evt.irq, "error: can't map timer irq"))
+ return;
+ clockevents_config_and_register(&ccount_timer.evt, ccount_freq, 0xf,
+ 0xffffffff);
+ setup_irq(ccount_timer.evt.irq, &timer_irqaction);
+ ccount_timer.irq_enabled = 1;
- irq = irq_create_mapping(NULL, LINUX_TIMER_INT);
- setup_irq(irq, &timer_irqaction);
- set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY);
+ setup_sched_clock(ccount_sched_clock_read, 32, ccount_freq);
}
/*
@@ -75,36 +150,14 @@
irqreturn_t timer_interrupt (int irq, void *dev_id)
{
+ struct ccount_timer_t *timer = dev_id;
+ struct clock_event_device *evt = &timer->evt;
- unsigned long next;
-
- next = get_linux_timer();
-
-again:
- while ((signed long)(get_ccount() - next) > 0) {
-
- profile_tick(CPU_PROFILING);
-#ifndef CONFIG_SMP
- update_process_times(user_mode(get_irq_regs()));
-#endif
-
- xtime_update(1); /* Linux handler in kernel/time/timekeeping */
-
- /* Note that writing CCOMPARE clears the interrupt. */
-
- next += CCOUNT_PER_JIFFY;
- set_linux_timer(next);
- }
+ evt->event_handler(evt);
/* Allow platform to do something useful (Wdog). */
-
platform_heartbeat();
- /* Make sure we didn't miss any tick... */
-
- if ((signed long)(get_ccount() - next) > 0)
- goto again;
-
return IRQ_HANDLED;
}
diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c
index 42c53c87..d8507f8 100644
--- a/arch/xtensa/kernel/xtensa_ksyms.c
+++ b/arch/xtensa/kernel/xtensa_ksyms.c
@@ -124,3 +124,7 @@
extern long _spill_registers;
EXPORT_SYMBOL(common_exception_return);
EXPORT_SYMBOL(_spill_registers);
+
+#ifdef CONFIG_FUNCTION_TRACER
+EXPORT_SYMBOL(_mcount);
+#endif
diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c
index 5411aa6..ca9d236 100644
--- a/arch/xtensa/mm/tlb.c
+++ b/arch/xtensa/mm/tlb.c
@@ -64,7 +64,7 @@
{
if (mm == current->active_mm) {
unsigned long flags;
- local_save_flags(flags);
+ local_irq_save(flags);
__get_new_mmu_context(mm);
__load_mmu_context(mm);
local_irq_restore(flags);
@@ -94,7 +94,7 @@
printk("[tlbrange<%02lx,%08lx,%08lx>]\n",
(unsigned long)mm->context, start, end);
#endif
- local_save_flags(flags);
+ local_irq_save(flags);
if (end-start + (PAGE_SIZE-1) <= _TLB_ENTRIES << PAGE_SHIFT) {
int oldpid = get_rasid_register();
@@ -128,9 +128,10 @@
if(mm->context == NO_CONTEXT)
return;
- local_save_flags(flags);
+ local_irq_save(flags);
oldpid = get_rasid_register();
+ set_rasid_register(ASID_INSERT(mm->context));
if (vma->vm_flags & VM_EXEC)
invalidate_itlb_mapping(page);
@@ -140,3 +141,116 @@
local_irq_restore(flags);
}
+
+#ifdef CONFIG_DEBUG_TLB_SANITY
+
+static unsigned get_pte_for_vaddr(unsigned vaddr)
+{
+ struct task_struct *task = get_current();
+ struct mm_struct *mm = task->mm;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (!mm)
+ mm = task->active_mm;
+ pgd = pgd_offset(mm, vaddr);
+ if (pgd_none_or_clear_bad(pgd))
+ return 0;
+ pmd = pmd_offset(pgd, vaddr);
+ if (pmd_none_or_clear_bad(pmd))
+ return 0;
+ pte = pte_offset_map(pmd, vaddr);
+ if (!pte)
+ return 0;
+ return pte_val(*pte);
+}
+
+enum {
+ TLB_SUSPICIOUS = 1,
+ TLB_INSANE = 2,
+};
+
+static void tlb_insane(void)
+{
+ BUG_ON(1);
+}
+
+static void tlb_suspicious(void)
+{
+ WARN_ON(1);
+}
+
+/*
+ * Check that TLB entries with kernel ASID (1) have kernel VMA (>= TASK_SIZE),
+ * and TLB entries with user ASID (>=4) have VMA < TASK_SIZE.
+ *
+ * Check that valid TLB entries either have the same PA as the PTE, or PTE is
+ * marked as non-present. Non-present PTE and the page with non-zero refcount
+ * and zero mapcount is normal for batched TLB flush operation. Zero refcount
+ * means that the page was freed prematurely. Non-zero mapcount is unusual,
+ * but does not necessary means an error, thus marked as suspicious.
+ */
+static int check_tlb_entry(unsigned w, unsigned e, bool dtlb)
+{
+ unsigned tlbidx = w | (e << PAGE_SHIFT);
+ unsigned r0 = dtlb ?
+ read_dtlb_virtual(tlbidx) : read_itlb_virtual(tlbidx);
+ unsigned vpn = (r0 & PAGE_MASK) | (e << PAGE_SHIFT);
+ unsigned pte = get_pte_for_vaddr(vpn);
+ unsigned mm_asid = (get_rasid_register() >> 8) & ASID_MASK;
+ unsigned tlb_asid = r0 & ASID_MASK;
+ bool kernel = tlb_asid == 1;
+ int rc = 0;
+
+ if (tlb_asid > 0 && ((vpn < TASK_SIZE) == kernel)) {
+ pr_err("%cTLB: way: %u, entry: %u, VPN %08x in %s PTE\n",
+ dtlb ? 'D' : 'I', w, e, vpn,
+ kernel ? "kernel" : "user");
+ rc |= TLB_INSANE;
+ }
+
+ if (tlb_asid == mm_asid) {
+ unsigned r1 = dtlb ? read_dtlb_translation(tlbidx) :
+ read_itlb_translation(tlbidx);
+ if ((pte ^ r1) & PAGE_MASK) {
+ pr_err("%cTLB: way: %u, entry: %u, mapping: %08x->%08x, PTE: %08x\n",
+ dtlb ? 'D' : 'I', w, e, r0, r1, pte);
+ if (pte == 0 || !pte_present(__pte(pte))) {
+ struct page *p = pfn_to_page(r1 >> PAGE_SHIFT);
+ pr_err("page refcount: %d, mapcount: %d\n",
+ page_count(p),
+ page_mapcount(p));
+ if (!page_count(p))
+ rc |= TLB_INSANE;
+ else if (page_mapped(p))
+ rc |= TLB_SUSPICIOUS;
+ } else {
+ rc |= TLB_INSANE;
+ }
+ }
+ }
+ return rc;
+}
+
+void check_tlb_sanity(void)
+{
+ unsigned long flags;
+ unsigned w, e;
+ int bug = 0;
+
+ local_irq_save(flags);
+ for (w = 0; w < DTLB_ARF_WAYS; ++w)
+ for (e = 0; e < (1 << XCHAL_DTLB_ARF_ENTRIES_LOG2); ++e)
+ bug |= check_tlb_entry(w, e, true);
+ for (w = 0; w < ITLB_ARF_WAYS; ++w)
+ for (e = 0; e < (1 << XCHAL_ITLB_ARF_ENTRIES_LOG2); ++e)
+ bug |= check_tlb_entry(w, e, false);
+ if (bug & TLB_INSANE)
+ tlb_insane();
+ if (bug & TLB_SUSPICIOUS)
+ tlb_suspicious();
+ local_irq_restore(flags);
+}
+
+#endif /* CONFIG_DEBUG_TLB_SANITY */
diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c
index 7d0fea6..56f88b7 100644
--- a/arch/xtensa/platforms/iss/network.c
+++ b/arch/xtensa/platforms/iss/network.c
@@ -700,7 +700,7 @@
#define ERR KERN_ERR "iss_net_setup: "
-static int iss_net_setup(char *str)
+static int __init iss_net_setup(char *str)
{
struct iss_net_private *device = NULL;
struct iss_net_init *new;
diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index c0edb35..8c6e819 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -108,13 +108,13 @@
sector_t sector = bio->bi_sector;
bio_for_each_segment(bvec, bio, i) {
- char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
+ char *buffer = __bio_kmap_atomic(bio, i);
unsigned len = bvec->bv_len >> SECTOR_SHIFT;
simdisk_transfer(dev, sector, len, buffer,
bio_data_dir(bio) == WRITE);
sector += len;
- __bio_kunmap_atomic(bio, KM_USER0);
+ __bio_kunmap_atomic(bio);
}
return 0;
}
diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c
index 96ef8ee..74bb74f 100644
--- a/arch/xtensa/platforms/xtfpga/setup.c
+++ b/arch/xtensa/platforms/xtfpga/setup.c
@@ -163,7 +163,7 @@
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
-void platform_calibrate_ccount(void)
+void __init platform_calibrate_ccount(void)
{
long clk_freq = 0;
#ifdef CONFIG_OF
@@ -179,8 +179,7 @@
if (!clk_freq)
clk_freq = *(long *)XTFPGA_CLKFRQ_VADDR;
- ccount_per_jiffy = clk_freq / HZ;
- nsec_per_ccount = 1000000000UL / clk_freq;
+ ccount_freq = clk_freq;
}
#endif
diff --git a/arch/xtensa/variants/s6000/delay.c b/arch/xtensa/variants/s6000/delay.c
index 54b2b57..3915456 100644
--- a/arch/xtensa/variants/s6000/delay.c
+++ b/arch/xtensa/variants/s6000/delay.c
@@ -1,4 +1,3 @@
-#include <asm/delay.h>
#include <asm/timex.h>
#include <asm/io.h>
#include <variant/hardware.h>
@@ -17,11 +16,10 @@
"1: l32i %0, %2, 0 ;"
" beq %0, %1, 1b ;"
: "=&a"(u) : "a"(t), "a"(tstamp));
- b = xtensa_get_ccount();
+ b = get_ccount();
if (i == LOOPS)
a = b;
} while (--i >= 0);
b -= a;
- nsec_per_ccount = (LOOPS * 10000) / b;
- ccount_per_jiffy = b * (100000UL / (LOOPS * HZ));
+ ccount_freq = b * (100000UL / LOOPS);
}
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index e8918ff..290792a 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -32,26 +32,6 @@
static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
-static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg,
- struct request_queue *q, bool update_hint);
-
-/**
- * blkg_for_each_descendant_pre - pre-order walk of a blkg's descendants
- * @d_blkg: loop cursor pointing to the current descendant
- * @pos_cgrp: used for iteration
- * @p_blkg: target blkg to walk descendants of
- *
- * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU
- * read locked. If called under either blkcg or queue lock, the iteration
- * is guaranteed to include all and only online blkgs. The caller may
- * update @pos_cgrp by calling cgroup_rightmost_descendant() to skip
- * subtree.
- */
-#define blkg_for_each_descendant_pre(d_blkg, pos_cgrp, p_blkg) \
- cgroup_for_each_descendant_pre((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \
- if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \
- (p_blkg)->q, false)))
-
static bool blkcg_policy_enabled(struct request_queue *q,
const struct blkcg_policy *pol)
{
@@ -71,18 +51,8 @@
if (!blkg)
return;
- for (i = 0; i < BLKCG_MAX_POLS; i++) {
- struct blkcg_policy *pol = blkcg_policy[i];
- struct blkg_policy_data *pd = blkg->pd[i];
-
- if (!pd)
- continue;
-
- if (pol && pol->pd_exit_fn)
- pol->pd_exit_fn(blkg);
-
- kfree(pd);
- }
+ for (i = 0; i < BLKCG_MAX_POLS; i++)
+ kfree(blkg->pd[i]);
blk_exit_rl(&blkg->rl);
kfree(blkg);
@@ -134,10 +104,6 @@
blkg->pd[i] = pd;
pd->blkg = blkg;
pd->plid = i;
-
- /* invoke per-policy init */
- if (pol->pd_init_fn)
- pol->pd_init_fn(blkg);
}
return blkg;
@@ -158,8 +124,8 @@
* @q's bypass state. If @update_hint is %true, the caller should be
* holding @q->queue_lock and lookup hint is updated on success.
*/
-static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg,
- struct request_queue *q, bool update_hint)
+struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q,
+ bool update_hint)
{
struct blkcg_gq *blkg;
@@ -234,16 +200,25 @@
}
blkg = new_blkg;
- /* link parent and insert */
+ /* link parent */
if (blkcg_parent(blkcg)) {
blkg->parent = __blkg_lookup(blkcg_parent(blkcg), q, false);
if (WARN_ON_ONCE(!blkg->parent)) {
- blkg = ERR_PTR(-EINVAL);
+ ret = -EINVAL;
goto err_put_css;
}
blkg_get(blkg->parent);
}
+ /* invoke per-policy init */
+ for (i = 0; i < BLKCG_MAX_POLS; i++) {
+ struct blkcg_policy *pol = blkcg_policy[i];
+
+ if (blkg->pd[i] && pol->pd_init_fn)
+ pol->pd_init_fn(blkg);
+ }
+
+ /* insert */
spin_lock(&blkcg->lock);
ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg);
if (likely(!ret)) {
@@ -394,30 +369,38 @@
q->root_rl.blkg = NULL;
}
-static void blkg_rcu_free(struct rcu_head *rcu_head)
+/*
+ * A group is RCU protected, but having an rcu lock does not mean that one
+ * can access all the fields of blkg and assume these are valid. For
+ * example, don't try to follow throtl_data and request queue links.
+ *
+ * Having a reference to blkg under an rcu allows accesses to only values
+ * local to groups like group stats and group rate limits.
+ */
+void __blkg_release_rcu(struct rcu_head *rcu_head)
{
- blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head));
-}
+ struct blkcg_gq *blkg = container_of(rcu_head, struct blkcg_gq, rcu_head);
+ int i;
-void __blkg_release(struct blkcg_gq *blkg)
-{
+ /* tell policies that this one is being freed */
+ for (i = 0; i < BLKCG_MAX_POLS; i++) {
+ struct blkcg_policy *pol = blkcg_policy[i];
+
+ if (blkg->pd[i] && pol->pd_exit_fn)
+ pol->pd_exit_fn(blkg);
+ }
+
/* release the blkcg and parent blkg refs this blkg has been holding */
css_put(&blkg->blkcg->css);
- if (blkg->parent)
+ if (blkg->parent) {
+ spin_lock_irq(blkg->q->queue_lock);
blkg_put(blkg->parent);
+ spin_unlock_irq(blkg->q->queue_lock);
+ }
- /*
- * A group is freed in rcu manner. But having an rcu lock does not
- * mean that one can access all the fields of blkg and assume these
- * are valid. For example, don't try to follow throtl_data and
- * request queue links.
- *
- * Having a reference to blkg under an rcu allows acess to only
- * values local to groups like group stats and group rate limits
- */
- call_rcu(&blkg->rcu_head, blkg_rcu_free);
+ blkg_free(blkg);
}
-EXPORT_SYMBOL_GPL(__blkg_release);
+EXPORT_SYMBOL_GPL(__blkg_release_rcu);
/*
* The next function used by blk_queue_for_each_rl(). It's a bit tricky
@@ -928,14 +911,6 @@
.subsys_id = blkio_subsys_id,
.base_cftypes = blkcg_files,
.module = THIS_MODULE,
-
- /*
- * blkio subsystem is utterly broken in terms of hierarchy support.
- * It treats all cgroups equally regardless of where they're
- * located in the hierarchy - all cgroups are treated as if they're
- * right below the root. Fix it and remove the following.
- */
- .broken_hierarchy = true,
};
EXPORT_SYMBOL_GPL(blkio_subsys);
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h
index 4e595ee..8056c03 100644
--- a/block/blk-cgroup.h
+++ b/block/blk-cgroup.h
@@ -266,7 +266,7 @@
blkg->refcnt++;
}
-void __blkg_release(struct blkcg_gq *blkg);
+void __blkg_release_rcu(struct rcu_head *rcu);
/**
* blkg_put - put a blkg reference
@@ -279,9 +279,43 @@
lockdep_assert_held(blkg->q->queue_lock);
WARN_ON_ONCE(blkg->refcnt <= 0);
if (!--blkg->refcnt)
- __blkg_release(blkg);
+ call_rcu(&blkg->rcu_head, __blkg_release_rcu);
}
+struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q,
+ bool update_hint);
+
+/**
+ * blkg_for_each_descendant_pre - pre-order walk of a blkg's descendants
+ * @d_blkg: loop cursor pointing to the current descendant
+ * @pos_cgrp: used for iteration
+ * @p_blkg: target blkg to walk descendants of
+ *
+ * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU
+ * read locked. If called under either blkcg or queue lock, the iteration
+ * is guaranteed to include all and only online blkgs. The caller may
+ * update @pos_cgrp by calling cgroup_rightmost_descendant() to skip
+ * subtree.
+ */
+#define blkg_for_each_descendant_pre(d_blkg, pos_cgrp, p_blkg) \
+ cgroup_for_each_descendant_pre((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \
+ if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \
+ (p_blkg)->q, false)))
+
+/**
+ * blkg_for_each_descendant_post - post-order walk of a blkg's descendants
+ * @d_blkg: loop cursor pointing to the current descendant
+ * @pos_cgrp: used for iteration
+ * @p_blkg: target blkg to walk descendants of
+ *
+ * Similar to blkg_for_each_descendant_pre() but performs post-order
+ * traversal instead. Synchronization rules are the same.
+ */
+#define blkg_for_each_descendant_post(d_blkg, pos_cgrp, p_blkg) \
+ cgroup_for_each_descendant_post((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \
+ if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \
+ (p_blkg)->q, false)))
+
/**
* blk_get_rl - get request_list to use
* @q: request_queue of interest
diff --git a/block/blk-tag.c b/block/blk-tag.c
index cc345e1..3f33d86 100644
--- a/block/blk-tag.c
+++ b/block/blk-tag.c
@@ -348,9 +348,16 @@
*/
max_depth = bqt->max_depth;
if (!rq_is_sync(rq) && max_depth > 1) {
- max_depth -= 2;
- if (!max_depth)
+ switch (max_depth) {
+ case 2:
max_depth = 1;
+ break;
+ case 3:
+ max_depth = 2;
+ break;
+ default:
+ max_depth -= 2;
+ }
if (q->in_flight[BLK_RW_ASYNC] > max_depth)
return 1;
}
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 3114622..08a32df 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -25,18 +25,61 @@
/* A workqueue to queue throttle related work */
static struct workqueue_struct *kthrotld_workqueue;
-static void throtl_schedule_delayed_work(struct throtl_data *td,
- unsigned long delay);
-struct throtl_rb_root {
- struct rb_root rb;
- struct rb_node *left;
- unsigned int count;
- unsigned long min_disptime;
+/*
+ * To implement hierarchical throttling, throtl_grps form a tree and bios
+ * are dispatched upwards level by level until they reach the top and get
+ * issued. When dispatching bios from the children and local group at each
+ * level, if the bios are dispatched into a single bio_list, there's a risk
+ * of a local or child group which can queue many bios at once filling up
+ * the list starving others.
+ *
+ * To avoid such starvation, dispatched bios are queued separately
+ * according to where they came from. When they are again dispatched to
+ * the parent, they're popped in round-robin order so that no single source
+ * hogs the dispatch window.
+ *
+ * throtl_qnode is used to keep the queued bios separated by their sources.
+ * Bios are queued to throtl_qnode which in turn is queued to
+ * throtl_service_queue and then dispatched in round-robin order.
+ *
+ * It's also used to track the reference counts on blkg's. A qnode always
+ * belongs to a throtl_grp and gets queued on itself or the parent, so
+ * incrementing the reference of the associated throtl_grp when a qnode is
+ * queued and decrementing when dequeued is enough to keep the whole blkg
+ * tree pinned while bios are in flight.
+ */
+struct throtl_qnode {
+ struct list_head node; /* service_queue->queued[] */
+ struct bio_list bios; /* queued bios */
+ struct throtl_grp *tg; /* tg this qnode belongs to */
};
-#define THROTL_RB_ROOT (struct throtl_rb_root) { .rb = RB_ROOT, .left = NULL, \
- .count = 0, .min_disptime = 0}
+struct throtl_service_queue {
+ struct throtl_service_queue *parent_sq; /* the parent service_queue */
+
+ /*
+ * Bios queued directly to this service_queue or dispatched from
+ * children throtl_grp's.
+ */
+ struct list_head queued[2]; /* throtl_qnode [READ/WRITE] */
+ unsigned int nr_queued[2]; /* number of queued bios */
+
+ /*
+ * RB tree of active children throtl_grp's, which are sorted by
+ * their ->disptime.
+ */
+ struct rb_root pending_tree; /* RB tree of active tgs */
+ struct rb_node *first_pending; /* first node in the tree */
+ unsigned int nr_pending; /* # queued in the tree */
+ unsigned long first_pending_disptime; /* disptime of the first tg */
+ struct timer_list pending_timer; /* fires on first_pending_disptime */
+};
+
+enum tg_state_flags {
+ THROTL_TG_PENDING = 1 << 0, /* on parent's pending tree */
+ THROTL_TG_WAS_EMPTY = 1 << 1, /* bio_lists[] became non-empty */
+};
#define rb_entry_tg(node) rb_entry((node), struct throtl_grp, rb_node)
@@ -52,9 +95,26 @@
/* must be the first member */
struct blkg_policy_data pd;
- /* active throtl group service_tree member */
+ /* active throtl group service_queue member */
struct rb_node rb_node;
+ /* throtl_data this group belongs to */
+ struct throtl_data *td;
+
+ /* this group's service queue */
+ struct throtl_service_queue service_queue;
+
+ /*
+ * qnode_on_self is used when bios are directly queued to this
+ * throtl_grp so that local bios compete fairly with bios
+ * dispatched from children. qnode_on_parent is used when bios are
+ * dispatched from this throtl_grp into its parent and will compete
+ * with the sibling qnode_on_parents and the parent's
+ * qnode_on_self.
+ */
+ struct throtl_qnode qnode_on_self[2];
+ struct throtl_qnode qnode_on_parent[2];
+
/*
* Dispatch time in jiffies. This is the estimated time when group
* will unthrottle and is ready to dispatch more bio. It is used as
@@ -64,11 +124,8 @@
unsigned int flags;
- /* Two lists for READ and WRITE */
- struct bio_list bio_lists[2];
-
- /* Number of queued bios on READ and WRITE lists */
- unsigned int nr_queued[2];
+ /* are there any throtl rules between this group and td? */
+ bool has_rules[2];
/* bytes per second rate limits */
uint64_t bps[2];
@@ -85,9 +142,6 @@
unsigned long slice_start[2];
unsigned long slice_end[2];
- /* Some throttle limits got updated for the group */
- int limits_changed;
-
/* Per cpu stats pointer */
struct tg_stats_cpu __percpu *stats_cpu;
@@ -98,7 +152,7 @@
struct throtl_data
{
/* service tree for active throtl groups */
- struct throtl_rb_root tg_service_tree;
+ struct throtl_service_queue service_queue;
struct request_queue *queue;
@@ -111,9 +165,7 @@
unsigned int nr_undestroyed_grps;
/* Work for dispatching throttled bios */
- struct delayed_work throtl_work;
-
- int limits_changed;
+ struct work_struct dispatch_work;
};
/* list and work item to allocate percpu group stats */
@@ -123,6 +175,8 @@
static void tg_stats_alloc_fn(struct work_struct *);
static DECLARE_DELAYED_WORK(tg_stats_alloc_work, tg_stats_alloc_fn);
+static void throtl_pending_timer_fn(unsigned long arg);
+
static inline struct throtl_grp *pd_to_tg(struct blkg_policy_data *pd)
{
return pd ? container_of(pd, struct throtl_grp, pd) : NULL;
@@ -143,41 +197,65 @@
return blkg_to_tg(td->queue->root_blkg);
}
-enum tg_state_flags {
- THROTL_TG_FLAG_on_rr = 0, /* on round-robin busy list */
-};
-
-#define THROTL_TG_FNS(name) \
-static inline void throtl_mark_tg_##name(struct throtl_grp *tg) \
-{ \
- (tg)->flags |= (1 << THROTL_TG_FLAG_##name); \
-} \
-static inline void throtl_clear_tg_##name(struct throtl_grp *tg) \
-{ \
- (tg)->flags &= ~(1 << THROTL_TG_FLAG_##name); \
-} \
-static inline int throtl_tg_##name(const struct throtl_grp *tg) \
-{ \
- return ((tg)->flags & (1 << THROTL_TG_FLAG_##name)) != 0; \
-}
-
-THROTL_TG_FNS(on_rr);
-
-#define throtl_log_tg(td, tg, fmt, args...) do { \
- char __pbuf[128]; \
- \
- blkg_path(tg_to_blkg(tg), __pbuf, sizeof(__pbuf)); \
- blk_add_trace_msg((td)->queue, "throtl %s " fmt, __pbuf, ##args); \
-} while (0)
-
-#define throtl_log(td, fmt, args...) \
- blk_add_trace_msg((td)->queue, "throtl " fmt, ##args)
-
-static inline unsigned int total_nr_queued(struct throtl_data *td)
+/**
+ * sq_to_tg - return the throl_grp the specified service queue belongs to
+ * @sq: the throtl_service_queue of interest
+ *
+ * Return the throtl_grp @sq belongs to. If @sq is the top-level one
+ * embedded in throtl_data, %NULL is returned.
+ */
+static struct throtl_grp *sq_to_tg(struct throtl_service_queue *sq)
{
- return td->nr_queued[0] + td->nr_queued[1];
+ if (sq && sq->parent_sq)
+ return container_of(sq, struct throtl_grp, service_queue);
+ else
+ return NULL;
}
+/**
+ * sq_to_td - return throtl_data the specified service queue belongs to
+ * @sq: the throtl_service_queue of interest
+ *
+ * A service_queue can be embeded in either a throtl_grp or throtl_data.
+ * Determine the associated throtl_data accordingly and return it.
+ */
+static struct throtl_data *sq_to_td(struct throtl_service_queue *sq)
+{
+ struct throtl_grp *tg = sq_to_tg(sq);
+
+ if (tg)
+ return tg->td;
+ else
+ return container_of(sq, struct throtl_data, service_queue);
+}
+
+/**
+ * throtl_log - log debug message via blktrace
+ * @sq: the service_queue being reported
+ * @fmt: printf format string
+ * @args: printf args
+ *
+ * The messages are prefixed with "throtl BLKG_NAME" if @sq belongs to a
+ * throtl_grp; otherwise, just "throtl".
+ *
+ * TODO: this should be made a function and name formatting should happen
+ * after testing whether blktrace is enabled.
+ */
+#define throtl_log(sq, fmt, args...) do { \
+ struct throtl_grp *__tg = sq_to_tg((sq)); \
+ struct throtl_data *__td = sq_to_td((sq)); \
+ \
+ (void)__td; \
+ if ((__tg)) { \
+ char __pbuf[128]; \
+ \
+ blkg_path(tg_to_blkg(__tg), __pbuf, sizeof(__pbuf)); \
+ blk_add_trace_msg(__td->queue, "throtl %s " fmt, __pbuf, ##args); \
+ } else { \
+ blk_add_trace_msg(__td->queue, "throtl " fmt, ##args); \
+ } \
+} while (0)
+
/*
* Worker for allocating per cpu stat for tgs. This is scheduled on the
* system_wq once there are some groups on the alloc_list waiting for
@@ -215,15 +293,141 @@
goto alloc_stats;
}
+static void throtl_qnode_init(struct throtl_qnode *qn, struct throtl_grp *tg)
+{
+ INIT_LIST_HEAD(&qn->node);
+ bio_list_init(&qn->bios);
+ qn->tg = tg;
+}
+
+/**
+ * throtl_qnode_add_bio - add a bio to a throtl_qnode and activate it
+ * @bio: bio being added
+ * @qn: qnode to add bio to
+ * @queued: the service_queue->queued[] list @qn belongs to
+ *
+ * Add @bio to @qn and put @qn on @queued if it's not already on.
+ * @qn->tg's reference count is bumped when @qn is activated. See the
+ * comment on top of throtl_qnode definition for details.
+ */
+static void throtl_qnode_add_bio(struct bio *bio, struct throtl_qnode *qn,
+ struct list_head *queued)
+{
+ bio_list_add(&qn->bios, bio);
+ if (list_empty(&qn->node)) {
+ list_add_tail(&qn->node, queued);
+ blkg_get(tg_to_blkg(qn->tg));
+ }
+}
+
+/**
+ * throtl_peek_queued - peek the first bio on a qnode list
+ * @queued: the qnode list to peek
+ */
+static struct bio *throtl_peek_queued(struct list_head *queued)
+{
+ struct throtl_qnode *qn = list_first_entry(queued, struct throtl_qnode, node);
+ struct bio *bio;
+
+ if (list_empty(queued))
+ return NULL;
+
+ bio = bio_list_peek(&qn->bios);
+ WARN_ON_ONCE(!bio);
+ return bio;
+}
+
+/**
+ * throtl_pop_queued - pop the first bio form a qnode list
+ * @queued: the qnode list to pop a bio from
+ * @tg_to_put: optional out argument for throtl_grp to put
+ *
+ * Pop the first bio from the qnode list @queued. After popping, the first
+ * qnode is removed from @queued if empty or moved to the end of @queued so
+ * that the popping order is round-robin.
+ *
+ * When the first qnode is removed, its associated throtl_grp should be put
+ * too. If @tg_to_put is NULL, this function automatically puts it;
+ * otherwise, *@tg_to_put is set to the throtl_grp to put and the caller is
+ * responsible for putting it.
+ */
+static struct bio *throtl_pop_queued(struct list_head *queued,
+ struct throtl_grp **tg_to_put)
+{
+ struct throtl_qnode *qn = list_first_entry(queued, struct throtl_qnode, node);
+ struct bio *bio;
+
+ if (list_empty(queued))
+ return NULL;
+
+ bio = bio_list_pop(&qn->bios);
+ WARN_ON_ONCE(!bio);
+
+ if (bio_list_empty(&qn->bios)) {
+ list_del_init(&qn->node);
+ if (tg_to_put)
+ *tg_to_put = qn->tg;
+ else
+ blkg_put(tg_to_blkg(qn->tg));
+ } else {
+ list_move_tail(&qn->node, queued);
+ }
+
+ return bio;
+}
+
+/* init a service_queue, assumes the caller zeroed it */
+static void throtl_service_queue_init(struct throtl_service_queue *sq,
+ struct throtl_service_queue *parent_sq)
+{
+ INIT_LIST_HEAD(&sq->queued[0]);
+ INIT_LIST_HEAD(&sq->queued[1]);
+ sq->pending_tree = RB_ROOT;
+ sq->parent_sq = parent_sq;
+ setup_timer(&sq->pending_timer, throtl_pending_timer_fn,
+ (unsigned long)sq);
+}
+
+static void throtl_service_queue_exit(struct throtl_service_queue *sq)
+{
+ del_timer_sync(&sq->pending_timer);
+}
+
static void throtl_pd_init(struct blkcg_gq *blkg)
{
struct throtl_grp *tg = blkg_to_tg(blkg);
+ struct throtl_data *td = blkg->q->td;
+ struct throtl_service_queue *parent_sq;
unsigned long flags;
+ int rw;
+
+ /*
+ * If sane_hierarchy is enabled, we switch to properly hierarchical
+ * behavior where limits on a given throtl_grp are applied to the
+ * whole subtree rather than just the group itself. e.g. If 16M
+ * read_bps limit is set on the root group, the whole system can't
+ * exceed 16M for the device.
+ *
+ * If sane_hierarchy is not enabled, the broken flat hierarchy
+ * behavior is retained where all throtl_grps are treated as if
+ * they're all separate root groups right below throtl_data.
+ * Limits of a group don't interact with limits of other groups
+ * regardless of the position of the group in the hierarchy.
+ */
+ parent_sq = &td->service_queue;
+
+ if (cgroup_sane_behavior(blkg->blkcg->css.cgroup) && blkg->parent)
+ parent_sq = &blkg_to_tg(blkg->parent)->service_queue;
+
+ throtl_service_queue_init(&tg->service_queue, parent_sq);
+
+ for (rw = READ; rw <= WRITE; rw++) {
+ throtl_qnode_init(&tg->qnode_on_self[rw], tg);
+ throtl_qnode_init(&tg->qnode_on_parent[rw], tg);
+ }
RB_CLEAR_NODE(&tg->rb_node);
- bio_list_init(&tg->bio_lists[0]);
- bio_list_init(&tg->bio_lists[1]);
- tg->limits_changed = false;
+ tg->td = td;
tg->bps[READ] = -1;
tg->bps[WRITE] = -1;
@@ -241,6 +445,30 @@
spin_unlock_irqrestore(&tg_stats_alloc_lock, flags);
}
+/*
+ * Set has_rules[] if @tg or any of its parents have limits configured.
+ * This doesn't require walking up to the top of the hierarchy as the
+ * parent's has_rules[] is guaranteed to be correct.
+ */
+static void tg_update_has_rules(struct throtl_grp *tg)
+{
+ struct throtl_grp *parent_tg = sq_to_tg(tg->service_queue.parent_sq);
+ int rw;
+
+ for (rw = READ; rw <= WRITE; rw++)
+ tg->has_rules[rw] = (parent_tg && parent_tg->has_rules[rw]) ||
+ (tg->bps[rw] != -1 || tg->iops[rw] != -1);
+}
+
+static void throtl_pd_online(struct blkcg_gq *blkg)
+{
+ /*
+ * We don't want new groups to escape the limits of its ancestors.
+ * Update has_rules[] after a new group is brought online.
+ */
+ tg_update_has_rules(blkg_to_tg(blkg));
+}
+
static void throtl_pd_exit(struct blkcg_gq *blkg)
{
struct throtl_grp *tg = blkg_to_tg(blkg);
@@ -251,6 +479,8 @@
spin_unlock_irqrestore(&tg_stats_alloc_lock, flags);
free_percpu(tg->stats_cpu);
+
+ throtl_service_queue_exit(&tg->service_queue);
}
static void throtl_pd_reset_stats(struct blkcg_gq *blkg)
@@ -309,17 +539,18 @@
return tg;
}
-static struct throtl_grp *throtl_rb_first(struct throtl_rb_root *root)
+static struct throtl_grp *
+throtl_rb_first(struct throtl_service_queue *parent_sq)
{
/* Service tree is empty */
- if (!root->count)
+ if (!parent_sq->nr_pending)
return NULL;
- if (!root->left)
- root->left = rb_first(&root->rb);
+ if (!parent_sq->first_pending)
+ parent_sq->first_pending = rb_first(&parent_sq->pending_tree);
- if (root->left)
- return rb_entry_tg(root->left);
+ if (parent_sq->first_pending)
+ return rb_entry_tg(parent_sq->first_pending);
return NULL;
}
@@ -330,29 +561,30 @@
RB_CLEAR_NODE(n);
}
-static void throtl_rb_erase(struct rb_node *n, struct throtl_rb_root *root)
+static void throtl_rb_erase(struct rb_node *n,
+ struct throtl_service_queue *parent_sq)
{
- if (root->left == n)
- root->left = NULL;
- rb_erase_init(n, &root->rb);
- --root->count;
+ if (parent_sq->first_pending == n)
+ parent_sq->first_pending = NULL;
+ rb_erase_init(n, &parent_sq->pending_tree);
+ --parent_sq->nr_pending;
}
-static void update_min_dispatch_time(struct throtl_rb_root *st)
+static void update_min_dispatch_time(struct throtl_service_queue *parent_sq)
{
struct throtl_grp *tg;
- tg = throtl_rb_first(st);
+ tg = throtl_rb_first(parent_sq);
if (!tg)
return;
- st->min_disptime = tg->disptime;
+ parent_sq->first_pending_disptime = tg->disptime;
}
-static void
-tg_service_tree_add(struct throtl_rb_root *st, struct throtl_grp *tg)
+static void tg_service_queue_add(struct throtl_grp *tg)
{
- struct rb_node **node = &st->rb.rb_node;
+ struct throtl_service_queue *parent_sq = tg->service_queue.parent_sq;
+ struct rb_node **node = &parent_sq->pending_tree.rb_node;
struct rb_node *parent = NULL;
struct throtl_grp *__tg;
unsigned long key = tg->disptime;
@@ -371,89 +603,135 @@
}
if (left)
- st->left = &tg->rb_node;
+ parent_sq->first_pending = &tg->rb_node;
rb_link_node(&tg->rb_node, parent, node);
- rb_insert_color(&tg->rb_node, &st->rb);
+ rb_insert_color(&tg->rb_node, &parent_sq->pending_tree);
}
-static void __throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg)
+static void __throtl_enqueue_tg(struct throtl_grp *tg)
{
- struct throtl_rb_root *st = &td->tg_service_tree;
-
- tg_service_tree_add(st, tg);
- throtl_mark_tg_on_rr(tg);
- st->count++;
+ tg_service_queue_add(tg);
+ tg->flags |= THROTL_TG_PENDING;
+ tg->service_queue.parent_sq->nr_pending++;
}
-static void throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg)
+static void throtl_enqueue_tg(struct throtl_grp *tg)
{
- if (!throtl_tg_on_rr(tg))
- __throtl_enqueue_tg(td, tg);
+ if (!(tg->flags & THROTL_TG_PENDING))
+ __throtl_enqueue_tg(tg);
}
-static void __throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg)
+static void __throtl_dequeue_tg(struct throtl_grp *tg)
{
- throtl_rb_erase(&tg->rb_node, &td->tg_service_tree);
- throtl_clear_tg_on_rr(tg);
+ throtl_rb_erase(&tg->rb_node, tg->service_queue.parent_sq);
+ tg->flags &= ~THROTL_TG_PENDING;
}
-static void throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg)
+static void throtl_dequeue_tg(struct throtl_grp *tg)
{
- if (throtl_tg_on_rr(tg))
- __throtl_dequeue_tg(td, tg);
+ if (tg->flags & THROTL_TG_PENDING)
+ __throtl_dequeue_tg(tg);
}
-static void throtl_schedule_next_dispatch(struct throtl_data *td)
+/* Call with queue lock held */
+static void throtl_schedule_pending_timer(struct throtl_service_queue *sq,
+ unsigned long expires)
{
- struct throtl_rb_root *st = &td->tg_service_tree;
+ mod_timer(&sq->pending_timer, expires);
+ throtl_log(sq, "schedule timer. delay=%lu jiffies=%lu",
+ expires - jiffies, jiffies);
+}
+
+/**
+ * throtl_schedule_next_dispatch - schedule the next dispatch cycle
+ * @sq: the service_queue to schedule dispatch for
+ * @force: force scheduling
+ *
+ * Arm @sq->pending_timer so that the next dispatch cycle starts on the
+ * dispatch time of the first pending child. Returns %true if either timer
+ * is armed or there's no pending child left. %false if the current
+ * dispatch window is still open and the caller should continue
+ * dispatching.
+ *
+ * If @force is %true, the dispatch timer is always scheduled and this
+ * function is guaranteed to return %true. This is to be used when the
+ * caller can't dispatch itself and needs to invoke pending_timer
+ * unconditionally. Note that forced scheduling is likely to induce short
+ * delay before dispatch starts even if @sq->first_pending_disptime is not
+ * in the future and thus shouldn't be used in hot paths.
+ */
+static bool throtl_schedule_next_dispatch(struct throtl_service_queue *sq,
+ bool force)
+{
+ /* any pending children left? */
+ if (!sq->nr_pending)
+ return true;
+
+ update_min_dispatch_time(sq);
+
+ /* is the next dispatch time in the future? */
+ if (force || time_after(sq->first_pending_disptime, jiffies)) {
+ throtl_schedule_pending_timer(sq, sq->first_pending_disptime);
+ return true;
+ }
+
+ /* tell the caller to continue dispatching */
+ return false;
+}
+
+static inline void throtl_start_new_slice_with_credit(struct throtl_grp *tg,
+ bool rw, unsigned long start)
+{
+ tg->bytes_disp[rw] = 0;
+ tg->io_disp[rw] = 0;
/*
- * If there are more bios pending, schedule more work.
+ * Previous slice has expired. We must have trimmed it after last
+ * bio dispatch. That means since start of last slice, we never used
+ * that bandwidth. Do try to make use of that bandwidth while giving
+ * credit.
*/
- if (!total_nr_queued(td))
- return;
+ if (time_after_eq(start, tg->slice_start[rw]))
+ tg->slice_start[rw] = start;
- BUG_ON(!st->count);
-
- update_min_dispatch_time(st);
-
- if (time_before_eq(st->min_disptime, jiffies))
- throtl_schedule_delayed_work(td, 0);
- else
- throtl_schedule_delayed_work(td, (st->min_disptime - jiffies));
+ tg->slice_end[rw] = jiffies + throtl_slice;
+ throtl_log(&tg->service_queue,
+ "[%c] new slice with credit start=%lu end=%lu jiffies=%lu",
+ rw == READ ? 'R' : 'W', tg->slice_start[rw],
+ tg->slice_end[rw], jiffies);
}
-static inline void
-throtl_start_new_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw)
{
tg->bytes_disp[rw] = 0;
tg->io_disp[rw] = 0;
tg->slice_start[rw] = jiffies;
tg->slice_end[rw] = jiffies + throtl_slice;
- throtl_log_tg(td, tg, "[%c] new slice start=%lu end=%lu jiffies=%lu",
- rw == READ ? 'R' : 'W', tg->slice_start[rw],
- tg->slice_end[rw], jiffies);
+ throtl_log(&tg->service_queue,
+ "[%c] new slice start=%lu end=%lu jiffies=%lu",
+ rw == READ ? 'R' : 'W', tg->slice_start[rw],
+ tg->slice_end[rw], jiffies);
}
-static inline void throtl_set_slice_end(struct throtl_data *td,
- struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
+static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw,
+ unsigned long jiffy_end)
{
tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
}
-static inline void throtl_extend_slice(struct throtl_data *td,
- struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
+static inline void throtl_extend_slice(struct throtl_grp *tg, bool rw,
+ unsigned long jiffy_end)
{
tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
- throtl_log_tg(td, tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu",
- rw == READ ? 'R' : 'W', tg->slice_start[rw],
- tg->slice_end[rw], jiffies);
+ throtl_log(&tg->service_queue,
+ "[%c] extend slice start=%lu end=%lu jiffies=%lu",
+ rw == READ ? 'R' : 'W', tg->slice_start[rw],
+ tg->slice_end[rw], jiffies);
}
/* Determine if previously allocated or extended slice is complete or not */
-static bool
-throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static bool throtl_slice_used(struct throtl_grp *tg, bool rw)
{
if (time_in_range(jiffies, tg->slice_start[rw], tg->slice_end[rw]))
return 0;
@@ -462,8 +740,7 @@
}
/* Trim the used slices and adjust slice start accordingly */
-static inline void
-throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw)
{
unsigned long nr_slices, time_elapsed, io_trim;
u64 bytes_trim, tmp;
@@ -475,7 +752,7 @@
* renewed. Don't try to trim the slice if slice is used. A new
* slice will start when appropriate.
*/
- if (throtl_slice_used(td, tg, rw))
+ if (throtl_slice_used(tg, rw))
return;
/*
@@ -486,7 +763,7 @@
* is bad because it does not allow new slice to start.
*/
- throtl_set_slice_end(td, tg, rw, jiffies + throtl_slice);
+ throtl_set_slice_end(tg, rw, jiffies + throtl_slice);
time_elapsed = jiffies - tg->slice_start[rw];
@@ -515,14 +792,14 @@
tg->slice_start[rw] += nr_slices * throtl_slice;
- throtl_log_tg(td, tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu"
- " start=%lu end=%lu jiffies=%lu",
- rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim,
- tg->slice_start[rw], tg->slice_end[rw], jiffies);
+ throtl_log(&tg->service_queue,
+ "[%c] trim slice nr=%lu bytes=%llu io=%lu start=%lu end=%lu jiffies=%lu",
+ rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim,
+ tg->slice_start[rw], tg->slice_end[rw], jiffies);
}
-static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
unsigned int io_allowed;
@@ -571,8 +848,8 @@
return 0;
}
-static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
u64 bytes_allowed, extra_bytes, tmp;
@@ -613,18 +890,12 @@
return 0;
}
-static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
- if (tg->bps[rw] == -1 && tg->iops[rw] == -1)
- return 1;
- return 0;
-}
-
/*
* Returns whether one can dispatch a bio or not. Also returns approx number
* of jiffies to wait before this bio is with-in IO rate and can be dispatched
*/
-static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
@@ -635,7 +906,8 @@
* this function with a different bio if there are other bios
* queued.
*/
- BUG_ON(tg->nr_queued[rw] && bio != bio_list_peek(&tg->bio_lists[rw]));
+ BUG_ON(tg->service_queue.nr_queued[rw] &&
+ bio != throtl_peek_queued(&tg->service_queue.queued[rw]));
/* If tg->bps = -1, then BW is unlimited */
if (tg->bps[rw] == -1 && tg->iops[rw] == -1) {
@@ -649,15 +921,15 @@
* existing slice to make sure it is at least throtl_slice interval
* long since now.
*/
- if (throtl_slice_used(td, tg, rw))
- throtl_start_new_slice(td, tg, rw);
+ if (throtl_slice_used(tg, rw))
+ throtl_start_new_slice(tg, rw);
else {
if (time_before(tg->slice_end[rw], jiffies + throtl_slice))
- throtl_extend_slice(td, tg, rw, jiffies + throtl_slice);
+ throtl_extend_slice(tg, rw, jiffies + throtl_slice);
}
- if (tg_with_in_bps_limit(td, tg, bio, &bps_wait)
- && tg_with_in_iops_limit(td, tg, bio, &iops_wait)) {
+ if (tg_with_in_bps_limit(tg, bio, &bps_wait) &&
+ tg_with_in_iops_limit(tg, bio, &iops_wait)) {
if (wait)
*wait = 0;
return 1;
@@ -669,7 +941,7 @@
*wait = max_wait;
if (time_before(tg->slice_end[rw], jiffies + max_wait))
- throtl_extend_slice(td, tg, rw, jiffies + max_wait);
+ throtl_extend_slice(tg, rw, jiffies + max_wait);
return 0;
}
@@ -708,65 +980,136 @@
tg->bytes_disp[rw] += bio->bi_size;
tg->io_disp[rw]++;
- throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw);
+ /*
+ * REQ_THROTTLED is used to prevent the same bio to be throttled
+ * more than once as a throttled bio will go through blk-throtl the
+ * second time when it eventually gets issued. Set it when a bio
+ * is being charged to a tg.
+ *
+ * Dispatch stats aren't recursive and each @bio should only be
+ * accounted by the @tg it was originally associated with. Let's
+ * update the stats when setting REQ_THROTTLED for the first time
+ * which is guaranteed to be for the @bio's original tg.
+ */
+ if (!(bio->bi_rw & REQ_THROTTLED)) {
+ bio->bi_rw |= REQ_THROTTLED;
+ throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size,
+ bio->bi_rw);
+ }
}
-static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio)
+/**
+ * throtl_add_bio_tg - add a bio to the specified throtl_grp
+ * @bio: bio to add
+ * @qn: qnode to use
+ * @tg: the target throtl_grp
+ *
+ * Add @bio to @tg's service_queue using @qn. If @qn is not specified,
+ * tg->qnode_on_self[] is used.
+ */
+static void throtl_add_bio_tg(struct bio *bio, struct throtl_qnode *qn,
+ struct throtl_grp *tg)
{
+ struct throtl_service_queue *sq = &tg->service_queue;
bool rw = bio_data_dir(bio);
- bio_list_add(&tg->bio_lists[rw], bio);
- /* Take a bio reference on tg */
- blkg_get(tg_to_blkg(tg));
- tg->nr_queued[rw]++;
- td->nr_queued[rw]++;
- throtl_enqueue_tg(td, tg);
+ if (!qn)
+ qn = &tg->qnode_on_self[rw];
+
+ /*
+ * If @tg doesn't currently have any bios queued in the same
+ * direction, queueing @bio can change when @tg should be
+ * dispatched. Mark that @tg was empty. This is automatically
+ * cleaered on the next tg_update_disptime().
+ */
+ if (!sq->nr_queued[rw])
+ tg->flags |= THROTL_TG_WAS_EMPTY;
+
+ throtl_qnode_add_bio(bio, qn, &sq->queued[rw]);
+
+ sq->nr_queued[rw]++;
+ throtl_enqueue_tg(tg);
}
-static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
+static void tg_update_disptime(struct throtl_grp *tg)
{
+ struct throtl_service_queue *sq = &tg->service_queue;
unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime;
struct bio *bio;
- if ((bio = bio_list_peek(&tg->bio_lists[READ])))
- tg_may_dispatch(td, tg, bio, &read_wait);
+ if ((bio = throtl_peek_queued(&sq->queued[READ])))
+ tg_may_dispatch(tg, bio, &read_wait);
- if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
- tg_may_dispatch(td, tg, bio, &write_wait);
+ if ((bio = throtl_peek_queued(&sq->queued[WRITE])))
+ tg_may_dispatch(tg, bio, &write_wait);
min_wait = min(read_wait, write_wait);
disptime = jiffies + min_wait;
/* Update dispatch time */
- throtl_dequeue_tg(td, tg);
+ throtl_dequeue_tg(tg);
tg->disptime = disptime;
- throtl_enqueue_tg(td, tg);
+ throtl_enqueue_tg(tg);
+
+ /* see throtl_add_bio_tg() */
+ tg->flags &= ~THROTL_TG_WAS_EMPTY;
}
-static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
- bool rw, struct bio_list *bl)
+static void start_parent_slice_with_credit(struct throtl_grp *child_tg,
+ struct throtl_grp *parent_tg, bool rw)
{
+ if (throtl_slice_used(parent_tg, rw)) {
+ throtl_start_new_slice_with_credit(parent_tg, rw,
+ child_tg->slice_start[rw]);
+ }
+
+}
+
+static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw)
+{
+ struct throtl_service_queue *sq = &tg->service_queue;
+ struct throtl_service_queue *parent_sq = sq->parent_sq;
+ struct throtl_grp *parent_tg = sq_to_tg(parent_sq);
+ struct throtl_grp *tg_to_put = NULL;
struct bio *bio;
- bio = bio_list_pop(&tg->bio_lists[rw]);
- tg->nr_queued[rw]--;
- /* Drop bio reference on blkg */
- blkg_put(tg_to_blkg(tg));
-
- BUG_ON(td->nr_queued[rw] <= 0);
- td->nr_queued[rw]--;
+ /*
+ * @bio is being transferred from @tg to @parent_sq. Popping a bio
+ * from @tg may put its reference and @parent_sq might end up
+ * getting released prematurely. Remember the tg to put and put it
+ * after @bio is transferred to @parent_sq.
+ */
+ bio = throtl_pop_queued(&sq->queued[rw], &tg_to_put);
+ sq->nr_queued[rw]--;
throtl_charge_bio(tg, bio);
- bio_list_add(bl, bio);
- bio->bi_rw |= REQ_THROTTLED;
- throtl_trim_slice(td, tg, rw);
+ /*
+ * If our parent is another tg, we just need to transfer @bio to
+ * the parent using throtl_add_bio_tg(). If our parent is
+ * @td->service_queue, @bio is ready to be issued. Put it on its
+ * bio_lists[] and decrease total number queued. The caller is
+ * responsible for issuing these bios.
+ */
+ if (parent_tg) {
+ throtl_add_bio_tg(bio, &tg->qnode_on_parent[rw], parent_tg);
+ start_parent_slice_with_credit(tg, parent_tg, rw);
+ } else {
+ throtl_qnode_add_bio(bio, &tg->qnode_on_parent[rw],
+ &parent_sq->queued[rw]);
+ BUG_ON(tg->td->nr_queued[rw] <= 0);
+ tg->td->nr_queued[rw]--;
+ }
+
+ throtl_trim_slice(tg, rw);
+
+ if (tg_to_put)
+ blkg_put(tg_to_blkg(tg_to_put));
}
-static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
- struct bio_list *bl)
+static int throtl_dispatch_tg(struct throtl_grp *tg)
{
+ struct throtl_service_queue *sq = &tg->service_queue;
unsigned int nr_reads = 0, nr_writes = 0;
unsigned int max_nr_reads = throtl_grp_quantum*3/4;
unsigned int max_nr_writes = throtl_grp_quantum - max_nr_reads;
@@ -774,20 +1117,20 @@
/* Try to dispatch 75% READS and 25% WRITES */
- while ((bio = bio_list_peek(&tg->bio_lists[READ]))
- && tg_may_dispatch(td, tg, bio, NULL)) {
+ while ((bio = throtl_peek_queued(&sq->queued[READ])) &&
+ tg_may_dispatch(tg, bio, NULL)) {
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio));
nr_reads++;
if (nr_reads >= max_nr_reads)
break;
}
- while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
- && tg_may_dispatch(td, tg, bio, NULL)) {
+ while ((bio = throtl_peek_queued(&sq->queued[WRITE])) &&
+ tg_may_dispatch(tg, bio, NULL)) {
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio));
nr_writes++;
if (nr_writes >= max_nr_writes)
@@ -797,14 +1140,13 @@
return nr_reads + nr_writes;
}
-static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl)
+static int throtl_select_dispatch(struct throtl_service_queue *parent_sq)
{
unsigned int nr_disp = 0;
- struct throtl_grp *tg;
- struct throtl_rb_root *st = &td->tg_service_tree;
while (1) {
- tg = throtl_rb_first(st);
+ struct throtl_grp *tg = throtl_rb_first(parent_sq);
+ struct throtl_service_queue *sq = &tg->service_queue;
if (!tg)
break;
@@ -812,14 +1154,12 @@
if (time_before(jiffies, tg->disptime))
break;
- throtl_dequeue_tg(td, tg);
+ throtl_dequeue_tg(tg);
- nr_disp += throtl_dispatch_tg(td, tg, bl);
+ nr_disp += throtl_dispatch_tg(tg);
- if (tg->nr_queued[0] || tg->nr_queued[1]) {
- tg_update_disptime(td, tg);
- throtl_enqueue_tg(td, tg);
- }
+ if (sq->nr_queued[0] || sq->nr_queued[1])
+ tg_update_disptime(tg);
if (nr_disp >= throtl_quantum)
break;
@@ -828,111 +1168,111 @@
return nr_disp;
}
-static void throtl_process_limit_change(struct throtl_data *td)
+/**
+ * throtl_pending_timer_fn - timer function for service_queue->pending_timer
+ * @arg: the throtl_service_queue being serviced
+ *
+ * This timer is armed when a child throtl_grp with active bio's become
+ * pending and queued on the service_queue's pending_tree and expires when
+ * the first child throtl_grp should be dispatched. This function
+ * dispatches bio's from the children throtl_grps to the parent
+ * service_queue.
+ *
+ * If the parent's parent is another throtl_grp, dispatching is propagated
+ * by either arming its pending_timer or repeating dispatch directly. If
+ * the top-level service_tree is reached, throtl_data->dispatch_work is
+ * kicked so that the ready bio's are issued.
+ */
+static void throtl_pending_timer_fn(unsigned long arg)
{
+ struct throtl_service_queue *sq = (void *)arg;
+ struct throtl_grp *tg = sq_to_tg(sq);
+ struct throtl_data *td = sq_to_td(sq);
struct request_queue *q = td->queue;
- struct blkcg_gq *blkg, *n;
+ struct throtl_service_queue *parent_sq;
+ bool dispatched;
+ int ret;
- if (!td->limits_changed)
- return;
+ spin_lock_irq(q->queue_lock);
+again:
+ parent_sq = sq->parent_sq;
+ dispatched = false;
- xchg(&td->limits_changed, false);
+ while (true) {
+ throtl_log(sq, "dispatch nr_queued=%u read=%u write=%u",
+ sq->nr_queued[READ] + sq->nr_queued[WRITE],
+ sq->nr_queued[READ], sq->nr_queued[WRITE]);
- throtl_log(td, "limits changed");
+ ret = throtl_select_dispatch(sq);
+ if (ret) {
+ throtl_log(sq, "bios disp=%u", ret);
+ dispatched = true;
+ }
- list_for_each_entry_safe(blkg, n, &q->blkg_list, q_node) {
- struct throtl_grp *tg = blkg_to_tg(blkg);
+ if (throtl_schedule_next_dispatch(sq, false))
+ break;
- if (!tg->limits_changed)
- continue;
-
- if (!xchg(&tg->limits_changed, false))
- continue;
-
- throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu"
- " riops=%u wiops=%u", tg->bps[READ], tg->bps[WRITE],
- tg->iops[READ], tg->iops[WRITE]);
-
- /*
- * Restart the slices for both READ and WRITES. It
- * might happen that a group's limit are dropped
- * suddenly and we don't want to account recently
- * dispatched IO with new low rate
- */
- throtl_start_new_slice(td, tg, 0);
- throtl_start_new_slice(td, tg, 1);
-
- if (throtl_tg_on_rr(tg))
- tg_update_disptime(td, tg);
+ /* this dispatch windows is still open, relax and repeat */
+ spin_unlock_irq(q->queue_lock);
+ cpu_relax();
+ spin_lock_irq(q->queue_lock);
}
+
+ if (!dispatched)
+ goto out_unlock;
+
+ if (parent_sq) {
+ /* @parent_sq is another throl_grp, propagate dispatch */
+ if (tg->flags & THROTL_TG_WAS_EMPTY) {
+ tg_update_disptime(tg);
+ if (!throtl_schedule_next_dispatch(parent_sq, false)) {
+ /* window is already open, repeat dispatching */
+ sq = parent_sq;
+ tg = sq_to_tg(sq);
+ goto again;
+ }
+ }
+ } else {
+ /* reached the top-level, queue issueing */
+ queue_work(kthrotld_workqueue, &td->dispatch_work);
+ }
+out_unlock:
+ spin_unlock_irq(q->queue_lock);
}
-/* Dispatch throttled bios. Should be called without queue lock held. */
-static int throtl_dispatch(struct request_queue *q)
+/**
+ * blk_throtl_dispatch_work_fn - work function for throtl_data->dispatch_work
+ * @work: work item being executed
+ *
+ * This function is queued for execution when bio's reach the bio_lists[]
+ * of throtl_data->service_queue. Those bio's are ready and issued by this
+ * function.
+ */
+void blk_throtl_dispatch_work_fn(struct work_struct *work)
{
- struct throtl_data *td = q->td;
- unsigned int nr_disp = 0;
+ struct throtl_data *td = container_of(work, struct throtl_data,
+ dispatch_work);
+ struct throtl_service_queue *td_sq = &td->service_queue;
+ struct request_queue *q = td->queue;
struct bio_list bio_list_on_stack;
struct bio *bio;
struct blk_plug plug;
-
- spin_lock_irq(q->queue_lock);
-
- throtl_process_limit_change(td);
-
- if (!total_nr_queued(td))
- goto out;
+ int rw;
bio_list_init(&bio_list_on_stack);
- throtl_log(td, "dispatch nr_queued=%u read=%u write=%u",
- total_nr_queued(td), td->nr_queued[READ],
- td->nr_queued[WRITE]);
-
- nr_disp = throtl_select_dispatch(td, &bio_list_on_stack);
-
- if (nr_disp)
- throtl_log(td, "bios disp=%u", nr_disp);
-
- throtl_schedule_next_dispatch(td);
-out:
+ spin_lock_irq(q->queue_lock);
+ for (rw = READ; rw <= WRITE; rw++)
+ while ((bio = throtl_pop_queued(&td_sq->queued[rw], NULL)))
+ bio_list_add(&bio_list_on_stack, bio);
spin_unlock_irq(q->queue_lock);
- /*
- * If we dispatched some requests, unplug the queue to make sure
- * immediate dispatch
- */
- if (nr_disp) {
+ if (!bio_list_empty(&bio_list_on_stack)) {
blk_start_plug(&plug);
while((bio = bio_list_pop(&bio_list_on_stack)))
generic_make_request(bio);
blk_finish_plug(&plug);
}
- return nr_disp;
-}
-
-void blk_throtl_work(struct work_struct *work)
-{
- struct throtl_data *td = container_of(work, struct throtl_data,
- throtl_work.work);
- struct request_queue *q = td->queue;
-
- throtl_dispatch(q);
-}
-
-/* Call with queue lock held */
-static void
-throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay)
-{
-
- struct delayed_work *dwork = &td->throtl_work;
-
- /* schedule work if limits changed even if no bio is queued */
- if (total_nr_queued(td) || td->limits_changed) {
- mod_delayed_work(kthrotld_workqueue, dwork, delay);
- throtl_log(td, "schedule work. delay=%lu jiffies=%lu",
- delay, jiffies);
- }
}
static u64 tg_prfill_cpu_rwstat(struct seq_file *sf,
@@ -1007,7 +1347,9 @@
struct blkcg *blkcg = cgroup_to_blkcg(cgrp);
struct blkg_conf_ctx ctx;
struct throtl_grp *tg;
- struct throtl_data *td;
+ struct throtl_service_queue *sq;
+ struct blkcg_gq *blkg;
+ struct cgroup *pos_cgrp;
int ret;
ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx);
@@ -1015,7 +1357,7 @@
return ret;
tg = blkg_to_tg(ctx.blkg);
- td = ctx.blkg->q->td;
+ sq = &tg->service_queue;
if (!ctx.v)
ctx.v = -1;
@@ -1025,10 +1367,37 @@
else
*(unsigned int *)((void *)tg + cft->private) = ctx.v;
- /* XXX: we don't need the following deferred processing */
- xchg(&tg->limits_changed, true);
- xchg(&td->limits_changed, true);
- throtl_schedule_delayed_work(td, 0);
+ throtl_log(&tg->service_queue,
+ "limit change rbps=%llu wbps=%llu riops=%u wiops=%u",
+ tg->bps[READ], tg->bps[WRITE],
+ tg->iops[READ], tg->iops[WRITE]);
+
+ /*
+ * Update has_rules[] flags for the updated tg's subtree. A tg is
+ * considered to have rules if either the tg itself or any of its
+ * ancestors has rules. This identifies groups without any
+ * restrictions in the whole hierarchy and allows them to bypass
+ * blk-throttle.
+ */
+ tg_update_has_rules(tg);
+ blkg_for_each_descendant_pre(blkg, pos_cgrp, ctx.blkg)
+ tg_update_has_rules(blkg_to_tg(blkg));
+
+ /*
+ * We're already holding queue_lock and know @tg is valid. Let's
+ * apply the new config directly.
+ *
+ * Restart the slices for both READ and WRITES. It might happen
+ * that a group's limit are dropped suddenly and we don't want to
+ * account recently dispatched IO with new low rate.
+ */
+ throtl_start_new_slice(tg, 0);
+ throtl_start_new_slice(tg, 1);
+
+ if (tg->flags & THROTL_TG_PENDING) {
+ tg_update_disptime(tg);
+ throtl_schedule_next_dispatch(sq->parent_sq, true);
+ }
blkg_conf_finish(&ctx);
return 0;
@@ -1092,7 +1461,7 @@
{
struct throtl_data *td = q->td;
- cancel_delayed_work_sync(&td->throtl_work);
+ cancel_work_sync(&td->dispatch_work);
}
static struct blkcg_policy blkcg_policy_throtl = {
@@ -1100,6 +1469,7 @@
.cftypes = throtl_files,
.pd_init_fn = throtl_pd_init,
+ .pd_online_fn = throtl_pd_online,
.pd_exit_fn = throtl_pd_exit,
.pd_reset_stats_fn = throtl_pd_reset_stats,
};
@@ -1107,15 +1477,16 @@
bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
{
struct throtl_data *td = q->td;
+ struct throtl_qnode *qn = NULL;
struct throtl_grp *tg;
- bool rw = bio_data_dir(bio), update_disptime = true;
+ struct throtl_service_queue *sq;
+ bool rw = bio_data_dir(bio);
struct blkcg *blkcg;
bool throttled = false;
- if (bio->bi_rw & REQ_THROTTLED) {
- bio->bi_rw &= ~REQ_THROTTLED;
+ /* see throtl_charge_bio() */
+ if (bio->bi_rw & REQ_THROTTLED)
goto out;
- }
/*
* A throtl_grp pointer retrieved under rcu can be used to access
@@ -1126,7 +1497,7 @@
blkcg = bio_blkcg(bio);
tg = throtl_lookup_tg(td, blkcg);
if (tg) {
- if (tg_no_rule_group(tg, rw)) {
+ if (!tg->has_rules[rw]) {
throtl_update_dispatch_stats(tg_to_blkg(tg),
bio->bi_size, bio->bi_rw);
goto out_unlock_rcu;
@@ -1142,18 +1513,18 @@
if (unlikely(!tg))
goto out_unlock;
- if (tg->nr_queued[rw]) {
- /*
- * There is already another bio queued in same dir. No
- * need to update dispatch time.
- */
- update_disptime = false;
- goto queue_bio;
+ sq = &tg->service_queue;
- }
+ while (true) {
+ /* throtl is FIFO - if bios are already queued, should queue */
+ if (sq->nr_queued[rw])
+ break;
- /* Bio is with-in rate limit of group */
- if (tg_may_dispatch(td, tg, bio, NULL)) {
+ /* if above limits, break to queue */
+ if (!tg_may_dispatch(tg, bio, NULL))
+ break;
+
+ /* within limits, let's charge and dispatch directly */
throtl_charge_bio(tg, bio);
/*
@@ -1167,25 +1538,41 @@
*
* So keep on trimming slice even if bio is not queued.
*/
- throtl_trim_slice(td, tg, rw);
- goto out_unlock;
+ throtl_trim_slice(tg, rw);
+
+ /*
+ * @bio passed through this layer without being throttled.
+ * Climb up the ladder. If we''re already at the top, it
+ * can be executed directly.
+ */
+ qn = &tg->qnode_on_parent[rw];
+ sq = sq->parent_sq;
+ tg = sq_to_tg(sq);
+ if (!tg)
+ goto out_unlock;
}
-queue_bio:
- throtl_log_tg(td, tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu"
- " iodisp=%u iops=%u queued=%d/%d",
- rw == READ ? 'R' : 'W',
- tg->bytes_disp[rw], bio->bi_size, tg->bps[rw],
- tg->io_disp[rw], tg->iops[rw],
- tg->nr_queued[READ], tg->nr_queued[WRITE]);
+ /* out-of-limit, queue to @tg */
+ throtl_log(sq, "[%c] bio. bdisp=%llu sz=%u bps=%llu iodisp=%u iops=%u queued=%d/%d",
+ rw == READ ? 'R' : 'W',
+ tg->bytes_disp[rw], bio->bi_size, tg->bps[rw],
+ tg->io_disp[rw], tg->iops[rw],
+ sq->nr_queued[READ], sq->nr_queued[WRITE]);
bio_associate_current(bio);
- throtl_add_bio_tg(q->td, tg, bio);
+ tg->td->nr_queued[rw]++;
+ throtl_add_bio_tg(bio, qn, tg);
throttled = true;
- if (update_disptime) {
- tg_update_disptime(td, tg);
- throtl_schedule_next_dispatch(td);
+ /*
+ * Update @tg's dispatch time and force schedule dispatch if @tg
+ * was empty before @bio. The forced scheduling isn't likely to
+ * cause undue delay as @bio is likely to be dispatched directly if
+ * its @tg's disptime is not in the future.
+ */
+ if (tg->flags & THROTL_TG_WAS_EMPTY) {
+ tg_update_disptime(tg);
+ throtl_schedule_next_dispatch(tg->service_queue.parent_sq, true);
}
out_unlock:
@@ -1193,9 +1580,38 @@
out_unlock_rcu:
rcu_read_unlock();
out:
+ /*
+ * As multiple blk-throtls may stack in the same issue path, we
+ * don't want bios to leave with the flag set. Clear the flag if
+ * being issued.
+ */
+ if (!throttled)
+ bio->bi_rw &= ~REQ_THROTTLED;
return throttled;
}
+/*
+ * Dispatch all bios from all children tg's queued on @parent_sq. On
+ * return, @parent_sq is guaranteed to not have any active children tg's
+ * and all bios from previously active tg's are on @parent_sq->bio_lists[].
+ */
+static void tg_drain_bios(struct throtl_service_queue *parent_sq)
+{
+ struct throtl_grp *tg;
+
+ while ((tg = throtl_rb_first(parent_sq))) {
+ struct throtl_service_queue *sq = &tg->service_queue;
+ struct bio *bio;
+
+ throtl_dequeue_tg(tg);
+
+ while ((bio = throtl_peek_queued(&sq->queued[READ])))
+ tg_dispatch_one_bio(tg, bio_data_dir(bio));
+ while ((bio = throtl_peek_queued(&sq->queued[WRITE])))
+ tg_dispatch_one_bio(tg, bio_data_dir(bio));
+ }
+}
+
/**
* blk_throtl_drain - drain throttled bios
* @q: request_queue to drain throttled bios for
@@ -1206,27 +1622,36 @@
__releases(q->queue_lock) __acquires(q->queue_lock)
{
struct throtl_data *td = q->td;
- struct throtl_rb_root *st = &td->tg_service_tree;
- struct throtl_grp *tg;
- struct bio_list bl;
+ struct blkcg_gq *blkg;
+ struct cgroup *pos_cgrp;
struct bio *bio;
+ int rw;
queue_lockdep_assert_held(q);
+ rcu_read_lock();
- bio_list_init(&bl);
+ /*
+ * Drain each tg while doing post-order walk on the blkg tree, so
+ * that all bios are propagated to td->service_queue. It'd be
+ * better to walk service_queue tree directly but blkg walk is
+ * easier.
+ */
+ blkg_for_each_descendant_post(blkg, pos_cgrp, td->queue->root_blkg)
+ tg_drain_bios(&blkg_to_tg(blkg)->service_queue);
- while ((tg = throtl_rb_first(st))) {
- throtl_dequeue_tg(td, tg);
+ tg_drain_bios(&td_root_tg(td)->service_queue);
- while ((bio = bio_list_peek(&tg->bio_lists[READ])))
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl);
- while ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl);
- }
+ /* finally, transfer bios from top-level tg's into the td */
+ tg_drain_bios(&td->service_queue);
+
+ rcu_read_unlock();
spin_unlock_irq(q->queue_lock);
- while ((bio = bio_list_pop(&bl)))
- generic_make_request(bio);
+ /* all bios now should be in td->service_queue, issue them */
+ for (rw = READ; rw <= WRITE; rw++)
+ while ((bio = throtl_pop_queued(&td->service_queue.queued[rw],
+ NULL)))
+ generic_make_request(bio);
spin_lock_irq(q->queue_lock);
}
@@ -1240,9 +1665,8 @@
if (!td)
return -ENOMEM;
- td->tg_service_tree = THROTL_RB_ROOT;
- td->limits_changed = false;
- INIT_DELAYED_WORK(&td->throtl_work, blk_throtl_work);
+ INIT_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn);
+ throtl_service_queue_init(&td->service_queue, NULL);
q->td = td;
td->queue = q;
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index d5cd3131..d5bbdcf 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -4347,18 +4347,28 @@
kfree(cfqd);
}
-static int cfq_init_queue(struct request_queue *q)
+static int cfq_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct cfq_data *cfqd;
struct blkcg_gq *blkg __maybe_unused;
int i, ret;
+ struct elevator_queue *eq;
- cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node);
- if (!cfqd)
+ eq = elevator_alloc(q, e);
+ if (!eq)
return -ENOMEM;
+ cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node);
+ if (!cfqd) {
+ kobject_put(&eq->kobj);
+ return -ENOMEM;
+ }
+ eq->elevator_data = cfqd;
+
cfqd->queue = q;
- q->elevator->elevator_data = cfqd;
+ spin_lock_irq(q->queue_lock);
+ q->elevator = eq;
+ spin_unlock_irq(q->queue_lock);
/* Init root service tree */
cfqd->grp_service_tree = CFQ_RB_ROOT;
@@ -4433,6 +4443,7 @@
out_free:
kfree(cfqd);
+ kobject_put(&eq->kobj);
return ret;
}
diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c
index ba19a3a..20614a3 100644
--- a/block/deadline-iosched.c
+++ b/block/deadline-iosched.c
@@ -337,13 +337,21 @@
/*
* initialize elevator private data (deadline_data).
*/
-static int deadline_init_queue(struct request_queue *q)
+static int deadline_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct deadline_data *dd;
+ struct elevator_queue *eq;
+
+ eq = elevator_alloc(q, e);
+ if (!eq)
+ return -ENOMEM;
dd = kmalloc_node(sizeof(*dd), GFP_KERNEL | __GFP_ZERO, q->node);
- if (!dd)
+ if (!dd) {
+ kobject_put(&eq->kobj);
return -ENOMEM;
+ }
+ eq->elevator_data = dd;
INIT_LIST_HEAD(&dd->fifo_list[READ]);
INIT_LIST_HEAD(&dd->fifo_list[WRITE]);
@@ -355,7 +363,9 @@
dd->front_merges = 1;
dd->fifo_batch = fifo_batch;
- q->elevator->elevator_data = dd;
+ spin_lock_irq(q->queue_lock);
+ q->elevator = eq;
+ spin_unlock_irq(q->queue_lock);
return 0;
}
diff --git a/block/elevator.c b/block/elevator.c
index eba5b04..668394d 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -150,7 +150,7 @@
static struct kobj_type elv_ktype;
-static struct elevator_queue *elevator_alloc(struct request_queue *q,
+struct elevator_queue *elevator_alloc(struct request_queue *q,
struct elevator_type *e)
{
struct elevator_queue *eq;
@@ -170,6 +170,7 @@
elevator_put(e);
return NULL;
}
+EXPORT_SYMBOL(elevator_alloc);
static void elevator_release(struct kobject *kobj)
{
@@ -221,16 +222,7 @@
}
}
- q->elevator = elevator_alloc(q, e);
- if (!q->elevator)
- return -ENOMEM;
-
- err = e->ops.elevator_init_fn(q);
- if (err) {
- kobject_put(&q->elevator->kobj);
- return err;
- }
-
+ err = e->ops.elevator_init_fn(q, e);
return 0;
}
EXPORT_SYMBOL(elevator_init);
@@ -935,17 +927,10 @@
spin_unlock_irq(q->queue_lock);
/* allocate, init and register new elevator */
- err = -ENOMEM;
- q->elevator = elevator_alloc(q, new_e);
- if (!q->elevator)
+ err = new_e->ops.elevator_init_fn(q, new_e);
+ if (err)
goto fail_init;
- err = new_e->ops.elevator_init_fn(q);
- if (err) {
- kobject_put(&q->elevator->kobj);
- goto fail_init;
- }
-
if (registered) {
err = elv_register_queue(q);
if (err)
diff --git a/block/noop-iosched.c b/block/noop-iosched.c
index 5d1bf70..3de89d4 100644
--- a/block/noop-iosched.c
+++ b/block/noop-iosched.c
@@ -59,16 +59,27 @@
return list_entry(rq->queuelist.next, struct request, queuelist);
}
-static int noop_init_queue(struct request_queue *q)
+static int noop_init_queue(struct request_queue *q, struct elevator_type *e)
{
struct noop_data *nd;
+ struct elevator_queue *eq;
- nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node);
- if (!nd)
+ eq = elevator_alloc(q, e);
+ if (!eq)
return -ENOMEM;
+ nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node);
+ if (!nd) {
+ kobject_put(&eq->kobj);
+ return -ENOMEM;
+ }
+ eq->elevator_data = nd;
+
INIT_LIST_HEAD(&nd->queue);
- q->elevator->elevator_data = nd;
+
+ spin_lock_irq(q->queue_lock);
+ q->elevator = eq;
+ spin_unlock_irq(q->queue_lock);
return 0;
}
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index e9e8bb2..4ab807d 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -324,14 +324,27 @@
if (result)
return result;
- if (state == ACPI_STATE_UNKNOWN)
+ if (state == ACPI_STATE_UNKNOWN) {
state = ACPI_STATE_D0;
-
- result = acpi_device_set_power(device, state);
- if (!result && state_p)
+ result = acpi_device_set_power(device, state);
+ if (result)
+ return result;
+ } else {
+ if (device->power.flags.power_resources) {
+ /*
+ * We don't need to really switch the state, bu we need
+ * to update the power resources' reference counters.
+ */
+ result = acpi_power_transition(device, state);
+ if (result)
+ return result;
+ }
+ device->power.state = state;
+ }
+ if (state_p)
*state_p = state;
- return result;
+ return 0;
}
EXPORT_SYMBOL_GPL(acpi_bus_update_power);
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 14de9f4..8265607 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -1064,10 +1064,10 @@
return AE_OK;
}
-int __init acpi_dock_init(void)
+void __init acpi_dock_init(void)
{
if (acpi_disabled)
- return 0;
+ return;
/* look for dock stations and bays */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
@@ -1075,11 +1075,10 @@
if (!dock_station_count) {
pr_info(PREFIX "No dock devices found.\n");
- return 0;
+ return;
}
register_acpi_bus_notifier(&dock_acpi_notifier);
pr_info(PREFIX "%s: %d docks/bays found\n",
ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
- return 0;
}
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 8d1c010..5b02a0a 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -84,7 +84,7 @@
{
struct acpi_device *device = cdev->devdata;
int result;
- int acpi_state;
+ int acpi_state = ACPI_STATE_D0;
if (!device)
return -EINVAL;
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 288bb27..5c28c89 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -279,7 +279,7 @@
if (resource->ref_count++) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Power resource [%s] already on",
+ "Power resource [%s] already on\n",
resource->name));
} else {
result = __acpi_power_on(resource);
@@ -325,7 +325,7 @@
if (!resource->ref_count) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Power resource [%s] already off",
+ "Power resource [%s] already off\n",
resource->name));
return 0;
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index dfe76f1..1098557 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -35,7 +35,6 @@
static const char *dummy_hid = "device";
-static LIST_HEAD(acpi_device_list);
static LIST_HEAD(acpi_bus_id_list);
static DEFINE_MUTEX(acpi_scan_lock);
static LIST_HEAD(acpi_scan_handlers_list);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index aba6e93..80dc988 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -160,7 +160,7 @@
config PATA_OCTEON_CF
tristate "OCTEON Boot Bus Compact Flash support"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
help
This option enables a polled compact flash driver for use with
compact flash cards attached to the OCTEON boot bus.
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
index 08fe897..6687ba7 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/base/dma-buf.c
@@ -680,10 +680,7 @@
d = debugfs_create_file(name, S_IRUGO, dma_buf_debugfs_dir,
write, &dma_buf_debug_fops);
- if (IS_ERR(d))
- return PTR_ERR(d);
-
- return 0;
+ return PTR_RET(d);
}
#else
static inline int dma_buf_init_debugfs(void)
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 2f9dbf7..40a8654 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -167,7 +167,7 @@
config HW_RANDOM_OCTEON
tristate "Octeon Random Number Generator support"
- depends on HW_RANDOM && CPU_CAVIUM_OCTEON
+ depends on HW_RANDOM && CAVIUM_OCTEON_SOC
default HW_RANDOM
---help---
This driver provides kernel-side support for the Random Number
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 81465c2..b7b9b04 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -27,6 +27,11 @@
config ARMADA_370_XP_TIMER
bool
+config ORION_TIMER
+ select CLKSRC_OF
+ select CLKSRC_MMIO
+ bool
+
config SUN4I_TIMER
bool
@@ -69,6 +74,19 @@
bool
select CLKSRC_OF if OF
+config ARM_GLOBAL_TIMER
+ bool
+ select CLKSRC_OF if OF
+ help
+ This options enables support for the ARM global timer unit
+
+config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+ bool
+ depends on ARM_GLOBAL_TIMER
+ default y
+ help
+ Use ARM global timer clock source as sched_clock
+
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 9ba8b4d..8b00c5c 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -15,6 +15,7 @@
obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
+obj-$(CONFIG_ORION_TIMER) += time-orion.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
obj-$(CONFIG_ARCH_MXS) += mxs_timer.o
@@ -30,5 +31,6 @@
obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
+obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
new file mode 100644
index 0000000..db8afc7
--- /dev/null
+++ b/drivers/clocksource/arm_global_timer.c
@@ -0,0 +1,321 @@
+/*
+ * drivers/clocksource/arm_global_timer.c
+ *
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
+ * Author: Stuart Menefy <stuart.menefy@st.com>
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.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/init.h>
+#include <linux/interrupt.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/sched_clock.h>
+
+#include <asm/cputype.h>
+
+#define GT_COUNTER0 0x00
+#define GT_COUNTER1 0x04
+
+#define GT_CONTROL 0x08
+#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */
+#define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */
+#define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */
+#define GT_CONTROL_AUTO_INC BIT(3) /* banked */
+
+#define GT_INT_STATUS 0x0c
+#define GT_INT_STATUS_EVENT_FLAG BIT(0)
+
+#define GT_COMP0 0x10
+#define GT_COMP1 0x14
+#define GT_AUTO_INC 0x18
+
+/*
+ * We are expecting to be clocked by the ARM peripheral clock.
+ *
+ * Note: it is assumed we are using a prescaler value of zero, so this is
+ * the units for all operations.
+ */
+static void __iomem *gt_base;
+static unsigned long gt_clk_rate;
+static int gt_ppi;
+static struct clock_event_device __percpu *gt_evt;
+
+/*
+ * To get the value from the Global Timer Counter register proceed as follows:
+ * 1. Read the upper 32-bit timer counter register
+ * 2. Read the lower 32-bit timer counter register
+ * 3. Read the upper 32-bit timer counter register again. If the value is
+ * different to the 32-bit upper value read previously, go back to step 2.
+ * Otherwise the 64-bit timer counter value is correct.
+ */
+static u64 gt_counter_read(void)
+{
+ u64 counter;
+ u32 lower;
+ u32 upper, old_upper;
+
+ upper = readl_relaxed(gt_base + GT_COUNTER1);
+ do {
+ old_upper = upper;
+ lower = readl_relaxed(gt_base + GT_COUNTER0);
+ upper = readl_relaxed(gt_base + GT_COUNTER1);
+ } while (upper != old_upper);
+
+ counter = upper;
+ counter <<= 32;
+ counter |= lower;
+ return counter;
+}
+
+/**
+ * To ensure that updates to comparator value register do not set the
+ * Interrupt Status Register proceed as follows:
+ * 1. Clear the Comp Enable bit in the Timer Control Register.
+ * 2. Write the lower 32-bit Comparator Value Register.
+ * 3. Write the upper 32-bit Comparator Value Register.
+ * 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit.
+ */
+static void gt_compare_set(unsigned long delta, int periodic)
+{
+ u64 counter = gt_counter_read();
+ unsigned long ctrl;
+
+ counter += delta;
+ ctrl = GT_CONTROL_TIMER_ENABLE;
+ writel(ctrl, gt_base + GT_CONTROL);
+ writel(lower_32_bits(counter), gt_base + GT_COMP0);
+ writel(upper_32_bits(counter), gt_base + GT_COMP1);
+
+ if (periodic) {
+ writel(delta, gt_base + GT_AUTO_INC);
+ ctrl |= GT_CONTROL_AUTO_INC;
+ }
+
+ ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
+ writel(ctrl, gt_base + GT_CONTROL);
+}
+
+static void gt_clockevent_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ unsigned long ctrl;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ ctrl = readl(gt_base + GT_CONTROL);
+ ctrl &= ~(GT_CONTROL_COMP_ENABLE |
+ GT_CONTROL_IRQ_ENABLE | GT_CONTROL_AUTO_INC);
+ writel(ctrl, gt_base + GT_CONTROL);
+ break;
+ default:
+ break;
+ }
+}
+
+static int gt_clockevent_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ gt_compare_set(evt, 0);
+ return 0;
+}
+
+static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+
+ if (!(readl_relaxed(gt_base + GT_INT_STATUS) &
+ GT_INT_STATUS_EVENT_FLAG))
+ return IRQ_NONE;
+
+ /**
+ * ERRATA 740657( Global Timer can send 2 interrupts for
+ * the same event in single-shot mode)
+ * Workaround:
+ * Either disable single-shot mode.
+ * Or
+ * Modify the Interrupt Handler to avoid the
+ * offending sequence. This is achieved by clearing
+ * the Global Timer flag _after_ having incremented
+ * the Comparator register value to a higher value.
+ */
+ if (evt->mode == CLOCK_EVT_MODE_ONESHOT)
+ gt_compare_set(ULONG_MAX, 0);
+
+ writel_relaxed(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS);
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static int __cpuinit gt_clockevents_init(struct clock_event_device *clk)
+{
+ int cpu = smp_processor_id();
+
+ clk->name = "arm_global_timer";
+ clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ clk->set_mode = gt_clockevent_set_mode;
+ clk->set_next_event = gt_clockevent_set_next_event;
+ clk->cpumask = cpumask_of(cpu);
+ clk->rating = 300;
+ clk->irq = gt_ppi;
+ clockevents_config_and_register(clk, gt_clk_rate,
+ 1, 0xffffffff);
+ enable_percpu_irq(clk->irq, IRQ_TYPE_NONE);
+ return 0;
+}
+
+static void gt_clockevents_stop(struct clock_event_device *clk)
+{
+ gt_clockevent_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+ disable_percpu_irq(clk->irq);
+}
+
+static cycle_t gt_clocksource_read(struct clocksource *cs)
+{
+ return gt_counter_read();
+}
+
+static struct clocksource gt_clocksource = {
+ .name = "arm_global_timer",
+ .rating = 300,
+ .read = gt_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+static u32 notrace gt_sched_clock_read(void)
+{
+ return gt_counter_read();
+}
+#endif
+
+static void __init gt_clocksource_init(void)
+{
+ writel(0, gt_base + GT_CONTROL);
+ writel(0, gt_base + GT_COUNTER0);
+ writel(0, gt_base + GT_COUNTER1);
+ /* enables timer on all the cores */
+ writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
+
+#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+ setup_sched_clock(gt_sched_clock_read, 32, gt_clk_rate);
+#endif
+ clocksource_register_hz(>_clocksource, gt_clk_rate);
+}
+
+static int __cpuinit gt_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_STARTING:
+ gt_clockevents_init(this_cpu_ptr(gt_evt));
+ break;
+ case CPU_DYING:
+ gt_clockevents_stop(this_cpu_ptr(gt_evt));
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+static struct notifier_block gt_cpu_nb __cpuinitdata = {
+ .notifier_call = gt_cpu_notify,
+};
+
+static void __init global_timer_of_register(struct device_node *np)
+{
+ struct clk *gt_clk;
+ int err = 0;
+
+ /*
+ * In r2p0 the comparators for each processor with the global timer
+ * fire when the timer value is greater than or equal to. In previous
+ * revisions the comparators fired when the timer value was equal to.
+ */
+ if ((read_cpuid_id() & 0xf0000f) < 0x200000) {
+ pr_warn("global-timer: non support for this cpu version.\n");
+ return;
+ }
+
+ gt_ppi = irq_of_parse_and_map(np, 0);
+ if (!gt_ppi) {
+ pr_warn("global-timer: unable to parse irq\n");
+ return;
+ }
+
+ gt_base = of_iomap(np, 0);
+ if (!gt_base) {
+ pr_warn("global-timer: invalid base address\n");
+ return;
+ }
+
+ gt_clk = of_clk_get(np, 0);
+ if (!IS_ERR(gt_clk)) {
+ err = clk_prepare_enable(gt_clk);
+ if (err)
+ goto out_unmap;
+ } else {
+ pr_warn("global-timer: clk not found\n");
+ err = -EINVAL;
+ goto out_unmap;
+ }
+
+ gt_clk_rate = clk_get_rate(gt_clk);
+ gt_evt = alloc_percpu(struct clock_event_device);
+ if (!gt_evt) {
+ pr_warn("global-timer: can't allocate memory\n");
+ err = -ENOMEM;
+ goto out_clk;
+ }
+
+ err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
+ "gt", gt_evt);
+ if (err) {
+ pr_warn("global-timer: can't register interrupt %d (%d)\n",
+ gt_ppi, err);
+ goto out_free;
+ }
+
+ err = register_cpu_notifier(>_cpu_nb);
+ if (err) {
+ pr_warn("global-timer: unable to register cpu notifier.\n");
+ goto out_irq;
+ }
+
+ /* Immediately configure the timer on the boot CPU */
+ gt_clocksource_init();
+ gt_clockevents_init(this_cpu_ptr(gt_evt));
+
+ return;
+
+out_irq:
+ free_percpu_irq(gt_ppi, gt_evt);
+out_free:
+ free_percpu(gt_evt);
+out_clk:
+ clk_disable_unprepare(gt_clk);
+out_unmap:
+ iounmap(gt_base);
+ WARN(err, "ARM Global timer register failed (%d)\n", err);
+}
+
+/* Only tested on r2p2 and r3p0 */
+CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
+ global_timer_of_register);
diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
new file mode 100644
index 0000000..ecbeb68
--- /dev/null
+++ b/drivers/clocksource/time-orion.c
@@ -0,0 +1,150 @@
+/*
+ * Marvell Orion SoC timer handling.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.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.
+ *
+ * Timer 0 is used as free-running clocksource, while timer 1 is
+ * used as clock_event_device.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+#include <asm/sched_clock.h>
+
+#define TIMER_CTRL 0x00
+#define TIMER0_EN BIT(0)
+#define TIMER0_RELOAD_EN BIT(1)
+#define TIMER1_EN BIT(2)
+#define TIMER1_RELOAD_EN BIT(3)
+#define TIMER0_RELOAD 0x10
+#define TIMER0_VAL 0x14
+#define TIMER1_RELOAD 0x18
+#define TIMER1_VAL 0x1c
+
+#define ORION_ONESHOT_MIN 1
+#define ORION_ONESHOT_MAX 0xfffffffe
+
+static void __iomem *timer_base;
+static DEFINE_SPINLOCK(timer_ctrl_lock);
+
+/*
+ * Thread-safe access to TIMER_CTRL register
+ * (shared with watchdog timer)
+ */
+void orion_timer_ctrl_clrset(u32 clr, u32 set)
+{
+ spin_lock(&timer_ctrl_lock);
+ writel((readl(timer_base + TIMER_CTRL) & ~clr) | set,
+ timer_base + TIMER_CTRL);
+ spin_unlock(&timer_ctrl_lock);
+}
+EXPORT_SYMBOL(orion_timer_ctrl_clrset);
+
+/*
+ * Free-running clocksource handling.
+ */
+static u32 notrace orion_read_sched_clock(void)
+{
+ return ~readl(timer_base + TIMER0_VAL);
+}
+
+/*
+ * Clockevent handling.
+ */
+static u32 ticks_per_jiffy;
+
+static int orion_clkevt_next_event(unsigned long delta,
+ struct clock_event_device *dev)
+{
+ /* setup and enable one-shot timer */
+ writel(delta, timer_base + TIMER1_VAL);
+ orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN);
+
+ return 0;
+}
+
+static void orion_clkevt_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
+ if (mode == CLOCK_EVT_MODE_PERIODIC) {
+ /* setup and enable periodic timer at 1/HZ intervals */
+ writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD);
+ writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL);
+ orion_timer_ctrl_clrset(0, TIMER1_RELOAD_EN | TIMER1_EN);
+ } else {
+ /* disable timer */
+ orion_timer_ctrl_clrset(TIMER1_RELOAD_EN | TIMER1_EN, 0);
+ }
+}
+
+static struct clock_event_device orion_clkevt = {
+ .name = "orion_event",
+ .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
+ .shift = 32,
+ .rating = 300,
+ .set_next_event = orion_clkevt_next_event,
+ .set_mode = orion_clkevt_mode,
+};
+
+static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id)
+{
+ orion_clkevt.event_handler(&orion_clkevt);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction orion_clkevt_irq = {
+ .name = "orion_event",
+ .flags = IRQF_TIMER,
+ .handler = orion_clkevt_irq_handler,
+};
+
+static void __init orion_timer_init(struct device_node *np)
+{
+ struct clk *clk;
+ int irq;
+
+ /* timer registers are shared with watchdog timer */
+ timer_base = of_iomap(np, 0);
+ if (!timer_base)
+ panic("%s: unable to map resource\n", np->name);
+
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk))
+ panic("%s: unable to get clk\n", np->name);
+ clk_prepare_enable(clk);
+
+ /* we are only interested in timer1 irq */
+ irq = irq_of_parse_and_map(np, 1);
+ if (irq <= 0)
+ panic("%s: unable to parse timer1 irq\n", np->name);
+
+ /* setup timer0 as free-running clocksource */
+ writel(~0, timer_base + TIMER0_VAL);
+ writel(~0, timer_base + TIMER0_RELOAD);
+ orion_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | TIMER0_EN);
+ clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource",
+ clk_get_rate(clk), 300, 32,
+ clocksource_mmio_readl_down);
+ setup_sched_clock(orion_read_sched_clock, 32, clk_get_rate(clk));
+
+ /* setup timer1 as clockevent timer */
+ if (setup_irq(irq, &orion_clkevt_irq))
+ panic("%s: unable to setup irq\n", np->name);
+
+ ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ;
+ orion_clkevt.cpumask = cpumask_of(0);
+ orion_clkevt.irq = irq;
+ clockevents_config_and_register(&orion_clkevt, clk_get_rate(clk),
+ ORION_ONESHOT_MIN, ORION_ONESHOT_MAX);
+}
+CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 6a015ad..0937b8d 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -312,11 +312,12 @@
switch (state) {
case CPUFREQ_PRECHANGE:
- if (WARN(policy->transition_ongoing,
+ if (WARN(policy->transition_ongoing ==
+ cpumask_weight(policy->cpus),
"In middle of another frequency transition\n"))
return;
- policy->transition_ongoing = true;
+ policy->transition_ongoing++;
/* detect if the driver reported a value as "old frequency"
* which is not equal to what the cpufreq core thinks is
@@ -341,7 +342,7 @@
"No frequency transition in progress\n"))
return;
- policy->transition_ongoing = false;
+ policy->transition_ongoing--;
adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new,
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index a697a64..878f090 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -349,21 +349,21 @@
config EDAC_OCTEON_L2C
tristate "Cavium Octeon Secondary Caches (L2C)"
- depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
+ depends on EDAC_MM_EDAC && CAVIUM_OCTEON_SOC
help
Support for error detection and correction on the
Cavium Octeon family of SOCs.
config EDAC_OCTEON_LMC
tristate "Cavium Octeon DRAM Memory Controller (LMC)"
- depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
+ depends on EDAC_MM_EDAC && CAVIUM_OCTEON_SOC
help
Support for error detection and correction on the
Cavium Octeon family of SOCs.
config EDAC_OCTEON_PCI
tristate "Cavium Octeon PCI Controller"
- depends on EDAC_MM_EDAC && PCI && CPU_CAVIUM_OCTEON
+ depends on EDAC_MM_EDAC && PCI && CAVIUM_OCTEON_SOC
help
Support for error detection and correction on the
Cavium Octeon family of SOCs.
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
index a4f5ce1..271b42b 100644
--- a/drivers/gpu/drm/drm_edid_load.c
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -133,8 +133,8 @@
},
};
-static u8 *edid_load(struct drm_connector *connector, char *name,
- char *connector_name)
+static u8 *edid_load(struct drm_connector *connector, const char *name,
+ const char *connector_name)
{
const struct firmware *fw;
struct platform_device *pdev;
@@ -242,7 +242,7 @@
int drm_load_edid_firmware(struct drm_connector *connector)
{
- char *connector_name = drm_get_connector_name(connector);
+ const char *connector_name = drm_get_connector_name(connector);
char *edidname = edid_firmware, *last, *colon;
int ret;
struct edid *edid;
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index fdc2ab4..dc6dea6 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -739,7 +739,7 @@
config I2C_OCTEON
tristate "Cavium OCTEON I2C bus support"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
help
Say yes if you want to support the I2C serial bus on Cavium
OCTEON SOC.
diff --git a/drivers/ide/delkin_cb.c b/drivers/ide/delkin_cb.c
index 7e27d32..300daab 100644
--- a/drivers/ide/delkin_cb.c
+++ b/drivers/ide/delkin_cb.c
@@ -173,18 +173,7 @@
.resume = delkin_cb_resume,
};
-static int __init delkin_cb_init(void)
-{
- return pci_register_driver(&delkin_cb_pci_driver);
-}
-
-static void __exit delkin_cb_exit(void)
-{
- pci_unregister_driver(&delkin_cb_pci_driver);
-}
-
-module_init(delkin_cb_init);
-module_exit(delkin_cb_exit);
+module_pci_driver(delkin_cb_pci_driver);
MODULE_AUTHOR("Mark Lord");
MODULE_DESCRIPTION("Basic support for Delkin/ASKA/Workbit Cardbus IDE");
diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c
index 51beb85..0a8440a 100644
--- a/drivers/ide/gayle.c
+++ b/drivers/ide/gayle.c
@@ -183,20 +183,7 @@
},
};
-static int __init amiga_gayle_ide_init(void)
-{
- return platform_driver_probe(&amiga_gayle_ide_driver,
- amiga_gayle_ide_probe);
-}
-
-module_init(amiga_gayle_ide_init);
-
-static void __exit amiga_gayle_ide_exit(void)
-{
- platform_driver_unregister(&amiga_gayle_ide_driver);
-}
-
-module_exit(amiga_gayle_ide_exit);
+module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:amiga-gayle-ide");
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 729428e..dabb88b 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -239,9 +239,6 @@
unsigned nr_bytes = min(len, cursg->length - cmd->cursg_ofs);
int page_is_high;
- if (nr_bytes > PAGE_SIZE)
- nr_bytes = PAGE_SIZE;
-
page = sg_page(cursg);
offset = cursg->offset + cmd->cursg_ofs;
@@ -249,6 +246,8 @@
page = nth_page(page, (offset >> PAGE_SHIFT));
offset %= PAGE_SIZE;
+ nr_bytes = min_t(unsigned, nr_bytes, (PAGE_SIZE - offset));
+
page_is_high = PageHighMem(page);
if (page_is_high)
local_irq_save(flags);
diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c
index 91d49dd..ede8575 100644
--- a/drivers/ide/tx4938ide.c
+++ b/drivers/ide/tx4938ide.c
@@ -203,18 +203,7 @@
.remove = __exit_p(tx4938ide_remove),
};
-static int __init tx4938ide_init(void)
-{
- return platform_driver_probe(&tx4938ide_driver, tx4938ide_probe);
-}
-
-static void __exit tx4938ide_exit(void)
-{
- platform_driver_unregister(&tx4938ide_driver);
-}
-
-module_init(tx4938ide_init);
-module_exit(tx4938ide_exit);
+module_platform_driver_probe(tx4938ide_driver, tx4938ide_probe);
MODULE_DESCRIPTION("TX4938 internal IDE driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c
index c0ab800..4ecdee5 100644
--- a/drivers/ide/tx4939ide.c
+++ b/drivers/ide/tx4939ide.c
@@ -624,18 +624,7 @@
.resume = tx4939ide_resume,
};
-static int __init tx4939ide_init(void)
-{
- return platform_driver_probe(&tx4939ide_driver, tx4939ide_probe);
-}
-
-static void __exit tx4939ide_exit(void)
-{
- platform_driver_unregister(&tx4939ide_driver);
-}
-
-module_init(tx4939ide_init);
-module_exit(tx4939ide_exit);
+module_platform_driver_probe(tx4939ide_driver, tx4939ide_probe);
MODULE_DESCRIPTION("TX4939 internal IDE driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index c85b56c..5ceda71 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -50,6 +50,7 @@
source "drivers/infiniband/hw/cxgb3/Kconfig"
source "drivers/infiniband/hw/cxgb4/Kconfig"
source "drivers/infiniband/hw/mlx4/Kconfig"
+source "drivers/infiniband/hw/mlx5/Kconfig"
source "drivers/infiniband/hw/nes/Kconfig"
source "drivers/infiniband/hw/ocrdma/Kconfig"
diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile
index b126fef..1fe6988 100644
--- a/drivers/infiniband/Makefile
+++ b/drivers/infiniband/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_INFINIBAND_CXGB3) += hw/cxgb3/
obj-$(CONFIG_INFINIBAND_CXGB4) += hw/cxgb4/
obj-$(CONFIG_MLX4_INFINIBAND) += hw/mlx4/
+obj-$(CONFIG_MLX5_INFINIBAND) += hw/mlx5/
obj-$(CONFIG_INFINIBAND_NES) += hw/nes/
obj-$(CONFIG_INFINIBAND_OCRDMA) += hw/ocrdma/
obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index eaec8d7..e90f2b2 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -45,6 +45,7 @@
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <rdma/ib_addr.h>
+#include <rdma/ib.h>
MODULE_AUTHOR("Sean Hefty");
MODULE_DESCRIPTION("IB Address Translation");
@@ -70,6 +71,21 @@
static DECLARE_DELAYED_WORK(work, process_req);
static struct workqueue_struct *addr_wq;
+int rdma_addr_size(struct sockaddr *addr)
+{
+ switch (addr->sa_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ case AF_IB:
+ return sizeof(struct sockaddr_ib);
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(rdma_addr_size);
+
void rdma_addr_register_client(struct rdma_addr_client *client)
{
atomic_set(&client->refcount, 1);
@@ -369,12 +385,12 @@
goto err;
}
- memcpy(src_in, src_addr, ip_addr_size(src_addr));
+ memcpy(src_in, src_addr, rdma_addr_size(src_addr));
} else {
src_in->sa_family = dst_addr->sa_family;
}
- memcpy(dst_in, dst_addr, ip_addr_size(dst_addr));
+ memcpy(dst_in, dst_addr, rdma_addr_size(dst_addr));
req->addr = addr;
req->callback = callback;
req->context = context;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 34fbc2f..f1c279f 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -50,6 +50,7 @@
#include <rdma/rdma_cm.h>
#include <rdma/rdma_cm_ib.h>
#include <rdma/rdma_netlink.h>
+#include <rdma/ib.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_cm.h>
#include <rdma/ib_sa.h>
@@ -79,7 +80,6 @@
static LIST_HEAD(listen_any_list);
static DEFINE_MUTEX(lock);
static struct workqueue_struct *cma_wq;
-static DEFINE_IDR(sdp_ps);
static DEFINE_IDR(tcp_ps);
static DEFINE_IDR(udp_ps);
static DEFINE_IDR(ipoib_ps);
@@ -195,24 +195,7 @@
union cma_ip_addr dst_addr;
};
-struct sdp_hh {
- u8 bsdh[16];
- u8 sdp_version; /* Major version: 7:4 */
- u8 ip_version; /* IP version: 7:4 */
- u8 sdp_specific1[10];
- __be16 port;
- __be16 sdp_specific2;
- union cma_ip_addr src_addr;
- union cma_ip_addr dst_addr;
-};
-
-struct sdp_hah {
- u8 bsdh[16];
- u8 sdp_version;
-};
-
#define CMA_VERSION 0x00
-#define SDP_MAJ_VERSION 0x2
static int cma_comp(struct rdma_id_private *id_priv, enum rdma_cm_state comp)
{
@@ -261,21 +244,6 @@
hdr->ip_version = (ip_ver << 4) | (hdr->ip_version & 0xF);
}
-static inline u8 sdp_get_majv(u8 sdp_version)
-{
- return sdp_version >> 4;
-}
-
-static inline u8 sdp_get_ip_ver(struct sdp_hh *hh)
-{
- return hh->ip_version >> 4;
-}
-
-static inline void sdp_set_ip_ver(struct sdp_hh *hh, u8 ip_ver)
-{
- hh->ip_version = (ip_ver << 4) | (hh->ip_version & 0xF);
-}
-
static void cma_attach_to_dev(struct rdma_id_private *id_priv,
struct cma_device *cma_dev)
{
@@ -310,16 +278,40 @@
mutex_unlock(&lock);
}
-static int cma_set_qkey(struct rdma_id_private *id_priv)
+static inline struct sockaddr *cma_src_addr(struct rdma_id_private *id_priv)
+{
+ return (struct sockaddr *) &id_priv->id.route.addr.src_addr;
+}
+
+static inline struct sockaddr *cma_dst_addr(struct rdma_id_private *id_priv)
+{
+ return (struct sockaddr *) &id_priv->id.route.addr.dst_addr;
+}
+
+static inline unsigned short cma_family(struct rdma_id_private *id_priv)
+{
+ return id_priv->id.route.addr.src_addr.ss_family;
+}
+
+static int cma_set_qkey(struct rdma_id_private *id_priv, u32 qkey)
{
struct ib_sa_mcmember_rec rec;
int ret = 0;
- if (id_priv->qkey)
+ if (id_priv->qkey) {
+ if (qkey && id_priv->qkey != qkey)
+ return -EINVAL;
return 0;
+ }
+
+ if (qkey) {
+ id_priv->qkey = qkey;
+ return 0;
+ }
switch (id_priv->id.ps) {
case RDMA_PS_UDP:
+ case RDMA_PS_IB:
id_priv->qkey = RDMA_UDP_QKEY;
break;
case RDMA_PS_IPOIB:
@@ -358,6 +350,27 @@
return -EADDRNOTAVAIL;
}
+static void cma_translate_ib(struct sockaddr_ib *sib, struct rdma_dev_addr *dev_addr)
+{
+ dev_addr->dev_type = ARPHRD_INFINIBAND;
+ rdma_addr_set_sgid(dev_addr, (union ib_gid *) &sib->sib_addr);
+ ib_addr_set_pkey(dev_addr, ntohs(sib->sib_pkey));
+}
+
+static int cma_translate_addr(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
+{
+ int ret;
+
+ if (addr->sa_family != AF_IB) {
+ ret = rdma_translate_ip(addr, dev_addr);
+ } else {
+ cma_translate_ib((struct sockaddr_ib *) addr, dev_addr);
+ ret = 0;
+ }
+
+ return ret;
+}
+
static int cma_acquire_dev(struct rdma_id_private *id_priv)
{
struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
@@ -401,6 +414,61 @@
return ret;
}
+/*
+ * Select the source IB device and address to reach the destination IB address.
+ */
+static int cma_resolve_ib_dev(struct rdma_id_private *id_priv)
+{
+ struct cma_device *cma_dev, *cur_dev;
+ struct sockaddr_ib *addr;
+ union ib_gid gid, sgid, *dgid;
+ u16 pkey, index;
+ u8 port, p;
+ int i;
+
+ cma_dev = NULL;
+ addr = (struct sockaddr_ib *) cma_dst_addr(id_priv);
+ dgid = (union ib_gid *) &addr->sib_addr;
+ pkey = ntohs(addr->sib_pkey);
+
+ list_for_each_entry(cur_dev, &dev_list, list) {
+ if (rdma_node_get_transport(cur_dev->device->node_type) != RDMA_TRANSPORT_IB)
+ continue;
+
+ for (p = 1; p <= cur_dev->device->phys_port_cnt; ++p) {
+ if (ib_find_cached_pkey(cur_dev->device, p, pkey, &index))
+ continue;
+
+ for (i = 0; !ib_get_cached_gid(cur_dev->device, p, i, &gid); i++) {
+ if (!memcmp(&gid, dgid, sizeof(gid))) {
+ cma_dev = cur_dev;
+ sgid = gid;
+ port = p;
+ goto found;
+ }
+
+ if (!cma_dev && (gid.global.subnet_prefix ==
+ dgid->global.subnet_prefix)) {
+ cma_dev = cur_dev;
+ sgid = gid;
+ port = p;
+ }
+ }
+ }
+ }
+
+ if (!cma_dev)
+ return -ENODEV;
+
+found:
+ cma_attach_to_dev(id_priv, cma_dev);
+ id_priv->id.port_num = port;
+ addr = (struct sockaddr_ib *) cma_src_addr(id_priv);
+ memcpy(&addr->sib_addr, &sgid, sizeof sgid);
+ cma_translate_ib(addr, &id_priv->id.route.addr.dev_addr);
+ return 0;
+}
+
static void cma_deref_id(struct rdma_id_private *id_priv)
{
if (atomic_dec_and_test(&id_priv->refcount))
@@ -630,7 +698,7 @@
*qp_attr_mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_PORT;
if (id_priv->id.qp_type == IB_QPT_UD) {
- ret = cma_set_qkey(id_priv);
+ ret = cma_set_qkey(id_priv, 0);
if (ret)
return ret;
@@ -679,26 +747,30 @@
static inline int cma_zero_addr(struct sockaddr *addr)
{
- struct in6_addr *ip6;
-
- if (addr->sa_family == AF_INET)
- return ipv4_is_zeronet(
- ((struct sockaddr_in *)addr)->sin_addr.s_addr);
- else {
- ip6 = &((struct sockaddr_in6 *) addr)->sin6_addr;
- return (ip6->s6_addr32[0] | ip6->s6_addr32[1] |
- ip6->s6_addr32[2] | ip6->s6_addr32[3]) == 0;
+ switch (addr->sa_family) {
+ case AF_INET:
+ return ipv4_is_zeronet(((struct sockaddr_in *)addr)->sin_addr.s_addr);
+ case AF_INET6:
+ return ipv6_addr_any(&((struct sockaddr_in6 *) addr)->sin6_addr);
+ case AF_IB:
+ return ib_addr_any(&((struct sockaddr_ib *) addr)->sib_addr);
+ default:
+ return 0;
}
}
static inline int cma_loopback_addr(struct sockaddr *addr)
{
- if (addr->sa_family == AF_INET)
- return ipv4_is_loopback(
- ((struct sockaddr_in *) addr)->sin_addr.s_addr);
- else
- return ipv6_addr_loopback(
- &((struct sockaddr_in6 *) addr)->sin6_addr);
+ switch (addr->sa_family) {
+ case AF_INET:
+ return ipv4_is_loopback(((struct sockaddr_in *) addr)->sin_addr.s_addr);
+ case AF_INET6:
+ return ipv6_addr_loopback(&((struct sockaddr_in6 *) addr)->sin6_addr);
+ case AF_IB:
+ return ib_addr_loopback(&((struct sockaddr_ib *) addr)->sib_addr);
+ default:
+ return 0;
+ }
}
static inline int cma_any_addr(struct sockaddr *addr)
@@ -715,18 +787,31 @@
case AF_INET:
return ((struct sockaddr_in *) src)->sin_addr.s_addr !=
((struct sockaddr_in *) dst)->sin_addr.s_addr;
- default:
+ case AF_INET6:
return ipv6_addr_cmp(&((struct sockaddr_in6 *) src)->sin6_addr,
&((struct sockaddr_in6 *) dst)->sin6_addr);
+ default:
+ return ib_addr_cmp(&((struct sockaddr_ib *) src)->sib_addr,
+ &((struct sockaddr_ib *) dst)->sib_addr);
}
}
-static inline __be16 cma_port(struct sockaddr *addr)
+static __be16 cma_port(struct sockaddr *addr)
{
- if (addr->sa_family == AF_INET)
+ struct sockaddr_ib *sib;
+
+ switch (addr->sa_family) {
+ case AF_INET:
return ((struct sockaddr_in *) addr)->sin_port;
- else
+ case AF_INET6:
return ((struct sockaddr_in6 *) addr)->sin6_port;
+ case AF_IB:
+ sib = (struct sockaddr_ib *) addr;
+ return htons((u16) (be64_to_cpu(sib->sib_sid) &
+ be64_to_cpu(sib->sib_sid_mask)));
+ default:
+ return 0;
+ }
}
static inline int cma_any_port(struct sockaddr *addr)
@@ -734,83 +819,92 @@
return !cma_port(addr);
}
-static int cma_get_net_info(void *hdr, enum rdma_port_space ps,
- u8 *ip_ver, __be16 *port,
- union cma_ip_addr **src, union cma_ip_addr **dst)
+static void cma_save_ib_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id,
+ struct ib_sa_path_rec *path)
{
- switch (ps) {
- case RDMA_PS_SDP:
- if (sdp_get_majv(((struct sdp_hh *) hdr)->sdp_version) !=
- SDP_MAJ_VERSION)
- return -EINVAL;
+ struct sockaddr_ib *listen_ib, *ib;
- *ip_ver = sdp_get_ip_ver(hdr);
- *port = ((struct sdp_hh *) hdr)->port;
- *src = &((struct sdp_hh *) hdr)->src_addr;
- *dst = &((struct sdp_hh *) hdr)->dst_addr;
- break;
- default:
- if (((struct cma_hdr *) hdr)->cma_version != CMA_VERSION)
- return -EINVAL;
+ listen_ib = (struct sockaddr_ib *) &listen_id->route.addr.src_addr;
+ ib = (struct sockaddr_ib *) &id->route.addr.src_addr;
+ ib->sib_family = listen_ib->sib_family;
+ ib->sib_pkey = path->pkey;
+ ib->sib_flowinfo = path->flow_label;
+ memcpy(&ib->sib_addr, &path->sgid, 16);
+ ib->sib_sid = listen_ib->sib_sid;
+ ib->sib_sid_mask = cpu_to_be64(0xffffffffffffffffULL);
+ ib->sib_scope_id = listen_ib->sib_scope_id;
- *ip_ver = cma_get_ip_ver(hdr);
- *port = ((struct cma_hdr *) hdr)->port;
- *src = &((struct cma_hdr *) hdr)->src_addr;
- *dst = &((struct cma_hdr *) hdr)->dst_addr;
- break;
+ ib = (struct sockaddr_ib *) &id->route.addr.dst_addr;
+ ib->sib_family = listen_ib->sib_family;
+ ib->sib_pkey = path->pkey;
+ ib->sib_flowinfo = path->flow_label;
+ memcpy(&ib->sib_addr, &path->dgid, 16);
+}
+
+static void cma_save_ip4_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id,
+ struct cma_hdr *hdr)
+{
+ struct sockaddr_in *listen4, *ip4;
+
+ listen4 = (struct sockaddr_in *) &listen_id->route.addr.src_addr;
+ ip4 = (struct sockaddr_in *) &id->route.addr.src_addr;
+ ip4->sin_family = listen4->sin_family;
+ ip4->sin_addr.s_addr = hdr->dst_addr.ip4.addr;
+ ip4->sin_port = listen4->sin_port;
+
+ ip4 = (struct sockaddr_in *) &id->route.addr.dst_addr;
+ ip4->sin_family = listen4->sin_family;
+ ip4->sin_addr.s_addr = hdr->src_addr.ip4.addr;
+ ip4->sin_port = hdr->port;
+}
+
+static void cma_save_ip6_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id,
+ struct cma_hdr *hdr)
+{
+ struct sockaddr_in6 *listen6, *ip6;
+
+ listen6 = (struct sockaddr_in6 *) &listen_id->route.addr.src_addr;
+ ip6 = (struct sockaddr_in6 *) &id->route.addr.src_addr;
+ ip6->sin6_family = listen6->sin6_family;
+ ip6->sin6_addr = hdr->dst_addr.ip6;
+ ip6->sin6_port = listen6->sin6_port;
+
+ ip6 = (struct sockaddr_in6 *) &id->route.addr.dst_addr;
+ ip6->sin6_family = listen6->sin6_family;
+ ip6->sin6_addr = hdr->src_addr.ip6;
+ ip6->sin6_port = hdr->port;
+}
+
+static int cma_save_net_info(struct rdma_cm_id *id, struct rdma_cm_id *listen_id,
+ struct ib_cm_event *ib_event)
+{
+ struct cma_hdr *hdr;
+
+ if (listen_id->route.addr.src_addr.ss_family == AF_IB) {
+ cma_save_ib_info(id, listen_id, ib_event->param.req_rcvd.primary_path);
+ return 0;
}
- if (*ip_ver != 4 && *ip_ver != 6)
+ hdr = ib_event->private_data;
+ if (hdr->cma_version != CMA_VERSION)
return -EINVAL;
+
+ switch (cma_get_ip_ver(hdr)) {
+ case 4:
+ cma_save_ip4_info(id, listen_id, hdr);
+ break;
+ case 6:
+ cma_save_ip6_info(id, listen_id, hdr);
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
-static void cma_save_net_info(struct rdma_addr *addr,
- struct rdma_addr *listen_addr,
- u8 ip_ver, __be16 port,
- union cma_ip_addr *src, union cma_ip_addr *dst)
+static inline int cma_user_data_offset(struct rdma_id_private *id_priv)
{
- struct sockaddr_in *listen4, *ip4;
- struct sockaddr_in6 *listen6, *ip6;
-
- switch (ip_ver) {
- case 4:
- listen4 = (struct sockaddr_in *) &listen_addr->src_addr;
- ip4 = (struct sockaddr_in *) &addr->src_addr;
- ip4->sin_family = listen4->sin_family;
- ip4->sin_addr.s_addr = dst->ip4.addr;
- ip4->sin_port = listen4->sin_port;
-
- ip4 = (struct sockaddr_in *) &addr->dst_addr;
- ip4->sin_family = listen4->sin_family;
- ip4->sin_addr.s_addr = src->ip4.addr;
- ip4->sin_port = port;
- break;
- case 6:
- listen6 = (struct sockaddr_in6 *) &listen_addr->src_addr;
- ip6 = (struct sockaddr_in6 *) &addr->src_addr;
- ip6->sin6_family = listen6->sin6_family;
- ip6->sin6_addr = dst->ip6;
- ip6->sin6_port = listen6->sin6_port;
-
- ip6 = (struct sockaddr_in6 *) &addr->dst_addr;
- ip6->sin6_family = listen6->sin6_family;
- ip6->sin6_addr = src->ip6;
- ip6->sin6_port = port;
- break;
- default:
- break;
- }
-}
-
-static inline int cma_user_data_offset(enum rdma_port_space ps)
-{
- switch (ps) {
- case RDMA_PS_SDP:
- return 0;
- default:
- return sizeof(struct cma_hdr);
- }
+ return cma_family(id_priv) == AF_IB ? 0 : sizeof(struct cma_hdr);
}
static void cma_cancel_route(struct rdma_id_private *id_priv)
@@ -861,8 +955,7 @@
cma_cancel_route(id_priv);
break;
case RDMA_CM_LISTEN:
- if (cma_any_addr((struct sockaddr *) &id_priv->id.route.addr.src_addr)
- && !id_priv->cma_dev)
+ if (cma_any_addr(cma_src_addr(id_priv)) && !id_priv->cma_dev)
cma_cancel_listens(id_priv);
break;
default:
@@ -977,16 +1070,6 @@
return ret;
}
-static int cma_verify_rep(struct rdma_id_private *id_priv, void *data)
-{
- if (id_priv->id.ps == RDMA_PS_SDP &&
- sdp_get_majv(((struct sdp_hah *) data)->sdp_version) !=
- SDP_MAJ_VERSION)
- return -EINVAL;
-
- return 0;
-}
-
static void cma_set_rep_event_data(struct rdma_cm_event *event,
struct ib_cm_rep_event_param *rep_data,
void *private_data)
@@ -1021,15 +1104,13 @@
event.status = -ETIMEDOUT;
break;
case IB_CM_REP_RECEIVED:
- event.status = cma_verify_rep(id_priv, ib_event->private_data);
- if (event.status)
- event.event = RDMA_CM_EVENT_CONNECT_ERROR;
- else if (id_priv->id.qp && id_priv->id.ps != RDMA_PS_SDP) {
+ if (id_priv->id.qp) {
event.status = cma_rep_recv(id_priv);
event.event = event.status ? RDMA_CM_EVENT_CONNECT_ERROR :
RDMA_CM_EVENT_ESTABLISHED;
- } else
+ } else {
event.event = RDMA_CM_EVENT_CONNECT_RESPONSE;
+ }
cma_set_rep_event_data(&event, &ib_event->param.rep_rcvd,
ib_event->private_data);
break;
@@ -1085,22 +1166,16 @@
struct rdma_id_private *id_priv;
struct rdma_cm_id *id;
struct rdma_route *rt;
- union cma_ip_addr *src, *dst;
- __be16 port;
- u8 ip_ver;
int ret;
- if (cma_get_net_info(ib_event->private_data, listen_id->ps,
- &ip_ver, &port, &src, &dst))
- return NULL;
-
id = rdma_create_id(listen_id->event_handler, listen_id->context,
listen_id->ps, ib_event->param.req_rcvd.qp_type);
if (IS_ERR(id))
return NULL;
- cma_save_net_info(&id->route.addr, &listen_id->route.addr,
- ip_ver, port, src, dst);
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (cma_save_net_info(id, listen_id, ib_event))
+ goto err;
rt = &id->route;
rt->num_paths = ib_event->param.req_rcvd.alternate_path ? 2 : 1;
@@ -1113,19 +1188,17 @@
if (rt->num_paths == 2)
rt->path_rec[1] = *ib_event->param.req_rcvd.alternate_path;
- if (cma_any_addr((struct sockaddr *) &rt->addr.src_addr)) {
+ if (cma_any_addr(cma_src_addr(id_priv))) {
rt->addr.dev_addr.dev_type = ARPHRD_INFINIBAND;
rdma_addr_set_sgid(&rt->addr.dev_addr, &rt->path_rec[0].sgid);
ib_addr_set_pkey(&rt->addr.dev_addr, be16_to_cpu(rt->path_rec[0].pkey));
} else {
- ret = rdma_translate_ip((struct sockaddr *) &rt->addr.src_addr,
- &rt->addr.dev_addr);
+ ret = cma_translate_addr(cma_src_addr(id_priv), &rt->addr.dev_addr);
if (ret)
goto err;
}
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 = RDMA_CM_CONNECT;
return id_priv;
@@ -1139,9 +1212,6 @@
{
struct rdma_id_private *id_priv;
struct rdma_cm_id *id;
- union cma_ip_addr *src, *dst;
- __be16 port;
- u8 ip_ver;
int ret;
id = rdma_create_id(listen_id->event_handler, listen_id->context,
@@ -1149,22 +1219,16 @@
if (IS_ERR(id))
return NULL;
-
- if (cma_get_net_info(ib_event->private_data, listen_id->ps,
- &ip_ver, &port, &src, &dst))
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (cma_save_net_info(id, listen_id, ib_event))
goto err;
- cma_save_net_info(&id->route.addr, &listen_id->route.addr,
- ip_ver, port, src, dst);
-
if (!cma_any_addr((struct sockaddr *) &id->route.addr.src_addr)) {
- ret = rdma_translate_ip((struct sockaddr *) &id->route.addr.src_addr,
- &id->route.addr.dev_addr);
+ ret = cma_translate_addr(cma_src_addr(id_priv), &id->route.addr.dev_addr);
if (ret)
goto err;
}
- id_priv = container_of(id, struct rdma_id_private, id);
id_priv->state = RDMA_CM_CONNECT;
return id_priv;
err:
@@ -1210,7 +1274,7 @@
return -ECONNABORTED;
memset(&event, 0, sizeof event);
- offset = cma_user_data_offset(listen_id->id.ps);
+ offset = cma_user_data_offset(listen_id);
event.event = RDMA_CM_EVENT_CONNECT_REQUEST;
if (ib_event->event == IB_CM_SIDR_REQ_RECEIVED) {
conn_id = cma_new_udp_id(&listen_id->id, ib_event);
@@ -1272,58 +1336,44 @@
return ret;
}
-static __be64 cma_get_service_id(enum rdma_port_space ps, struct sockaddr *addr)
+__be64 rdma_get_service_id(struct rdma_cm_id *id, struct sockaddr *addr)
{
- return cpu_to_be64(((u64)ps << 16) + be16_to_cpu(cma_port(addr)));
+ if (addr->sa_family == AF_IB)
+ return ((struct sockaddr_ib *) addr)->sib_sid;
+
+ return cpu_to_be64(((u64)id->ps << 16) + be16_to_cpu(cma_port(addr)));
}
+EXPORT_SYMBOL(rdma_get_service_id);
static void cma_set_compare_data(enum rdma_port_space ps, struct sockaddr *addr,
struct ib_cm_compare_data *compare)
{
struct cma_hdr *cma_data, *cma_mask;
- struct sdp_hh *sdp_data, *sdp_mask;
__be32 ip4_addr;
struct in6_addr ip6_addr;
memset(compare, 0, sizeof *compare);
cma_data = (void *) compare->data;
cma_mask = (void *) compare->mask;
- sdp_data = (void *) compare->data;
- sdp_mask = (void *) compare->mask;
switch (addr->sa_family) {
case AF_INET:
ip4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
- if (ps == RDMA_PS_SDP) {
- sdp_set_ip_ver(sdp_data, 4);
- sdp_set_ip_ver(sdp_mask, 0xF);
- sdp_data->dst_addr.ip4.addr = ip4_addr;
- sdp_mask->dst_addr.ip4.addr = htonl(~0);
- } else {
- cma_set_ip_ver(cma_data, 4);
- cma_set_ip_ver(cma_mask, 0xF);
- if (!cma_any_addr(addr)) {
- cma_data->dst_addr.ip4.addr = ip4_addr;
- cma_mask->dst_addr.ip4.addr = htonl(~0);
- }
+ cma_set_ip_ver(cma_data, 4);
+ cma_set_ip_ver(cma_mask, 0xF);
+ if (!cma_any_addr(addr)) {
+ cma_data->dst_addr.ip4.addr = ip4_addr;
+ cma_mask->dst_addr.ip4.addr = htonl(~0);
}
break;
case AF_INET6:
ip6_addr = ((struct sockaddr_in6 *) addr)->sin6_addr;
- if (ps == RDMA_PS_SDP) {
- sdp_set_ip_ver(sdp_data, 6);
- sdp_set_ip_ver(sdp_mask, 0xF);
- sdp_data->dst_addr.ip6 = ip6_addr;
- memset(&sdp_mask->dst_addr.ip6, 0xFF,
- sizeof sdp_mask->dst_addr.ip6);
- } else {
- cma_set_ip_ver(cma_data, 6);
- cma_set_ip_ver(cma_mask, 0xF);
- if (!cma_any_addr(addr)) {
- cma_data->dst_addr.ip6 = ip6_addr;
- memset(&cma_mask->dst_addr.ip6, 0xFF,
- sizeof cma_mask->dst_addr.ip6);
- }
+ cma_set_ip_ver(cma_data, 6);
+ cma_set_ip_ver(cma_mask, 0xF);
+ if (!cma_any_addr(addr)) {
+ cma_data->dst_addr.ip6 = ip6_addr;
+ memset(&cma_mask->dst_addr.ip6, 0xFF,
+ sizeof cma_mask->dst_addr.ip6);
}
break;
default:
@@ -1347,9 +1397,9 @@
event.event = RDMA_CM_EVENT_DISCONNECTED;
break;
case IW_CM_EVENT_CONNECT_REPLY:
- sin = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
+ sin = (struct sockaddr_in *) cma_src_addr(id_priv);
*sin = iw_event->local_addr;
- sin = (struct sockaddr_in *) &id_priv->id.route.addr.dst_addr;
+ sin = (struct sockaddr_in *) cma_dst_addr(id_priv);
*sin = iw_event->remote_addr;
switch (iw_event->status) {
case 0:
@@ -1447,9 +1497,9 @@
cm_id->context = conn_id;
cm_id->cm_handler = cma_iw_handler;
- sin = (struct sockaddr_in *) &new_cm_id->route.addr.src_addr;
+ sin = (struct sockaddr_in *) cma_src_addr(conn_id);
*sin = iw_event->local_addr;
- sin = (struct sockaddr_in *) &new_cm_id->route.addr.dst_addr;
+ sin = (struct sockaddr_in *) cma_dst_addr(conn_id);
*sin = iw_event->remote_addr;
ret = ib_query_device(conn_id->id.device, &attr);
@@ -1506,8 +1556,8 @@
id_priv->cm_id.ib = id;
- addr = (struct sockaddr *) &id_priv->id.route.addr.src_addr;
- svc_id = cma_get_service_id(id_priv->id.ps, addr);
+ addr = cma_src_addr(id_priv);
+ svc_id = rdma_get_service_id(&id_priv->id, addr);
if (cma_any_addr(addr) && !id_priv->afonly)
ret = ib_cm_listen(id_priv->cm_id.ib, svc_id, 0, NULL);
else {
@@ -1537,7 +1587,7 @@
id_priv->cm_id.iw = id;
- sin = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
+ sin = (struct sockaddr_in *) cma_src_addr(id_priv);
id_priv->cm_id.iw->local_addr = *sin;
ret = iw_cm_listen(id_priv->cm_id.iw, backlog);
@@ -1567,6 +1617,10 @@
struct rdma_cm_id *id;
int ret;
+ if (cma_family(id_priv) == AF_IB &&
+ rdma_node_get_transport(cma_dev->device->node_type) != RDMA_TRANSPORT_IB)
+ return;
+
id = rdma_create_id(cma_listen_handler, id_priv, id_priv->id.ps,
id_priv->id.qp_type);
if (IS_ERR(id))
@@ -1575,8 +1629,8 @@
dev_id_priv = container_of(id, struct rdma_id_private, id);
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));
+ memcpy(cma_src_addr(dev_id_priv), cma_src_addr(id_priv),
+ rdma_addr_size(cma_src_addr(id_priv)));
cma_attach_to_dev(dev_id_priv, cma_dev);
list_add_tail(&dev_id_priv->listen_list, &id_priv->listen_list);
@@ -1634,31 +1688,39 @@
static int cma_query_ib_route(struct rdma_id_private *id_priv, int timeout_ms,
struct cma_work *work)
{
- struct rdma_addr *addr = &id_priv->id.route.addr;
+ struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
struct ib_sa_path_rec path_rec;
ib_sa_comp_mask comp_mask;
struct sockaddr_in6 *sin6;
+ struct sockaddr_ib *sib;
memset(&path_rec, 0, sizeof path_rec);
- rdma_addr_get_sgid(&addr->dev_addr, &path_rec.sgid);
- rdma_addr_get_dgid(&addr->dev_addr, &path_rec.dgid);
- path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(&addr->dev_addr));
+ rdma_addr_get_sgid(dev_addr, &path_rec.sgid);
+ rdma_addr_get_dgid(dev_addr, &path_rec.dgid);
+ path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr));
path_rec.numb_path = 1;
path_rec.reversible = 1;
- path_rec.service_id = cma_get_service_id(id_priv->id.ps,
- (struct sockaddr *) &addr->dst_addr);
+ path_rec.service_id = rdma_get_service_id(&id_priv->id, cma_dst_addr(id_priv));
comp_mask = IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID |
IB_SA_PATH_REC_PKEY | IB_SA_PATH_REC_NUMB_PATH |
IB_SA_PATH_REC_REVERSIBLE | IB_SA_PATH_REC_SERVICE_ID;
- if (addr->src_addr.ss_family == AF_INET) {
+ switch (cma_family(id_priv)) {
+ case AF_INET:
path_rec.qos_class = cpu_to_be16((u16) id_priv->tos);
comp_mask |= IB_SA_PATH_REC_QOS_CLASS;
- } else {
- sin6 = (struct sockaddr_in6 *) &addr->src_addr;
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) cma_src_addr(id_priv);
path_rec.traffic_class = (u8) (be32_to_cpu(sin6->sin6_flowinfo) >> 20);
comp_mask |= IB_SA_PATH_REC_TRAFFIC_CLASS;
+ break;
+ case AF_IB:
+ sib = (struct sockaddr_ib *) cma_src_addr(id_priv);
+ path_rec.traffic_class = (u8) (be32_to_cpu(sib->sib_flowinfo) >> 20);
+ comp_mask |= IB_SA_PATH_REC_TRAFFIC_CLASS;
+ break;
}
id_priv->query_id = ib_sa_path_rec_get(&sa_client, id_priv->id.device,
@@ -1800,14 +1862,9 @@
struct rdma_addr *addr = &route->addr;
struct cma_work *work;
int ret;
- struct sockaddr_in *src_addr = (struct sockaddr_in *)&route->addr.src_addr;
- struct sockaddr_in *dst_addr = (struct sockaddr_in *)&route->addr.dst_addr;
struct net_device *ndev = NULL;
u16 vid;
- if (src_addr->sin_family != dst_addr->sin_family)
- return -EINVAL;
-
work = kzalloc(sizeof *work, GFP_KERNEL);
if (!work)
return -ENOMEM;
@@ -1913,28 +1970,57 @@
}
EXPORT_SYMBOL(rdma_resolve_route);
+static void cma_set_loopback(struct sockaddr *addr)
+{
+ switch (addr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ break;
+ case AF_INET6:
+ ipv6_addr_set(&((struct sockaddr_in6 *) addr)->sin6_addr,
+ 0, 0, 0, htonl(1));
+ break;
+ default:
+ ib_addr_set(&((struct sockaddr_ib *) addr)->sib_addr,
+ 0, 0, 0, htonl(1));
+ break;
+ }
+}
+
static int cma_bind_loopback(struct rdma_id_private *id_priv)
{
- struct cma_device *cma_dev;
+ struct cma_device *cma_dev, *cur_dev;
struct ib_port_attr port_attr;
union ib_gid gid;
u16 pkey;
int ret;
u8 p;
+ cma_dev = NULL;
mutex_lock(&lock);
- if (list_empty(&dev_list)) {
+ list_for_each_entry(cur_dev, &dev_list, list) {
+ if (cma_family(id_priv) == AF_IB &&
+ rdma_node_get_transport(cur_dev->device->node_type) != RDMA_TRANSPORT_IB)
+ continue;
+
+ if (!cma_dev)
+ cma_dev = cur_dev;
+
+ for (p = 1; p <= cur_dev->device->phys_port_cnt; ++p) {
+ if (!ib_query_port(cur_dev->device, p, &port_attr) &&
+ port_attr.state == IB_PORT_ACTIVE) {
+ cma_dev = cur_dev;
+ goto port_found;
+ }
+ }
+ }
+
+ if (!cma_dev) {
ret = -ENODEV;
goto out;
}
- list_for_each_entry(cma_dev, &dev_list, list)
- for (p = 1; p <= cma_dev->device->phys_port_cnt; ++p)
- if (!ib_query_port(cma_dev->device, p, &port_attr) &&
- port_attr.state == IB_PORT_ACTIVE)
- goto port_found;
p = 1;
- cma_dev = list_entry(dev_list.next, struct cma_device, list);
port_found:
ret = ib_get_cached_gid(cma_dev->device, p, 0, &gid);
@@ -1953,6 +2039,7 @@
ib_addr_set_pkey(&id_priv->id.route.addr.dev_addr, pkey);
id_priv->id.port_num = p;
cma_attach_to_dev(id_priv, cma_dev);
+ cma_set_loopback(cma_src_addr(id_priv));
out:
mutex_unlock(&lock);
return ret;
@@ -1980,8 +2067,7 @@
event.event = RDMA_CM_EVENT_ADDR_ERROR;
event.status = status;
} else {
- memcpy(&id_priv->id.route.addr.src_addr, src_addr,
- ip_addr_size(src_addr));
+ memcpy(cma_src_addr(id_priv), src_addr, rdma_addr_size(src_addr));
event.event = RDMA_CM_EVENT_ADDR_RESOLVED;
}
@@ -2000,7 +2086,6 @@
static int cma_resolve_loopback(struct rdma_id_private *id_priv)
{
struct cma_work *work;
- struct sockaddr *src, *dst;
union ib_gid gid;
int ret;
@@ -2017,18 +2102,36 @@
rdma_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid);
rdma_addr_set_dgid(&id_priv->id.route.addr.dev_addr, &gid);
- src = (struct sockaddr *) &id_priv->id.route.addr.src_addr;
- if (cma_zero_addr(src)) {
- dst = (struct sockaddr *) &id_priv->id.route.addr.dst_addr;
- if ((src->sa_family = dst->sa_family) == AF_INET) {
- ((struct sockaddr_in *)src)->sin_addr =
- ((struct sockaddr_in *)dst)->sin_addr;
- } else {
- ((struct sockaddr_in6 *)src)->sin6_addr =
- ((struct sockaddr_in6 *)dst)->sin6_addr;
- }
+ work->id = id_priv;
+ INIT_WORK(&work->work, cma_work_handler);
+ 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;
+err:
+ kfree(work);
+ return ret;
+}
+
+static int cma_resolve_ib_addr(struct rdma_id_private *id_priv)
+{
+ struct cma_work *work;
+ int ret;
+
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (!work)
+ return -ENOMEM;
+
+ if (!id_priv->cma_dev) {
+ ret = cma_resolve_ib_dev(id_priv);
+ if (ret)
+ goto err;
}
+ rdma_addr_set_dgid(&id_priv->id.route.addr.dev_addr, (union ib_gid *)
+ &(((struct sockaddr_ib *) &id_priv->id.route.addr.dst_addr)->sib_addr));
+
work->id = id_priv;
INIT_WORK(&work->work, cma_work_handler);
work->old_state = RDMA_CM_ADDR_QUERY;
@@ -2046,9 +2149,13 @@
{
if (!src_addr || !src_addr->sa_family) {
src_addr = (struct sockaddr *) &id->route.addr.src_addr;
- if ((src_addr->sa_family = dst_addr->sa_family) == AF_INET6) {
+ src_addr->sa_family = dst_addr->sa_family;
+ if (dst_addr->sa_family == AF_INET6) {
((struct sockaddr_in6 *) src_addr)->sin6_scope_id =
((struct sockaddr_in6 *) dst_addr)->sin6_scope_id;
+ } else if (dst_addr->sa_family == AF_IB) {
+ ((struct sockaddr_ib *) src_addr)->sib_pkey =
+ ((struct sockaddr_ib *) dst_addr)->sib_pkey;
}
}
return rdma_bind_addr(id, src_addr);
@@ -2067,17 +2174,25 @@
return ret;
}
+ if (cma_family(id_priv) != dst_addr->sa_family)
+ return -EINVAL;
+
if (!cma_comp_exch(id_priv, RDMA_CM_ADDR_BOUND, RDMA_CM_ADDR_QUERY))
return -EINVAL;
atomic_inc(&id_priv->refcount);
- memcpy(&id->route.addr.dst_addr, dst_addr, ip_addr_size(dst_addr));
- if (cma_any_addr(dst_addr))
+ memcpy(cma_dst_addr(id_priv), dst_addr, rdma_addr_size(dst_addr));
+ if (cma_any_addr(dst_addr)) {
ret = cma_resolve_loopback(id_priv);
- else
- ret = rdma_resolve_ip(&addr_client, (struct sockaddr *) &id->route.addr.src_addr,
- dst_addr, &id->route.addr.dev_addr,
- timeout_ms, addr_handler, id_priv);
+ } else {
+ if (dst_addr->sa_family == AF_IB) {
+ ret = cma_resolve_ib_addr(id_priv);
+ } else {
+ ret = rdma_resolve_ip(&addr_client, cma_src_addr(id_priv),
+ dst_addr, &id->route.addr.dev_addr,
+ timeout_ms, addr_handler, id_priv);
+ }
+ }
if (ret)
goto err;
@@ -2097,7 +2212,7 @@
id_priv = container_of(id, struct rdma_id_private, id);
spin_lock_irqsave(&id_priv->lock, flags);
- if (id_priv->state == RDMA_CM_IDLE) {
+ if (reuse || id_priv->state == RDMA_CM_IDLE) {
id_priv->reuseaddr = reuse;
ret = 0;
} else {
@@ -2131,10 +2246,29 @@
static void cma_bind_port(struct rdma_bind_list *bind_list,
struct rdma_id_private *id_priv)
{
- struct sockaddr_in *sin;
+ struct sockaddr *addr;
+ struct sockaddr_ib *sib;
+ u64 sid, mask;
+ __be16 port;
- sin = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
- sin->sin_port = htons(bind_list->port);
+ addr = cma_src_addr(id_priv);
+ port = htons(bind_list->port);
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) addr)->sin_port = port;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) addr)->sin6_port = port;
+ break;
+ case AF_IB:
+ sib = (struct sockaddr_ib *) addr;
+ sid = be64_to_cpu(sib->sib_sid);
+ mask = be64_to_cpu(sib->sib_sid_mask);
+ sib->sib_sid = cpu_to_be64((sid & mask) | (u64) ntohs(port));
+ sib->sib_sid_mask = cpu_to_be64(~0ULL);
+ break;
+ }
id_priv->bind_list = bind_list;
hlist_add_head(&id_priv->node, &bind_list->owners);
}
@@ -2205,7 +2339,7 @@
struct rdma_id_private *cur_id;
struct sockaddr *addr, *cur_addr;
- addr = (struct sockaddr *) &id_priv->id.route.addr.src_addr;
+ addr = cma_src_addr(id_priv);
hlist_for_each_entry(cur_id, &bind_list->owners, node) {
if (id_priv == cur_id)
continue;
@@ -2214,7 +2348,7 @@
cur_id->reuseaddr)
continue;
- cur_addr = (struct sockaddr *) &cur_id->id.route.addr.src_addr;
+ cur_addr = cma_src_addr(cur_id);
if (id_priv->afonly && cur_id->afonly &&
(addr->sa_family != cur_addr->sa_family))
continue;
@@ -2234,7 +2368,7 @@
unsigned short snum;
int ret;
- snum = ntohs(cma_port((struct sockaddr *) &id_priv->id.route.addr.src_addr));
+ snum = ntohs(cma_port(cma_src_addr(id_priv)));
if (snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
@@ -2261,33 +2395,67 @@
return ret;
}
+static struct idr *cma_select_inet_ps(struct rdma_id_private *id_priv)
+{
+ switch (id_priv->id.ps) {
+ case RDMA_PS_TCP:
+ return &tcp_ps;
+ case RDMA_PS_UDP:
+ return &udp_ps;
+ case RDMA_PS_IPOIB:
+ return &ipoib_ps;
+ case RDMA_PS_IB:
+ return &ib_ps;
+ default:
+ return NULL;
+ }
+}
+
+static struct idr *cma_select_ib_ps(struct rdma_id_private *id_priv)
+{
+ struct idr *ps = NULL;
+ struct sockaddr_ib *sib;
+ u64 sid_ps, mask, sid;
+
+ sib = (struct sockaddr_ib *) cma_src_addr(id_priv);
+ mask = be64_to_cpu(sib->sib_sid_mask) & RDMA_IB_IP_PS_MASK;
+ sid = be64_to_cpu(sib->sib_sid) & mask;
+
+ if ((id_priv->id.ps == RDMA_PS_IB) && (sid == (RDMA_IB_IP_PS_IB & mask))) {
+ sid_ps = RDMA_IB_IP_PS_IB;
+ ps = &ib_ps;
+ } else if (((id_priv->id.ps == RDMA_PS_IB) || (id_priv->id.ps == RDMA_PS_TCP)) &&
+ (sid == (RDMA_IB_IP_PS_TCP & mask))) {
+ sid_ps = RDMA_IB_IP_PS_TCP;
+ ps = &tcp_ps;
+ } else if (((id_priv->id.ps == RDMA_PS_IB) || (id_priv->id.ps == RDMA_PS_UDP)) &&
+ (sid == (RDMA_IB_IP_PS_UDP & mask))) {
+ sid_ps = RDMA_IB_IP_PS_UDP;
+ ps = &udp_ps;
+ }
+
+ if (ps) {
+ sib->sib_sid = cpu_to_be64(sid_ps | ntohs(cma_port((struct sockaddr *) sib)));
+ sib->sib_sid_mask = cpu_to_be64(RDMA_IB_IP_PS_MASK |
+ be64_to_cpu(sib->sib_sid_mask));
+ }
+ return ps;
+}
+
static int cma_get_port(struct rdma_id_private *id_priv)
{
struct idr *ps;
int ret;
- switch (id_priv->id.ps) {
- case RDMA_PS_SDP:
- ps = &sdp_ps;
- break;
- case RDMA_PS_TCP:
- ps = &tcp_ps;
- break;
- case RDMA_PS_UDP:
- ps = &udp_ps;
- break;
- case RDMA_PS_IPOIB:
- ps = &ipoib_ps;
- break;
- case RDMA_PS_IB:
- ps = &ib_ps;
- break;
- default:
+ if (cma_family(id_priv) != AF_IB)
+ ps = cma_select_inet_ps(id_priv);
+ else
+ ps = cma_select_ib_ps(id_priv);
+ if (!ps)
return -EPROTONOSUPPORT;
- }
mutex_lock(&lock);
- if (cma_any_port((struct sockaddr *) &id_priv->id.route.addr.src_addr))
+ if (cma_any_port(cma_src_addr(id_priv)))
ret = cma_alloc_any_port(ps, id_priv);
else
ret = cma_use_port(ps, id_priv);
@@ -2322,8 +2490,8 @@
id_priv = container_of(id, struct rdma_id_private, id);
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);
+ id->route.addr.src_addr.ss_family = AF_INET;
+ ret = rdma_bind_addr(id, cma_src_addr(id_priv));
if (ret)
return ret;
}
@@ -2370,7 +2538,8 @@
struct rdma_id_private *id_priv;
int ret;
- if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
+ if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6 &&
+ addr->sa_family != AF_IB)
return -EAFNOSUPPORT;
id_priv = container_of(id, struct rdma_id_private, id);
@@ -2382,7 +2551,7 @@
goto err1;
if (!cma_any_addr(addr)) {
- ret = rdma_translate_ip(addr, &id->route.addr.dev_addr);
+ ret = cma_translate_addr(addr, &id->route.addr.dev_addr);
if (ret)
goto err1;
@@ -2391,7 +2560,7 @@
goto err1;
}
- memcpy(&id->route.addr.src_addr, addr, ip_addr_size(addr));
+ memcpy(cma_src_addr(id_priv), addr, rdma_addr_size(addr));
if (!(id_priv->options & (1 << CMA_OPTION_AFONLY))) {
if (addr->sa_family == AF_INET)
id_priv->afonly = 1;
@@ -2414,62 +2583,32 @@
}
EXPORT_SYMBOL(rdma_bind_addr);
-static int cma_format_hdr(void *hdr, enum rdma_port_space ps,
- struct rdma_route *route)
+static int cma_format_hdr(void *hdr, struct rdma_id_private *id_priv)
{
struct cma_hdr *cma_hdr;
- struct sdp_hh *sdp_hdr;
- if (route->addr.src_addr.ss_family == AF_INET) {
+ cma_hdr = hdr;
+ cma_hdr->cma_version = CMA_VERSION;
+ if (cma_family(id_priv) == AF_INET) {
struct sockaddr_in *src4, *dst4;
- src4 = (struct sockaddr_in *) &route->addr.src_addr;
- dst4 = (struct sockaddr_in *) &route->addr.dst_addr;
+ src4 = (struct sockaddr_in *) cma_src_addr(id_priv);
+ dst4 = (struct sockaddr_in *) cma_dst_addr(id_priv);
- switch (ps) {
- case RDMA_PS_SDP:
- sdp_hdr = hdr;
- if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
- return -EINVAL;
- sdp_set_ip_ver(sdp_hdr, 4);
- sdp_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
- sdp_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
- sdp_hdr->port = src4->sin_port;
- break;
- default:
- cma_hdr = hdr;
- cma_hdr->cma_version = CMA_VERSION;
- cma_set_ip_ver(cma_hdr, 4);
- cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
- cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
- cma_hdr->port = src4->sin_port;
- break;
- }
- } else {
+ cma_set_ip_ver(cma_hdr, 4);
+ cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+ cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+ cma_hdr->port = src4->sin_port;
+ } else if (cma_family(id_priv) == AF_INET6) {
struct sockaddr_in6 *src6, *dst6;
- src6 = (struct sockaddr_in6 *) &route->addr.src_addr;
- dst6 = (struct sockaddr_in6 *) &route->addr.dst_addr;
+ src6 = (struct sockaddr_in6 *) cma_src_addr(id_priv);
+ dst6 = (struct sockaddr_in6 *) cma_dst_addr(id_priv);
- switch (ps) {
- case RDMA_PS_SDP:
- sdp_hdr = hdr;
- if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
- return -EINVAL;
- sdp_set_ip_ver(sdp_hdr, 6);
- sdp_hdr->src_addr.ip6 = src6->sin6_addr;
- sdp_hdr->dst_addr.ip6 = dst6->sin6_addr;
- sdp_hdr->port = src6->sin6_port;
- break;
- default:
- cma_hdr = hdr;
- cma_hdr->cma_version = CMA_VERSION;
- cma_set_ip_ver(cma_hdr, 6);
- cma_hdr->src_addr.ip6 = src6->sin6_addr;
- cma_hdr->dst_addr.ip6 = dst6->sin6_addr;
- cma_hdr->port = src6->sin6_port;
- break;
- }
+ cma_set_ip_ver(cma_hdr, 6);
+ cma_hdr->src_addr.ip6 = src6->sin6_addr;
+ cma_hdr->dst_addr.ip6 = dst6->sin6_addr;
+ cma_hdr->port = src6->sin6_port;
}
return 0;
}
@@ -2499,15 +2638,10 @@
event.status = ib_event->param.sidr_rep_rcvd.status;
break;
}
- ret = cma_set_qkey(id_priv);
+ ret = cma_set_qkey(id_priv, rep->qkey);
if (ret) {
event.event = RDMA_CM_EVENT_ADDR_ERROR;
- event.status = -EINVAL;
- break;
- }
- if (id_priv->qkey != rep->qkey) {
- event.event = RDMA_CM_EVENT_UNREACHABLE;
- event.status = -EINVAL;
+ event.status = ret;
break;
}
ib_init_ah_from_path(id_priv->id.device, id_priv->id.port_num,
@@ -2542,27 +2676,31 @@
struct rdma_conn_param *conn_param)
{
struct ib_cm_sidr_req_param req;
- struct rdma_route *route;
struct ib_cm_id *id;
- int ret;
+ int offset, ret;
- req.private_data_len = sizeof(struct cma_hdr) +
- conn_param->private_data_len;
+ offset = cma_user_data_offset(id_priv);
+ req.private_data_len = offset + conn_param->private_data_len;
if (req.private_data_len < conn_param->private_data_len)
return -EINVAL;
- req.private_data = kzalloc(req.private_data_len, GFP_ATOMIC);
- if (!req.private_data)
- return -ENOMEM;
+ if (req.private_data_len) {
+ req.private_data = kzalloc(req.private_data_len, GFP_ATOMIC);
+ if (!req.private_data)
+ return -ENOMEM;
+ } else {
+ req.private_data = NULL;
+ }
if (conn_param->private_data && conn_param->private_data_len)
- memcpy((void *) req.private_data + sizeof(struct cma_hdr),
+ memcpy((void *) req.private_data + offset,
conn_param->private_data, conn_param->private_data_len);
- route = &id_priv->id.route;
- ret = cma_format_hdr((void *) req.private_data, id_priv->id.ps, route);
- if (ret)
- goto out;
+ if (req.private_data) {
+ ret = cma_format_hdr((void *) req.private_data, id_priv);
+ if (ret)
+ goto out;
+ }
id = ib_create_cm_id(id_priv->id.device, cma_sidr_rep_handler,
id_priv);
@@ -2572,9 +2710,8 @@
}
id_priv->cm_id.ib = id;
- req.path = route->path_rec;
- req.service_id = cma_get_service_id(id_priv->id.ps,
- (struct sockaddr *) &route->addr.dst_addr);
+ req.path = id_priv->id.route.path_rec;
+ req.service_id = rdma_get_service_id(&id_priv->id, cma_dst_addr(id_priv));
req.timeout_ms = 1 << (CMA_CM_RESPONSE_TIMEOUT - 8);
req.max_cm_retries = CMA_MAX_CM_RETRIES;
@@ -2598,14 +2735,18 @@
int offset, ret;
memset(&req, 0, sizeof req);
- offset = cma_user_data_offset(id_priv->id.ps);
+ offset = cma_user_data_offset(id_priv);
req.private_data_len = offset + conn_param->private_data_len;
if (req.private_data_len < conn_param->private_data_len)
return -EINVAL;
- private_data = kzalloc(req.private_data_len, GFP_ATOMIC);
- if (!private_data)
- return -ENOMEM;
+ if (req.private_data_len) {
+ private_data = kzalloc(req.private_data_len, GFP_ATOMIC);
+ if (!private_data)
+ return -ENOMEM;
+ } else {
+ private_data = NULL;
+ }
if (conn_param->private_data && conn_param->private_data_len)
memcpy(private_data + offset, conn_param->private_data,
@@ -2619,17 +2760,18 @@
id_priv->cm_id.ib = id;
route = &id_priv->id.route;
- ret = cma_format_hdr(private_data, id_priv->id.ps, route);
- if (ret)
- goto out;
- req.private_data = private_data;
+ if (private_data) {
+ ret = cma_format_hdr(private_data, id_priv);
+ if (ret)
+ goto out;
+ req.private_data = private_data;
+ }
req.primary_path = &route->path_rec[0];
if (route->num_paths == 2)
req.alternate_path = &route->path_rec[1];
- req.service_id = cma_get_service_id(id_priv->id.ps,
- (struct sockaddr *) &route->addr.dst_addr);
+ req.service_id = rdma_get_service_id(&id_priv->id, cma_dst_addr(id_priv));
req.qp_num = id_priv->qp_num;
req.qp_type = id_priv->id.qp_type;
req.starting_psn = id_priv->seq_num;
@@ -2668,10 +2810,10 @@
id_priv->cm_id.iw = cm_id;
- sin = (struct sockaddr_in*) &id_priv->id.route.addr.src_addr;
+ sin = (struct sockaddr_in *) cma_src_addr(id_priv);
cm_id->local_addr = *sin;
- sin = (struct sockaddr_in*) &id_priv->id.route.addr.dst_addr;
+ sin = (struct sockaddr_in *) cma_dst_addr(id_priv);
cm_id->remote_addr = *sin;
ret = cma_modify_qp_rtr(id_priv, conn_param);
@@ -2789,7 +2931,7 @@
}
static int cma_send_sidr_rep(struct rdma_id_private *id_priv,
- enum ib_cm_sidr_status status,
+ enum ib_cm_sidr_status status, u32 qkey,
const void *private_data, int private_data_len)
{
struct ib_cm_sidr_rep_param rep;
@@ -2798,7 +2940,7 @@
memset(&rep, 0, sizeof rep);
rep.status = status;
if (status == IB_SIDR_SUCCESS) {
- ret = cma_set_qkey(id_priv);
+ ret = cma_set_qkey(id_priv, qkey);
if (ret)
return ret;
rep.qp_num = id_priv->qp_num;
@@ -2832,11 +2974,12 @@
if (id->qp_type == IB_QPT_UD) {
if (conn_param)
ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
+ conn_param->qkey,
conn_param->private_data,
conn_param->private_data_len);
else
ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
- NULL, 0);
+ 0, NULL, 0);
} else {
if (conn_param)
ret = cma_accept_ib(id_priv, conn_param);
@@ -2897,7 +3040,7 @@
switch (rdma_node_get_transport(id->device->node_type)) {
case RDMA_TRANSPORT_IB:
if (id->qp_type == IB_QPT_UD)
- ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT,
+ ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT, 0,
private_data, private_data_len);
else
ret = ib_send_cm_rej(id_priv->cm_id.ib,
@@ -2958,6 +3101,8 @@
cma_disable_callback(id_priv, RDMA_CM_ADDR_RESOLVED))
return 0;
+ if (!status)
+ status = cma_set_qkey(id_priv, be32_to_cpu(multicast->rec.qkey));
mutex_lock(&id_priv->qp_mutex);
if (!status && id_priv->id.qp)
status = ib_attach_mcast(id_priv->id.qp, &multicast->rec.mgid,
@@ -3004,6 +3149,8 @@
0xFF10A01B)) {
/* IPv6 address is an SA assigned MGID. */
memcpy(mgid, &sin6->sin6_addr, sizeof *mgid);
+ } else if (addr->sa_family == AF_IB) {
+ memcpy(mgid, &((struct sockaddr_ib *) addr)->sib_addr, sizeof *mgid);
} else if ((addr->sa_family == AF_INET6)) {
ipv6_ib_mc_map(&sin6->sin6_addr, dev_addr->broadcast, mc_map);
if (id_priv->id.ps == RDMA_PS_UDP)
@@ -3031,9 +3178,12 @@
if (ret)
return ret;
+ ret = cma_set_qkey(id_priv, 0);
+ if (ret)
+ return ret;
+
cma_set_mgid(id_priv, (struct sockaddr *) &mc->addr, &rec.mgid);
- if (id_priv->id.ps == RDMA_PS_UDP)
- rec.qkey = cpu_to_be32(RDMA_UDP_QKEY);
+ rec.qkey = cpu_to_be32(id_priv->qkey);
rdma_addr_get_sgid(dev_addr, &rec.port_gid);
rec.pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr));
rec.join_state = 1;
@@ -3170,7 +3320,7 @@
if (!mc)
return -ENOMEM;
- memcpy(&mc->addr, addr, ip_addr_size(addr));
+ memcpy(&mc->addr, addr, rdma_addr_size(addr));
mc->context = context;
mc->id_priv = id_priv;
@@ -3215,7 +3365,7 @@
id_priv = container_of(id, struct rdma_id_private, id);
spin_lock_irq(&id_priv->lock);
list_for_each_entry(mc, &id_priv->mc_list, list) {
- if (!memcmp(&mc->addr, addr, ip_addr_size(addr))) {
+ if (!memcmp(&mc->addr, addr, rdma_addr_size(addr))) {
list_del(&mc->list);
spin_unlock_irq(&id_priv->lock);
@@ -3436,33 +3586,16 @@
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;
- }
- }
+ if (ibnl_put_attr(skb, nlh,
+ rdma_addr_size(cma_src_addr(id_priv)),
+ cma_src_addr(id_priv),
+ RDMA_NL_RDMA_CM_ATTR_SRC_ADDR))
+ goto out;
+ if (ibnl_put_attr(skb, nlh,
+ rdma_addr_size(cma_src_addr(id_priv)),
+ cma_dst_addr(id_priv),
+ RDMA_NL_RDMA_CM_ATTR_DST_ADDR))
+ goto out;
id_stats->pid = id_priv->owner;
id_stats->port_space = id->ps;
@@ -3527,7 +3660,6 @@
rdma_addr_unregister_client(&addr_client);
ib_sa_unregister_client(&sa_client);
destroy_workqueue(cma_wq);
- idr_destroy(&sdp_ps);
idr_destroy(&tcp_ps);
idr_destroy(&udp_ps);
idr_destroy(&ipoib_ps);
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 934f45e..9838ca4 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -652,6 +652,12 @@
}
EXPORT_SYMBOL(ib_sa_unpack_path);
+void ib_sa_pack_path(struct ib_sa_path_rec *rec, void *attribute)
+{
+ ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table), rec, attribute);
+}
+EXPORT_SYMBOL(ib_sa_pack_path);
+
static void ib_sa_path_rec_callback(struct ib_sa_query *sa_query,
int status,
struct ib_sa_mad *mad)
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 99904f7..cde1e7b 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -545,8 +545,10 @@
p->gid_group.name = "gids";
p->gid_group.attrs = alloc_group_attrs(show_port_gid, attr.gid_tbl_len);
- if (!p->gid_group.attrs)
+ if (!p->gid_group.attrs) {
+ ret = -ENOMEM;
goto err_remove_pma;
+ }
ret = sysfs_create_group(&p->kobj, &p->gid_group);
if (ret)
@@ -555,8 +557,10 @@
p->pkey_group.name = "pkeys";
p->pkey_group.attrs = alloc_group_attrs(show_port_pkey,
attr.pkey_tbl_len);
- if (!p->pkey_group.attrs)
+ if (!p->pkey_group.attrs) {
+ ret = -ENOMEM;
goto err_remove_gid;
+ }
ret = sysfs_create_group(&p->kobj, &p->pkey_group);
if (ret)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 5ca44cd..b0f189b 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -47,6 +47,8 @@
#include <rdma/ib_marshall.h>
#include <rdma/rdma_cm.h>
#include <rdma/rdma_cm_ib.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib.h>
MODULE_AUTHOR("Sean Hefty");
MODULE_DESCRIPTION("RDMA Userspace Connection Manager Access");
@@ -510,10 +512,10 @@
return ret;
}
-static ssize_t ucma_bind_addr(struct ucma_file *file, const char __user *inbuf,
+static ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf,
int in_len, int out_len)
{
- struct rdma_ucm_bind_addr cmd;
+ struct rdma_ucm_bind_ip cmd;
struct ucma_context *ctx;
int ret;
@@ -529,11 +531,35 @@
return ret;
}
-static ssize_t ucma_resolve_addr(struct ucma_file *file,
- const char __user *inbuf,
- int in_len, int out_len)
+static ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf,
+ int in_len, int out_len)
{
- struct rdma_ucm_resolve_addr cmd;
+ struct rdma_ucm_bind cmd;
+ struct sockaddr *addr;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ addr = (struct sockaddr *) &cmd.addr;
+ if (cmd.reserved || !cmd.addr_size || (cmd.addr_size != rdma_addr_size(addr)))
+ return -EINVAL;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_bind_addr(ctx->cm_id, addr);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static ssize_t ucma_resolve_ip(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_resolve_ip cmd;
struct ucma_context *ctx;
int ret;
@@ -551,6 +577,33 @@
return ret;
}
+static ssize_t ucma_resolve_addr(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_resolve_addr cmd;
+ struct sockaddr *src, *dst;
+ struct ucma_context *ctx;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ src = (struct sockaddr *) &cmd.src_addr;
+ dst = (struct sockaddr *) &cmd.dst_addr;
+ if (cmd.reserved || (cmd.src_size && (cmd.src_size != rdma_addr_size(src))) ||
+ !cmd.dst_size || (cmd.dst_size != rdma_addr_size(dst)))
+ return -EINVAL;
+
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
static ssize_t ucma_resolve_route(struct ucma_file *file,
const char __user *inbuf,
int in_len, int out_len)
@@ -649,7 +702,7 @@
const char __user *inbuf,
int in_len, int out_len)
{
- struct rdma_ucm_query_route cmd;
+ struct rdma_ucm_query cmd;
struct rdma_ucm_query_route_resp resp;
struct ucma_context *ctx;
struct sockaddr *addr;
@@ -709,7 +762,162 @@
return ret;
}
-static void ucma_copy_conn_param(struct rdma_conn_param *dst,
+static void ucma_query_device_addr(struct rdma_cm_id *cm_id,
+ struct rdma_ucm_query_addr_resp *resp)
+{
+ if (!cm_id->device)
+ return;
+
+ resp->node_guid = (__force __u64) cm_id->device->node_guid;
+ resp->port_num = cm_id->port_num;
+ resp->pkey = (__force __u16) cpu_to_be16(
+ ib_addr_get_pkey(&cm_id->route.addr.dev_addr));
+}
+
+static ssize_t ucma_query_addr(struct ucma_context *ctx,
+ void __user *response, int out_len)
+{
+ struct rdma_ucm_query_addr_resp resp;
+ struct sockaddr *addr;
+ int ret = 0;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ memset(&resp, 0, sizeof resp);
+
+ addr = (struct sockaddr *) &ctx->cm_id->route.addr.src_addr;
+ resp.src_size = rdma_addr_size(addr);
+ memcpy(&resp.src_addr, addr, resp.src_size);
+
+ addr = (struct sockaddr *) &ctx->cm_id->route.addr.dst_addr;
+ resp.dst_size = rdma_addr_size(addr);
+ memcpy(&resp.dst_addr, addr, resp.dst_size);
+
+ ucma_query_device_addr(ctx->cm_id, &resp);
+
+ if (copy_to_user(response, &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+static ssize_t ucma_query_path(struct ucma_context *ctx,
+ void __user *response, int out_len)
+{
+ struct rdma_ucm_query_path_resp *resp;
+ int i, ret = 0;
+
+ if (out_len < sizeof(*resp))
+ return -ENOSPC;
+
+ resp = kzalloc(out_len, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ resp->num_paths = ctx->cm_id->route.num_paths;
+ for (i = 0, out_len -= sizeof(*resp);
+ i < resp->num_paths && out_len > sizeof(struct ib_path_rec_data);
+ i++, out_len -= sizeof(struct ib_path_rec_data)) {
+
+ resp->path_data[i].flags = IB_PATH_GMP | IB_PATH_PRIMARY |
+ IB_PATH_BIDIRECTIONAL;
+ ib_sa_pack_path(&ctx->cm_id->route.path_rec[i],
+ &resp->path_data[i].path_rec);
+ }
+
+ if (copy_to_user(response, resp,
+ sizeof(*resp) + (i * sizeof(struct ib_path_rec_data))))
+ ret = -EFAULT;
+
+ kfree(resp);
+ return ret;
+}
+
+static ssize_t ucma_query_gid(struct ucma_context *ctx,
+ void __user *response, int out_len)
+{
+ struct rdma_ucm_query_addr_resp resp;
+ struct sockaddr_ib *addr;
+ int ret = 0;
+
+ if (out_len < sizeof(resp))
+ return -ENOSPC;
+
+ memset(&resp, 0, sizeof resp);
+
+ ucma_query_device_addr(ctx->cm_id, &resp);
+
+ addr = (struct sockaddr_ib *) &resp.src_addr;
+ resp.src_size = sizeof(*addr);
+ if (ctx->cm_id->route.addr.src_addr.ss_family == AF_IB) {
+ memcpy(addr, &ctx->cm_id->route.addr.src_addr, resp.src_size);
+ } else {
+ addr->sib_family = AF_IB;
+ addr->sib_pkey = (__force __be16) resp.pkey;
+ rdma_addr_get_sgid(&ctx->cm_id->route.addr.dev_addr,
+ (union ib_gid *) &addr->sib_addr);
+ addr->sib_sid = rdma_get_service_id(ctx->cm_id, (struct sockaddr *)
+ &ctx->cm_id->route.addr.src_addr);
+ }
+
+ addr = (struct sockaddr_ib *) &resp.dst_addr;
+ resp.dst_size = sizeof(*addr);
+ if (ctx->cm_id->route.addr.dst_addr.ss_family == AF_IB) {
+ memcpy(addr, &ctx->cm_id->route.addr.dst_addr, resp.dst_size);
+ } else {
+ addr->sib_family = AF_IB;
+ addr->sib_pkey = (__force __be16) resp.pkey;
+ rdma_addr_get_dgid(&ctx->cm_id->route.addr.dev_addr,
+ (union ib_gid *) &addr->sib_addr);
+ addr->sib_sid = rdma_get_service_id(ctx->cm_id, (struct sockaddr *)
+ &ctx->cm_id->route.addr.dst_addr);
+ }
+
+ if (copy_to_user(response, &resp, sizeof(resp)))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+static ssize_t ucma_query(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_query cmd;
+ struct ucma_context *ctx;
+ void __user *response;
+ int ret;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ response = (void __user *)(unsigned long) cmd.response;
+ ctx = ucma_get_ctx(file, cmd.id);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ switch (cmd.option) {
+ case RDMA_USER_CM_QUERY_ADDR:
+ ret = ucma_query_addr(ctx, response, out_len);
+ break;
+ case RDMA_USER_CM_QUERY_PATH:
+ ret = ucma_query_path(ctx, response, out_len);
+ break;
+ case RDMA_USER_CM_QUERY_GID:
+ ret = ucma_query_gid(ctx, response, out_len);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ ucma_put_ctx(ctx);
+ return ret;
+}
+
+static void ucma_copy_conn_param(struct rdma_cm_id *id,
+ struct rdma_conn_param *dst,
struct rdma_ucm_conn_param *src)
{
dst->private_data = src->private_data;
@@ -721,6 +929,7 @@
dst->rnr_retry_count = src->rnr_retry_count;
dst->srq = src->srq;
dst->qp_num = src->qp_num;
+ dst->qkey = (id->route.addr.src_addr.ss_family == AF_IB) ? src->qkey : 0;
}
static ssize_t ucma_connect(struct ucma_file *file, const char __user *inbuf,
@@ -741,7 +950,7 @@
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ucma_copy_conn_param(&conn_param, &cmd.conn_param);
+ ucma_copy_conn_param(ctx->cm_id, &conn_param, &cmd.conn_param);
ret = rdma_connect(ctx->cm_id, &conn_param);
ucma_put_ctx(ctx);
return ret;
@@ -784,7 +993,7 @@
return PTR_ERR(ctx);
if (cmd.conn_param.valid) {
- ucma_copy_conn_param(&conn_param, &cmd.conn_param);
+ ucma_copy_conn_param(ctx->cm_id, &conn_param, &cmd.conn_param);
mutex_lock(&file->mut);
ret = rdma_accept(ctx->cm_id, &conn_param);
if (!ret)
@@ -1020,23 +1229,23 @@
return ret;
}
-static ssize_t ucma_join_multicast(struct ucma_file *file,
- const char __user *inbuf,
- int in_len, int out_len)
+static ssize_t ucma_process_join(struct ucma_file *file,
+ struct rdma_ucm_join_mcast *cmd, int out_len)
{
- struct rdma_ucm_join_mcast cmd;
struct rdma_ucm_create_id_resp resp;
struct ucma_context *ctx;
struct ucma_multicast *mc;
+ struct sockaddr *addr;
int ret;
if (out_len < sizeof(resp))
return -ENOSPC;
- if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
- return -EFAULT;
+ addr = (struct sockaddr *) &cmd->addr;
+ if (cmd->reserved || !cmd->addr_size || (cmd->addr_size != rdma_addr_size(addr)))
+ return -EINVAL;
- ctx = ucma_get_ctx(file, cmd.id);
+ ctx = ucma_get_ctx(file, cmd->id);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
@@ -1047,14 +1256,14 @@
goto err1;
}
- mc->uid = cmd.uid;
- memcpy(&mc->addr, &cmd.addr, sizeof cmd.addr);
+ mc->uid = cmd->uid;
+ memcpy(&mc->addr, addr, cmd->addr_size);
ret = rdma_join_multicast(ctx->cm_id, (struct sockaddr *) &mc->addr, mc);
if (ret)
goto err2;
resp.id = mc->id;
- if (copy_to_user((void __user *)(unsigned long)cmd.response,
+ if (copy_to_user((void __user *)(unsigned long) cmd->response,
&resp, sizeof(resp))) {
ret = -EFAULT;
goto err3;
@@ -1079,6 +1288,38 @@
return ret;
}
+static ssize_t ucma_join_ip_multicast(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_join_ip_mcast cmd;
+ struct rdma_ucm_join_mcast join_cmd;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ join_cmd.response = cmd.response;
+ join_cmd.uid = cmd.uid;
+ join_cmd.id = cmd.id;
+ join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr);
+ join_cmd.reserved = 0;
+ memcpy(&join_cmd.addr, &cmd.addr, join_cmd.addr_size);
+
+ return ucma_process_join(file, &join_cmd, out_len);
+}
+
+static ssize_t ucma_join_multicast(struct ucma_file *file,
+ const char __user *inbuf,
+ int in_len, int out_len)
+{
+ struct rdma_ucm_join_mcast cmd;
+
+ if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
+ return -EFAULT;
+
+ return ucma_process_join(file, &cmd, out_len);
+}
+
static ssize_t ucma_leave_multicast(struct ucma_file *file,
const char __user *inbuf,
int in_len, int out_len)
@@ -1221,25 +1462,29 @@
static ssize_t (*ucma_cmd_table[])(struct ucma_file *file,
const char __user *inbuf,
int in_len, int out_len) = {
- [RDMA_USER_CM_CMD_CREATE_ID] = ucma_create_id,
- [RDMA_USER_CM_CMD_DESTROY_ID] = ucma_destroy_id,
- [RDMA_USER_CM_CMD_BIND_ADDR] = ucma_bind_addr,
- [RDMA_USER_CM_CMD_RESOLVE_ADDR] = ucma_resolve_addr,
- [RDMA_USER_CM_CMD_RESOLVE_ROUTE]= ucma_resolve_route,
- [RDMA_USER_CM_CMD_QUERY_ROUTE] = ucma_query_route,
- [RDMA_USER_CM_CMD_CONNECT] = ucma_connect,
- [RDMA_USER_CM_CMD_LISTEN] = ucma_listen,
- [RDMA_USER_CM_CMD_ACCEPT] = ucma_accept,
- [RDMA_USER_CM_CMD_REJECT] = ucma_reject,
- [RDMA_USER_CM_CMD_DISCONNECT] = ucma_disconnect,
- [RDMA_USER_CM_CMD_INIT_QP_ATTR] = ucma_init_qp_attr,
- [RDMA_USER_CM_CMD_GET_EVENT] = ucma_get_event,
- [RDMA_USER_CM_CMD_GET_OPTION] = NULL,
- [RDMA_USER_CM_CMD_SET_OPTION] = ucma_set_option,
- [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify,
- [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast,
- [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast,
- [RDMA_USER_CM_CMD_MIGRATE_ID] = ucma_migrate_id
+ [RDMA_USER_CM_CMD_CREATE_ID] = ucma_create_id,
+ [RDMA_USER_CM_CMD_DESTROY_ID] = ucma_destroy_id,
+ [RDMA_USER_CM_CMD_BIND_IP] = ucma_bind_ip,
+ [RDMA_USER_CM_CMD_RESOLVE_IP] = ucma_resolve_ip,
+ [RDMA_USER_CM_CMD_RESOLVE_ROUTE] = ucma_resolve_route,
+ [RDMA_USER_CM_CMD_QUERY_ROUTE] = ucma_query_route,
+ [RDMA_USER_CM_CMD_CONNECT] = ucma_connect,
+ [RDMA_USER_CM_CMD_LISTEN] = ucma_listen,
+ [RDMA_USER_CM_CMD_ACCEPT] = ucma_accept,
+ [RDMA_USER_CM_CMD_REJECT] = ucma_reject,
+ [RDMA_USER_CM_CMD_DISCONNECT] = ucma_disconnect,
+ [RDMA_USER_CM_CMD_INIT_QP_ATTR] = ucma_init_qp_attr,
+ [RDMA_USER_CM_CMD_GET_EVENT] = ucma_get_event,
+ [RDMA_USER_CM_CMD_GET_OPTION] = NULL,
+ [RDMA_USER_CM_CMD_SET_OPTION] = ucma_set_option,
+ [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify,
+ [RDMA_USER_CM_CMD_JOIN_IP_MCAST] = ucma_join_ip_multicast,
+ [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast,
+ [RDMA_USER_CM_CMD_MIGRATE_ID] = ucma_migrate_id,
+ [RDMA_USER_CM_CMD_QUERY] = ucma_query,
+ [RDMA_USER_CM_CMD_BIND] = ucma_bind,
+ [RDMA_USER_CM_CMD_RESOLVE_ADDR] = ucma_resolve_addr,
+ [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast
};
static ssize_t ucma_write(struct file *filp, const char __user *buf,
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index a7d00f6..b3c07b0 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -334,7 +334,7 @@
resp.num_comp_vectors = file->device->num_comp_vectors;
- ret = get_unused_fd();
+ ret = get_unused_fd_flags(O_CLOEXEC);
if (ret < 0)
goto err_free;
resp.async_fd = ret;
@@ -1184,7 +1184,7 @@
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
- ret = get_unused_fd();
+ ret = get_unused_fd_flags(O_CLOEXEC);
if (ret < 0)
return ret;
resp.fd = ret;
diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c
index e5649e8..b57c0be 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_qp.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c
@@ -883,7 +883,8 @@
{
union t3_wr *wqe = qhp->wq.queue;
u16 count = 0;
- while ((count+1) != 0 && fw_riwrh_opcode((struct fw_riwrh *)wqe) == T3_WR_RCV) {
+
+ while (count < USHRT_MAX && fw_riwrh_opcode((struct fw_riwrh *)wqe) == T3_WR_RCV) {
count++;
wqe++;
}
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 982e3ef..cd8d290 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -211,6 +211,7 @@
if (!ctblk_cache) {
ehca_gen_err("Cannot create ctblk SLAB cache.");
ehca_cleanup_small_qp_cache();
+ ret = -ENOMEM;
goto create_slab_caches6;
}
#endif
diff --git a/drivers/infiniband/hw/mlx5/Kconfig b/drivers/infiniband/hw/mlx5/Kconfig
new file mode 100644
index 0000000..8e6aebf
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/Kconfig
@@ -0,0 +1,10 @@
+config MLX5_INFINIBAND
+ tristate "Mellanox Connect-IB HCA support"
+ depends on NETDEVICES && ETHERNET && PCI && X86
+ select NET_VENDOR_MELLANOX
+ select MLX5_CORE
+ ---help---
+ This driver provides low-level InfiniBand support for
+ Mellanox Connect-IB PCI Express host channel adapters (HCAs).
+ This is required to use InfiniBand protocols such as
+ IP-over-IB or SRP with these devices.
diff --git a/drivers/infiniband/hw/mlx5/Makefile b/drivers/infiniband/hw/mlx5/Makefile
new file mode 100644
index 0000000..4ea0135
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MLX5_INFINIBAND) += mlx5_ib.o
+
+mlx5_ib-y := main.o cq.o doorbell.o qp.o mem.o srq.o mr.o ah.o mad.o
diff --git a/drivers/infiniband/hw/mlx5/ah.c b/drivers/infiniband/hw/mlx5/ah.c
new file mode 100644
index 0000000..39ab0ca
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/ah.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include "mlx5_ib.h"
+
+struct ib_ah *create_ib_ah(struct ib_ah_attr *ah_attr,
+ struct mlx5_ib_ah *ah)
+{
+ if (ah_attr->ah_flags & IB_AH_GRH) {
+ memcpy(ah->av.rgid, &ah_attr->grh.dgid, 16);
+ ah->av.grh_gid_fl = cpu_to_be32(ah_attr->grh.flow_label |
+ (1 << 30) |
+ ah_attr->grh.sgid_index << 20);
+ ah->av.hop_limit = ah_attr->grh.hop_limit;
+ ah->av.tclass = ah_attr->grh.traffic_class;
+ }
+
+ ah->av.rlid = cpu_to_be16(ah_attr->dlid);
+ ah->av.fl_mlid = ah_attr->src_path_bits & 0x7f;
+ ah->av.stat_rate_sl = (ah_attr->static_rate << 4) | (ah_attr->sl & 0xf);
+
+ return &ah->ibah;
+}
+
+struct ib_ah *mlx5_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+ struct mlx5_ib_ah *ah;
+
+ ah = kzalloc(sizeof(*ah), GFP_ATOMIC);
+ if (!ah)
+ return ERR_PTR(-ENOMEM);
+
+ return create_ib_ah(ah_attr, ah); /* never fails */
+}
+
+int mlx5_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr)
+{
+ struct mlx5_ib_ah *ah = to_mah(ibah);
+ u32 tmp;
+
+ memset(ah_attr, 0, sizeof(*ah_attr));
+
+ tmp = be32_to_cpu(ah->av.grh_gid_fl);
+ if (tmp & (1 << 30)) {
+ ah_attr->ah_flags = IB_AH_GRH;
+ ah_attr->grh.sgid_index = (tmp >> 20) & 0xff;
+ ah_attr->grh.flow_label = tmp & 0xfffff;
+ memcpy(&ah_attr->grh.dgid, ah->av.rgid, 16);
+ ah_attr->grh.hop_limit = ah->av.hop_limit;
+ ah_attr->grh.traffic_class = ah->av.tclass;
+ }
+ ah_attr->dlid = be16_to_cpu(ah->av.rlid);
+ ah_attr->static_rate = ah->av.stat_rate_sl >> 4;
+ ah_attr->sl = ah->av.stat_rate_sl & 0xf;
+
+ return 0;
+}
+
+int mlx5_ib_destroy_ah(struct ib_ah *ah)
+{
+ kfree(to_mah(ah));
+ return 0;
+}
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
new file mode 100644
index 0000000..344ab03
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kref.h>
+#include <rdma/ib_umem.h>
+#include "mlx5_ib.h"
+#include "user.h"
+
+static void mlx5_ib_cq_comp(struct mlx5_core_cq *cq)
+{
+ struct ib_cq *ibcq = &to_mibcq(cq)->ibcq;
+
+ ibcq->comp_handler(ibcq, ibcq->cq_context);
+}
+
+static void mlx5_ib_cq_event(struct mlx5_core_cq *mcq, enum mlx5_event type)
+{
+ struct mlx5_ib_cq *cq = container_of(mcq, struct mlx5_ib_cq, mcq);
+ struct mlx5_ib_dev *dev = to_mdev(cq->ibcq.device);
+ struct ib_cq *ibcq = &cq->ibcq;
+ struct ib_event event;
+
+ if (type != MLX5_EVENT_TYPE_CQ_ERROR) {
+ mlx5_ib_warn(dev, "Unexpected event type %d on CQ %06x\n",
+ type, mcq->cqn);
+ return;
+ }
+
+ if (ibcq->event_handler) {
+ event.device = &dev->ib_dev;
+ event.event = IB_EVENT_CQ_ERR;
+ event.element.cq = ibcq;
+ ibcq->event_handler(&event, ibcq->cq_context);
+ }
+}
+
+static void *get_cqe_from_buf(struct mlx5_ib_cq_buf *buf, int n, int size)
+{
+ return mlx5_buf_offset(&buf->buf, n * size);
+}
+
+static void *get_cqe(struct mlx5_ib_cq *cq, int n)
+{
+ return get_cqe_from_buf(&cq->buf, n, cq->mcq.cqe_sz);
+}
+
+static void *get_sw_cqe(struct mlx5_ib_cq *cq, int n)
+{
+ void *cqe = get_cqe(cq, n & cq->ibcq.cqe);
+ struct mlx5_cqe64 *cqe64;
+
+ cqe64 = (cq->mcq.cqe_sz == 64) ? cqe : cqe + 64;
+ return ((cqe64->op_own & MLX5_CQE_OWNER_MASK) ^
+ !!(n & (cq->ibcq.cqe + 1))) ? NULL : cqe;
+}
+
+static void *next_cqe_sw(struct mlx5_ib_cq *cq)
+{
+ return get_sw_cqe(cq, cq->mcq.cons_index);
+}
+
+static enum ib_wc_opcode get_umr_comp(struct mlx5_ib_wq *wq, int idx)
+{
+ switch (wq->wr_data[idx]) {
+ case MLX5_IB_WR_UMR:
+ return 0;
+
+ case IB_WR_LOCAL_INV:
+ return IB_WC_LOCAL_INV;
+
+ case IB_WR_FAST_REG_MR:
+ return IB_WC_FAST_REG_MR;
+
+ default:
+ pr_warn("unknown completion status\n");
+ return 0;
+ }
+}
+
+static void handle_good_req(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
+ struct mlx5_ib_wq *wq, int idx)
+{
+ wc->wc_flags = 0;
+ switch (be32_to_cpu(cqe->sop_drop_qpn) >> 24) {
+ case MLX5_OPCODE_RDMA_WRITE_IMM:
+ wc->wc_flags |= IB_WC_WITH_IMM;
+ case MLX5_OPCODE_RDMA_WRITE:
+ wc->opcode = IB_WC_RDMA_WRITE;
+ break;
+ case MLX5_OPCODE_SEND_IMM:
+ wc->wc_flags |= IB_WC_WITH_IMM;
+ case MLX5_OPCODE_SEND:
+ case MLX5_OPCODE_SEND_INVAL:
+ wc->opcode = IB_WC_SEND;
+ break;
+ case MLX5_OPCODE_RDMA_READ:
+ wc->opcode = IB_WC_RDMA_READ;
+ wc->byte_len = be32_to_cpu(cqe->byte_cnt);
+ break;
+ case MLX5_OPCODE_ATOMIC_CS:
+ wc->opcode = IB_WC_COMP_SWAP;
+ wc->byte_len = 8;
+ break;
+ case MLX5_OPCODE_ATOMIC_FA:
+ wc->opcode = IB_WC_FETCH_ADD;
+ wc->byte_len = 8;
+ break;
+ case MLX5_OPCODE_ATOMIC_MASKED_CS:
+ wc->opcode = IB_WC_MASKED_COMP_SWAP;
+ wc->byte_len = 8;
+ break;
+ case MLX5_OPCODE_ATOMIC_MASKED_FA:
+ wc->opcode = IB_WC_MASKED_FETCH_ADD;
+ wc->byte_len = 8;
+ break;
+ case MLX5_OPCODE_BIND_MW:
+ wc->opcode = IB_WC_BIND_MW;
+ break;
+ case MLX5_OPCODE_UMR:
+ wc->opcode = get_umr_comp(wq, idx);
+ break;
+ }
+}
+
+enum {
+ MLX5_GRH_IN_BUFFER = 1,
+ MLX5_GRH_IN_CQE = 2,
+};
+
+static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
+ struct mlx5_ib_qp *qp)
+{
+ struct mlx5_ib_dev *dev = to_mdev(qp->ibqp.device);
+ struct mlx5_ib_srq *srq;
+ struct mlx5_ib_wq *wq;
+ u16 wqe_ctr;
+ u8 g;
+
+ if (qp->ibqp.srq || qp->ibqp.xrcd) {
+ struct mlx5_core_srq *msrq = NULL;
+
+ if (qp->ibqp.xrcd) {
+ msrq = mlx5_core_get_srq(&dev->mdev,
+ be32_to_cpu(cqe->srqn));
+ srq = to_mibsrq(msrq);
+ } else {
+ srq = to_msrq(qp->ibqp.srq);
+ }
+ if (srq) {
+ wqe_ctr = be16_to_cpu(cqe->wqe_counter);
+ wc->wr_id = srq->wrid[wqe_ctr];
+ mlx5_ib_free_srq_wqe(srq, wqe_ctr);
+ if (msrq && atomic_dec_and_test(&msrq->refcount))
+ complete(&msrq->free);
+ }
+ } else {
+ wq = &qp->rq;
+ wc->wr_id = wq->wrid[wq->tail & (wq->wqe_cnt - 1)];
+ ++wq->tail;
+ }
+ wc->byte_len = be32_to_cpu(cqe->byte_cnt);
+
+ switch (cqe->op_own >> 4) {
+ case MLX5_CQE_RESP_WR_IMM:
+ wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+ wc->wc_flags = IB_WC_WITH_IMM;
+ wc->ex.imm_data = cqe->imm_inval_pkey;
+ break;
+ case MLX5_CQE_RESP_SEND:
+ wc->opcode = IB_WC_RECV;
+ wc->wc_flags = 0;
+ break;
+ case MLX5_CQE_RESP_SEND_IMM:
+ wc->opcode = IB_WC_RECV;
+ wc->wc_flags = IB_WC_WITH_IMM;
+ wc->ex.imm_data = cqe->imm_inval_pkey;
+ break;
+ case MLX5_CQE_RESP_SEND_INV:
+ wc->opcode = IB_WC_RECV;
+ wc->wc_flags = IB_WC_WITH_INVALIDATE;
+ wc->ex.invalidate_rkey = be32_to_cpu(cqe->imm_inval_pkey);
+ break;
+ }
+ wc->slid = be16_to_cpu(cqe->slid);
+ wc->sl = (be32_to_cpu(cqe->flags_rqpn) >> 24) & 0xf;
+ wc->src_qp = be32_to_cpu(cqe->flags_rqpn) & 0xffffff;
+ wc->dlid_path_bits = cqe->ml_path;
+ g = (be32_to_cpu(cqe->flags_rqpn) >> 28) & 3;
+ wc->wc_flags |= g ? IB_WC_GRH : 0;
+ wc->pkey_index = be32_to_cpu(cqe->imm_inval_pkey) & 0xffff;
+}
+
+static void dump_cqe(struct mlx5_ib_dev *dev, struct mlx5_err_cqe *cqe)
+{
+ __be32 *p = (__be32 *)cqe;
+ int i;
+
+ mlx5_ib_warn(dev, "dump error cqe\n");
+ for (i = 0; i < sizeof(*cqe) / 16; i++, p += 4)
+ pr_info("%08x %08x %08x %08x\n", be32_to_cpu(p[0]),
+ be32_to_cpu(p[1]), be32_to_cpu(p[2]),
+ be32_to_cpu(p[3]));
+}
+
+static void mlx5_handle_error_cqe(struct mlx5_ib_dev *dev,
+ struct mlx5_err_cqe *cqe,
+ struct ib_wc *wc)
+{
+ int dump = 1;
+
+ switch (cqe->syndrome) {
+ case MLX5_CQE_SYNDROME_LOCAL_LENGTH_ERR:
+ wc->status = IB_WC_LOC_LEN_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_LOCAL_QP_OP_ERR:
+ wc->status = IB_WC_LOC_QP_OP_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_LOCAL_PROT_ERR:
+ wc->status = IB_WC_LOC_PROT_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_WR_FLUSH_ERR:
+ dump = 0;
+ wc->status = IB_WC_WR_FLUSH_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_MW_BIND_ERR:
+ wc->status = IB_WC_MW_BIND_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_BAD_RESP_ERR:
+ wc->status = IB_WC_BAD_RESP_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_LOCAL_ACCESS_ERR:
+ wc->status = IB_WC_LOC_ACCESS_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_REMOTE_INVAL_REQ_ERR:
+ wc->status = IB_WC_REM_INV_REQ_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_REMOTE_ACCESS_ERR:
+ wc->status = IB_WC_REM_ACCESS_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_REMOTE_OP_ERR:
+ wc->status = IB_WC_REM_OP_ERR;
+ break;
+ case MLX5_CQE_SYNDROME_TRANSPORT_RETRY_EXC_ERR:
+ wc->status = IB_WC_RETRY_EXC_ERR;
+ dump = 0;
+ break;
+ case MLX5_CQE_SYNDROME_RNR_RETRY_EXC_ERR:
+ wc->status = IB_WC_RNR_RETRY_EXC_ERR;
+ dump = 0;
+ break;
+ case MLX5_CQE_SYNDROME_REMOTE_ABORTED_ERR:
+ wc->status = IB_WC_REM_ABORT_ERR;
+ break;
+ default:
+ wc->status = IB_WC_GENERAL_ERR;
+ break;
+ }
+
+ wc->vendor_err = cqe->vendor_err_synd;
+ if (dump)
+ dump_cqe(dev, cqe);
+}
+
+static int is_atomic_response(struct mlx5_ib_qp *qp, uint16_t idx)
+{
+ /* TBD: waiting decision
+ */
+ return 0;
+}
+
+static void *mlx5_get_atomic_laddr(struct mlx5_ib_qp *qp, uint16_t idx)
+{
+ struct mlx5_wqe_data_seg *dpseg;
+ void *addr;
+
+ dpseg = mlx5_get_send_wqe(qp, idx) + sizeof(struct mlx5_wqe_ctrl_seg) +
+ sizeof(struct mlx5_wqe_raddr_seg) +
+ sizeof(struct mlx5_wqe_atomic_seg);
+ addr = (void *)(unsigned long)be64_to_cpu(dpseg->addr);
+ return addr;
+}
+
+static void handle_atomic(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64,
+ uint16_t idx)
+{
+ void *addr;
+ int byte_count;
+ int i;
+
+ if (!is_atomic_response(qp, idx))
+ return;
+
+ byte_count = be32_to_cpu(cqe64->byte_cnt);
+ addr = mlx5_get_atomic_laddr(qp, idx);
+
+ if (byte_count == 4) {
+ *(uint32_t *)addr = be32_to_cpu(*((__be32 *)addr));
+ } else {
+ for (i = 0; i < byte_count; i += 8) {
+ *(uint64_t *)addr = be64_to_cpu(*((__be64 *)addr));
+ addr += 8;
+ }
+ }
+
+ return;
+}
+
+static void handle_atomics(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64,
+ u16 tail, u16 head)
+{
+ int idx;
+
+ do {
+ idx = tail & (qp->sq.wqe_cnt - 1);
+ handle_atomic(qp, cqe64, idx);
+ if (idx == head)
+ break;
+
+ tail = qp->sq.w_list[idx].next;
+ } while (1);
+ tail = qp->sq.w_list[idx].next;
+ qp->sq.last_poll = tail;
+}
+
+static int mlx5_poll_one(struct mlx5_ib_cq *cq,
+ struct mlx5_ib_qp **cur_qp,
+ struct ib_wc *wc)
+{
+ struct mlx5_ib_dev *dev = to_mdev(cq->ibcq.device);
+ struct mlx5_err_cqe *err_cqe;
+ struct mlx5_cqe64 *cqe64;
+ struct mlx5_core_qp *mqp;
+ struct mlx5_ib_wq *wq;
+ uint8_t opcode;
+ uint32_t qpn;
+ u16 wqe_ctr;
+ void *cqe;
+ int idx;
+
+ cqe = next_cqe_sw(cq);
+ if (!cqe)
+ return -EAGAIN;
+
+ cqe64 = (cq->mcq.cqe_sz == 64) ? cqe : cqe + 64;
+
+ ++cq->mcq.cons_index;
+
+ /* Make sure we read CQ entry contents after we've checked the
+ * ownership bit.
+ */
+ rmb();
+
+ /* TBD: resize CQ */
+
+ qpn = ntohl(cqe64->sop_drop_qpn) & 0xffffff;
+ if (!*cur_qp || (qpn != (*cur_qp)->ibqp.qp_num)) {
+ /* We do not have to take the QP table lock here,
+ * because CQs will be locked while QPs are removed
+ * from the table.
+ */
+ mqp = __mlx5_qp_lookup(&dev->mdev, qpn);
+ if (unlikely(!mqp)) {
+ mlx5_ib_warn(dev, "CQE@CQ %06x for unknown QPN %6x\n",
+ cq->mcq.cqn, qpn);
+ return -EINVAL;
+ }
+
+ *cur_qp = to_mibqp(mqp);
+ }
+
+ wc->qp = &(*cur_qp)->ibqp;
+ opcode = cqe64->op_own >> 4;
+ switch (opcode) {
+ case MLX5_CQE_REQ:
+ wq = &(*cur_qp)->sq;
+ wqe_ctr = be16_to_cpu(cqe64->wqe_counter);
+ idx = wqe_ctr & (wq->wqe_cnt - 1);
+ handle_good_req(wc, cqe64, wq, idx);
+ handle_atomics(*cur_qp, cqe64, wq->last_poll, idx);
+ wc->wr_id = wq->wrid[idx];
+ wq->tail = wq->wqe_head[idx] + 1;
+ wc->status = IB_WC_SUCCESS;
+ break;
+ case MLX5_CQE_RESP_WR_IMM:
+ case MLX5_CQE_RESP_SEND:
+ case MLX5_CQE_RESP_SEND_IMM:
+ case MLX5_CQE_RESP_SEND_INV:
+ handle_responder(wc, cqe64, *cur_qp);
+ wc->status = IB_WC_SUCCESS;
+ break;
+ case MLX5_CQE_RESIZE_CQ:
+ break;
+ case MLX5_CQE_REQ_ERR:
+ case MLX5_CQE_RESP_ERR:
+ err_cqe = (struct mlx5_err_cqe *)cqe64;
+ mlx5_handle_error_cqe(dev, err_cqe, wc);
+ mlx5_ib_dbg(dev, "%s error cqe on cqn 0x%x:\n",
+ opcode == MLX5_CQE_REQ_ERR ?
+ "Requestor" : "Responder", cq->mcq.cqn);
+ mlx5_ib_dbg(dev, "syndrome 0x%x, vendor syndrome 0x%x\n",
+ err_cqe->syndrome, err_cqe->vendor_err_synd);
+ if (opcode == MLX5_CQE_REQ_ERR) {
+ wq = &(*cur_qp)->sq;
+ wqe_ctr = be16_to_cpu(cqe64->wqe_counter);
+ idx = wqe_ctr & (wq->wqe_cnt - 1);
+ wc->wr_id = wq->wrid[idx];
+ wq->tail = wq->wqe_head[idx] + 1;
+ } else {
+ struct mlx5_ib_srq *srq;
+
+ if ((*cur_qp)->ibqp.srq) {
+ srq = to_msrq((*cur_qp)->ibqp.srq);
+ wqe_ctr = be16_to_cpu(cqe64->wqe_counter);
+ wc->wr_id = srq->wrid[wqe_ctr];
+ mlx5_ib_free_srq_wqe(srq, wqe_ctr);
+ } else {
+ wq = &(*cur_qp)->rq;
+ wc->wr_id = wq->wrid[wq->tail & (wq->wqe_cnt - 1)];
+ ++wq->tail;
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
+{
+ struct mlx5_ib_cq *cq = to_mcq(ibcq);
+ struct mlx5_ib_qp *cur_qp = NULL;
+ unsigned long flags;
+ int npolled;
+ int err = 0;
+
+ spin_lock_irqsave(&cq->lock, flags);
+
+ for (npolled = 0; npolled < num_entries; npolled++) {
+ err = mlx5_poll_one(cq, &cur_qp, wc + npolled);
+ if (err)
+ break;
+ }
+
+ if (npolled)
+ mlx5_cq_set_ci(&cq->mcq);
+
+ spin_unlock_irqrestore(&cq->lock, flags);
+
+ if (err == 0 || err == -EAGAIN)
+ return npolled;
+ else
+ return err;
+}
+
+int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
+{
+ mlx5_cq_arm(&to_mcq(ibcq)->mcq,
+ (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ?
+ MLX5_CQ_DB_REQ_NOT_SOL : MLX5_CQ_DB_REQ_NOT,
+ to_mdev(ibcq->device)->mdev.priv.uuari.uars[0].map,
+ MLX5_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->mdev.priv.cq_uar_lock));
+
+ return 0;
+}
+
+static int alloc_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf,
+ int nent, int cqe_size)
+{
+ int err;
+
+ err = mlx5_buf_alloc(&dev->mdev, nent * cqe_size,
+ PAGE_SIZE * 2, &buf->buf);
+ if (err)
+ return err;
+
+ buf->cqe_size = cqe_size;
+
+ return 0;
+}
+
+static void free_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf)
+{
+ mlx5_buf_free(&dev->mdev, &buf->buf);
+}
+
+static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata,
+ struct ib_ucontext *context, struct mlx5_ib_cq *cq,
+ int entries, struct mlx5_create_cq_mbox_in **cqb,
+ int *cqe_size, int *index, int *inlen)
+{
+ struct mlx5_ib_create_cq ucmd;
+ int page_shift;
+ int npages;
+ int ncont;
+ int err;
+
+ if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd)))
+ return -EFAULT;
+
+ if (ucmd.cqe_size != 64 && ucmd.cqe_size != 128)
+ return -EINVAL;
+
+ *cqe_size = ucmd.cqe_size;
+
+ cq->buf.umem = ib_umem_get(context, ucmd.buf_addr,
+ entries * ucmd.cqe_size,
+ IB_ACCESS_LOCAL_WRITE, 1);
+ if (IS_ERR(cq->buf.umem)) {
+ err = PTR_ERR(cq->buf.umem);
+ return err;
+ }
+
+ err = mlx5_ib_db_map_user(to_mucontext(context), ucmd.db_addr,
+ &cq->db);
+ if (err)
+ goto err_umem;
+
+ mlx5_ib_cont_pages(cq->buf.umem, ucmd.buf_addr, &npages, &page_shift,
+ &ncont, NULL);
+ mlx5_ib_dbg(dev, "addr 0x%llx, size %u, npages %d, page_shift %d, ncont %d\n",
+ ucmd.buf_addr, entries * ucmd.cqe_size, npages, page_shift, ncont);
+
+ *inlen = sizeof(**cqb) + sizeof(*(*cqb)->pas) * ncont;
+ *cqb = mlx5_vzalloc(*inlen);
+ if (!*cqb) {
+ err = -ENOMEM;
+ goto err_db;
+ }
+ mlx5_ib_populate_pas(dev, cq->buf.umem, page_shift, (*cqb)->pas, 0);
+ (*cqb)->ctx.log_pg_sz = page_shift - PAGE_SHIFT;
+
+ *index = to_mucontext(context)->uuari.uars[0].index;
+
+ return 0;
+
+err_db:
+ mlx5_ib_db_unmap_user(to_mucontext(context), &cq->db);
+
+err_umem:
+ ib_umem_release(cq->buf.umem);
+ return err;
+}
+
+static void destroy_cq_user(struct mlx5_ib_cq *cq, struct ib_ucontext *context)
+{
+ mlx5_ib_db_unmap_user(to_mucontext(context), &cq->db);
+ ib_umem_release(cq->buf.umem);
+}
+
+static void init_cq_buf(struct mlx5_ib_cq *cq, int nent)
+{
+ int i;
+ void *cqe;
+ struct mlx5_cqe64 *cqe64;
+
+ for (i = 0; i < nent; i++) {
+ cqe = get_cqe(cq, i);
+ cqe64 = (cq->buf.cqe_size == 64) ? cqe : cqe + 64;
+ cqe64->op_own = 0xf1;
+ }
+}
+
+static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq,
+ int entries, int cqe_size,
+ struct mlx5_create_cq_mbox_in **cqb,
+ int *index, int *inlen)
+{
+ int err;
+
+ err = mlx5_db_alloc(&dev->mdev, &cq->db);
+ if (err)
+ return err;
+
+ cq->mcq.set_ci_db = cq->db.db;
+ cq->mcq.arm_db = cq->db.db + 1;
+ *cq->mcq.set_ci_db = 0;
+ *cq->mcq.arm_db = 0;
+ cq->mcq.cqe_sz = cqe_size;
+
+ err = alloc_cq_buf(dev, &cq->buf, entries, cqe_size);
+ if (err)
+ goto err_db;
+
+ init_cq_buf(cq, entries);
+
+ *inlen = sizeof(**cqb) + sizeof(*(*cqb)->pas) * cq->buf.buf.npages;
+ *cqb = mlx5_vzalloc(*inlen);
+ if (!*cqb) {
+ err = -ENOMEM;
+ goto err_buf;
+ }
+ mlx5_fill_page_array(&cq->buf.buf, (*cqb)->pas);
+
+ (*cqb)->ctx.log_pg_sz = cq->buf.buf.page_shift - PAGE_SHIFT;
+ *index = dev->mdev.priv.uuari.uars[0].index;
+
+ return 0;
+
+err_buf:
+ free_cq_buf(dev, &cq->buf);
+
+err_db:
+ mlx5_db_free(&dev->mdev, &cq->db);
+ return err;
+}
+
+static void destroy_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq)
+{
+ free_cq_buf(dev, &cq->buf);
+ mlx5_db_free(&dev->mdev, &cq->db);
+}
+
+struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
+ int vector, struct ib_ucontext *context,
+ struct ib_udata *udata)
+{
+ struct mlx5_create_cq_mbox_in *cqb = NULL;
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct mlx5_ib_cq *cq;
+ int uninitialized_var(index);
+ int uninitialized_var(inlen);
+ int cqe_size;
+ int irqn;
+ int eqn;
+ int err;
+
+ entries = roundup_pow_of_two(entries + 1);
+ if (entries < 1 || entries > dev->mdev.caps.max_cqes)
+ return ERR_PTR(-EINVAL);
+
+ cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+ if (!cq)
+ return ERR_PTR(-ENOMEM);
+
+ cq->ibcq.cqe = entries - 1;
+ mutex_init(&cq->resize_mutex);
+ spin_lock_init(&cq->lock);
+ cq->resize_buf = NULL;
+ cq->resize_umem = NULL;
+
+ if (context) {
+ err = create_cq_user(dev, udata, context, cq, entries,
+ &cqb, &cqe_size, &index, &inlen);
+ if (err)
+ goto err_create;
+ } else {
+ /* for now choose 64 bytes till we have a proper interface */
+ cqe_size = 64;
+ err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb,
+ &index, &inlen);
+ if (err)
+ goto err_create;
+ }
+
+ cq->cqe_size = cqe_size;
+ cqb->ctx.cqe_sz_flags = cqe_sz_to_mlx_sz(cqe_size) << 5;
+ cqb->ctx.log_sz_usr_page = cpu_to_be32((ilog2(entries) << 24) | index);
+ err = mlx5_vector2eqn(dev, vector, &eqn, &irqn);
+ if (err)
+ goto err_cqb;
+
+ cqb->ctx.c_eqn = cpu_to_be16(eqn);
+ cqb->ctx.db_record_addr = cpu_to_be64(cq->db.dma);
+
+ err = mlx5_core_create_cq(&dev->mdev, &cq->mcq, cqb, inlen);
+ if (err)
+ goto err_cqb;
+
+ mlx5_ib_dbg(dev, "cqn 0x%x\n", cq->mcq.cqn);
+ cq->mcq.irqn = irqn;
+ cq->mcq.comp = mlx5_ib_cq_comp;
+ cq->mcq.event = mlx5_ib_cq_event;
+
+ if (context)
+ if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof(__u32))) {
+ err = -EFAULT;
+ goto err_cmd;
+ }
+
+
+ mlx5_vfree(cqb);
+ return &cq->ibcq;
+
+err_cmd:
+ mlx5_core_destroy_cq(&dev->mdev, &cq->mcq);
+
+err_cqb:
+ mlx5_vfree(cqb);
+ if (context)
+ destroy_cq_user(cq, context);
+ else
+ destroy_cq_kernel(dev, cq);
+
+err_create:
+ kfree(cq);
+
+ return ERR_PTR(err);
+}
+
+
+int mlx5_ib_destroy_cq(struct ib_cq *cq)
+{
+ struct mlx5_ib_dev *dev = to_mdev(cq->device);
+ struct mlx5_ib_cq *mcq = to_mcq(cq);
+ struct ib_ucontext *context = NULL;
+
+ if (cq->uobject)
+ context = cq->uobject->context;
+
+ mlx5_core_destroy_cq(&dev->mdev, &mcq->mcq);
+ if (context)
+ destroy_cq_user(mcq, context);
+ else
+ destroy_cq_kernel(dev, mcq);
+
+ kfree(mcq);
+
+ return 0;
+}
+
+static int is_equal_rsn(struct mlx5_cqe64 *cqe64, struct mlx5_ib_srq *srq,
+ u32 rsn)
+{
+ u32 lrsn;
+
+ if (srq)
+ lrsn = be32_to_cpu(cqe64->srqn) & 0xffffff;
+ else
+ lrsn = be32_to_cpu(cqe64->sop_drop_qpn) & 0xffffff;
+
+ return rsn == lrsn;
+}
+
+void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 rsn, struct mlx5_ib_srq *srq)
+{
+ struct mlx5_cqe64 *cqe64, *dest64;
+ void *cqe, *dest;
+ u32 prod_index;
+ int nfreed = 0;
+ u8 owner_bit;
+
+ if (!cq)
+ return;
+
+ /* First we need to find the current producer index, so we
+ * know where to start cleaning from. It doesn't matter if HW
+ * adds new entries after this loop -- the QP we're worried
+ * about is already in RESET, so the new entries won't come
+ * from our QP and therefore don't need to be checked.
+ */
+ for (prod_index = cq->mcq.cons_index; get_sw_cqe(cq, prod_index); prod_index++)
+ if (prod_index == cq->mcq.cons_index + cq->ibcq.cqe)
+ break;
+
+ /* Now sweep backwards through the CQ, removing CQ entries
+ * that match our QP by copying older entries on top of them.
+ */
+ while ((int) --prod_index - (int) cq->mcq.cons_index >= 0) {
+ cqe = get_cqe(cq, prod_index & cq->ibcq.cqe);
+ cqe64 = (cq->mcq.cqe_sz == 64) ? cqe : cqe + 64;
+ if (is_equal_rsn(cqe64, srq, rsn)) {
+ if (srq)
+ mlx5_ib_free_srq_wqe(srq, be16_to_cpu(cqe64->wqe_counter));
+ ++nfreed;
+ } else if (nfreed) {
+ dest = get_cqe(cq, (prod_index + nfreed) & cq->ibcq.cqe);
+ dest64 = (cq->mcq.cqe_sz == 64) ? dest : dest + 64;
+ owner_bit = dest64->op_own & MLX5_CQE_OWNER_MASK;
+ memcpy(dest, cqe, cq->mcq.cqe_sz);
+ dest64->op_own = owner_bit |
+ (dest64->op_own & ~MLX5_CQE_OWNER_MASK);
+ }
+ }
+
+ if (nfreed) {
+ cq->mcq.cons_index += nfreed;
+ /* Make sure update of buffer contents is done before
+ * updating consumer index.
+ */
+ wmb();
+ mlx5_cq_set_ci(&cq->mcq);
+ }
+}
+
+void mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq)
+{
+ if (!cq)
+ return;
+
+ spin_lock_irq(&cq->lock);
+ __mlx5_ib_cq_clean(cq, qpn, srq);
+ spin_unlock_irq(&cq->lock);
+}
+
+int mlx5_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period)
+{
+ return -ENOSYS;
+}
+
+int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
+{
+ return -ENOSYS;
+}
+
+int mlx5_ib_get_cqe_size(struct mlx5_ib_dev *dev, struct ib_cq *ibcq)
+{
+ struct mlx5_ib_cq *cq;
+
+ if (!ibcq)
+ return 128;
+
+ cq = to_mcq(ibcq);
+ return cq->cqe_size;
+}
diff --git a/drivers/infiniband/hw/mlx5/doorbell.c b/drivers/infiniband/hw/mlx5/doorbell.c
new file mode 100644
index 0000000..256a233
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/doorbell.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kref.h>
+#include <linux/slab.h>
+#include <rdma/ib_umem.h>
+
+#include "mlx5_ib.h"
+
+struct mlx5_ib_user_db_page {
+ struct list_head list;
+ struct ib_umem *umem;
+ unsigned long user_virt;
+ int refcnt;
+};
+
+int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
+ struct mlx5_db *db)
+{
+ struct mlx5_ib_user_db_page *page;
+ struct ib_umem_chunk *chunk;
+ int err = 0;
+
+ mutex_lock(&context->db_page_mutex);
+
+ list_for_each_entry(page, &context->db_page_list, list)
+ if (page->user_virt == (virt & PAGE_MASK))
+ goto found;
+
+ page = kmalloc(sizeof(*page), GFP_KERNEL);
+ if (!page) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ page->user_virt = (virt & PAGE_MASK);
+ page->refcnt = 0;
+ page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK,
+ PAGE_SIZE, 0, 0);
+ if (IS_ERR(page->umem)) {
+ err = PTR_ERR(page->umem);
+ kfree(page);
+ goto out;
+ }
+
+ list_add(&page->list, &context->db_page_list);
+
+found:
+ chunk = list_entry(page->umem->chunk_list.next, struct ib_umem_chunk, list);
+ db->dma = sg_dma_address(chunk->page_list) + (virt & ~PAGE_MASK);
+ db->u.user_page = page;
+ ++page->refcnt;
+
+out:
+ mutex_unlock(&context->db_page_mutex);
+
+ return err;
+}
+
+void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db)
+{
+ mutex_lock(&context->db_page_mutex);
+
+ if (!--db->u.user_page->refcnt) {
+ list_del(&db->u.user_page->list);
+ ib_umem_release(db->u.user_page->umem);
+ kfree(db->u.user_page);
+ }
+
+ mutex_unlock(&context->db_page_mutex);
+}
diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c
new file mode 100644
index 0000000..5c8938b
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/mad.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/mlx5/cmd.h>
+#include <rdma/ib_mad.h>
+#include <rdma/ib_smi.h>
+#include "mlx5_ib.h"
+
+enum {
+ MLX5_IB_VENDOR_CLASS1 = 0x9,
+ MLX5_IB_VENDOR_CLASS2 = 0xa
+};
+
+int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
+ int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
+ void *in_mad, void *response_mad)
+{
+ u8 op_modifier = 0;
+
+ /* Key check traps can't be generated unless we have in_wc to
+ * tell us where to send the trap.
+ */
+ if (ignore_mkey || !in_wc)
+ op_modifier |= 0x1;
+ if (ignore_bkey || !in_wc)
+ op_modifier |= 0x2;
+
+ return mlx5_core_mad_ifc(&dev->mdev, in_mad, response_mad, op_modifier, port);
+}
+
+int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
+ struct ib_wc *in_wc, struct ib_grh *in_grh,
+ struct ib_mad *in_mad, struct ib_mad *out_mad)
+{
+ u16 slid;
+ int err;
+
+ slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
+
+ if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP && slid == 0)
+ return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
+
+ if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
+ in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
+ if (in_mad->mad_hdr.method != IB_MGMT_METHOD_GET &&
+ in_mad->mad_hdr.method != IB_MGMT_METHOD_SET &&
+ in_mad->mad_hdr.method != IB_MGMT_METHOD_TRAP_REPRESS)
+ return IB_MAD_RESULT_SUCCESS;
+
+ /* Don't process SMInfo queries -- the SMA can't handle them.
+ */
+ if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO)
+ return IB_MAD_RESULT_SUCCESS;
+ } else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT ||
+ in_mad->mad_hdr.mgmt_class == MLX5_IB_VENDOR_CLASS1 ||
+ in_mad->mad_hdr.mgmt_class == MLX5_IB_VENDOR_CLASS2 ||
+ in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_CONG_MGMT) {
+ if (in_mad->mad_hdr.method != IB_MGMT_METHOD_GET &&
+ in_mad->mad_hdr.method != IB_MGMT_METHOD_SET)
+ return IB_MAD_RESULT_SUCCESS;
+ } else {
+ return IB_MAD_RESULT_SUCCESS;
+ }
+
+ err = mlx5_MAD_IFC(to_mdev(ibdev),
+ mad_flags & IB_MAD_IGNORE_MKEY,
+ mad_flags & IB_MAD_IGNORE_BKEY,
+ port_num, in_wc, in_grh, in_mad, out_mad);
+ if (err)
+ return IB_MAD_RESULT_FAILURE;
+
+ /* set return bit in status of directed route responses */
+ if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+ out_mad->mad_hdr.status |= cpu_to_be16(1 << 15);
+
+ if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS)
+ /* no response for trap repress */
+ return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
+
+ return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
+}
+
+int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port)
+{
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int err = -ENOMEM;
+ u16 packet_error;
+
+ in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+ out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = MLX5_ATTR_EXTENDED_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx5_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad);
+
+ packet_error = be16_to_cpu(out_mad->status);
+
+ dev->mdev.caps.ext_port_cap[port - 1] = (!err && !packet_error) ?
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO : 0;
+
+out:
+ kfree(in_mad);
+ kfree(out_mad);
+ return err;
+}
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
new file mode 100644
index 0000000..8000fff
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -0,0 +1,1504 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <asm-generic/kmap_types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/io-mapping.h>
+#include <linux/sched.h>
+#include <rdma/ib_user_verbs.h>
+#include <rdma/ib_smi.h>
+#include <rdma/ib_umem.h>
+#include "user.h"
+#include "mlx5_ib.h"
+
+#define DRIVER_NAME "mlx5_ib"
+#define DRIVER_VERSION "1.0"
+#define DRIVER_RELDATE "June 2013"
+
+MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox Connect-IB HCA IB driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+static int prof_sel = 2;
+module_param_named(prof_sel, prof_sel, int, 0444);
+MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2");
+
+static char mlx5_version[] =
+ DRIVER_NAME ": Mellanox Connect-IB Infiniband driver v"
+ DRIVER_VERSION " (" DRIVER_RELDATE ")\n";
+
+static struct mlx5_profile profile[] = {
+ [0] = {
+ .mask = 0,
+ },
+ [1] = {
+ .mask = MLX5_PROF_MASK_QP_SIZE,
+ .log_max_qp = 12,
+ },
+ [2] = {
+ .mask = MLX5_PROF_MASK_QP_SIZE |
+ MLX5_PROF_MASK_MR_CACHE,
+ .log_max_qp = 17,
+ .mr_cache[0] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[1] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[2] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[3] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[4] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[5] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[6] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[7] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[8] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[9] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[10] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[11] = {
+ .size = 500,
+ .limit = 250
+ },
+ .mr_cache[12] = {
+ .size = 64,
+ .limit = 32
+ },
+ .mr_cache[13] = {
+ .size = 32,
+ .limit = 16
+ },
+ .mr_cache[14] = {
+ .size = 16,
+ .limit = 8
+ },
+ .mr_cache[15] = {
+ .size = 8,
+ .limit = 4
+ },
+ },
+};
+
+int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn)
+{
+ struct mlx5_eq_table *table = &dev->mdev.priv.eq_table;
+ struct mlx5_eq *eq, *n;
+ int err = -ENOENT;
+
+ spin_lock(&table->lock);
+ list_for_each_entry_safe(eq, n, &dev->eqs_list, list) {
+ if (eq->index == vector) {
+ *eqn = eq->eqn;
+ *irqn = eq->irqn;
+ err = 0;
+ break;
+ }
+ }
+ spin_unlock(&table->lock);
+
+ return err;
+}
+
+static int alloc_comp_eqs(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->mdev.priv.eq_table;
+ struct mlx5_eq *eq, *n;
+ int ncomp_vec;
+ int nent;
+ int err;
+ int i;
+
+ INIT_LIST_HEAD(&dev->eqs_list);
+ ncomp_vec = table->num_comp_vectors;
+ nent = MLX5_COMP_EQ_SIZE;
+ for (i = 0; i < ncomp_vec; i++) {
+ eq = kzalloc(sizeof(*eq), GFP_KERNEL);
+ if (!eq) {
+ err = -ENOMEM;
+ goto clean;
+ }
+
+ snprintf(eq->name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i);
+ err = mlx5_create_map_eq(&dev->mdev, eq,
+ i + MLX5_EQ_VEC_COMP_BASE, nent, 0,
+ eq->name,
+ &dev->mdev.priv.uuari.uars[0]);
+ if (err) {
+ kfree(eq);
+ goto clean;
+ }
+ mlx5_ib_dbg(dev, "allocated completion EQN %d\n", eq->eqn);
+ eq->index = i;
+ spin_lock(&table->lock);
+ list_add_tail(&eq->list, &dev->eqs_list);
+ spin_unlock(&table->lock);
+ }
+
+ dev->num_comp_vectors = ncomp_vec;
+ return 0;
+
+clean:
+ spin_lock(&table->lock);
+ list_for_each_entry_safe(eq, n, &dev->eqs_list, list) {
+ list_del(&eq->list);
+ spin_unlock(&table->lock);
+ if (mlx5_destroy_unmap_eq(&dev->mdev, eq))
+ mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn);
+ kfree(eq);
+ spin_lock(&table->lock);
+ }
+ spin_unlock(&table->lock);
+ return err;
+}
+
+static void free_comp_eqs(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->mdev.priv.eq_table;
+ struct mlx5_eq *eq, *n;
+
+ spin_lock(&table->lock);
+ list_for_each_entry_safe(eq, n, &dev->eqs_list, list) {
+ list_del(&eq->list);
+ spin_unlock(&table->lock);
+ if (mlx5_destroy_unmap_eq(&dev->mdev, eq))
+ mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn);
+ kfree(eq);
+ spin_lock(&table->lock);
+ }
+ spin_unlock(&table->lock);
+}
+
+static int mlx5_ib_query_device(struct ib_device *ibdev,
+ struct ib_device_attr *props)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int err = -ENOMEM;
+ int max_rq_sg;
+ int max_sq_sg;
+ u64 flags;
+
+ in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+ out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_NODE_INFO;
+
+ err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, 1, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ memset(props, 0, sizeof(*props));
+
+ props->fw_ver = ((u64)fw_rev_maj(&dev->mdev) << 32) |
+ (fw_rev_min(&dev->mdev) << 16) |
+ fw_rev_sub(&dev->mdev);
+ props->device_cap_flags = IB_DEVICE_CHANGE_PHY_PORT |
+ IB_DEVICE_PORT_ACTIVE_EVENT |
+ IB_DEVICE_SYS_IMAGE_GUID |
+ IB_DEVICE_RC_RNR_NAK_GEN |
+ IB_DEVICE_BLOCK_MULTICAST_LOOPBACK;
+ flags = dev->mdev.caps.flags;
+ if (flags & MLX5_DEV_CAP_FLAG_BAD_PKEY_CNTR)
+ props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR;
+ if (flags & MLX5_DEV_CAP_FLAG_BAD_QKEY_CNTR)
+ props->device_cap_flags |= IB_DEVICE_BAD_QKEY_CNTR;
+ if (flags & MLX5_DEV_CAP_FLAG_APM)
+ props->device_cap_flags |= IB_DEVICE_AUTO_PATH_MIG;
+ props->device_cap_flags |= IB_DEVICE_LOCAL_DMA_LKEY;
+ if (flags & MLX5_DEV_CAP_FLAG_XRC)
+ props->device_cap_flags |= IB_DEVICE_XRC;
+ props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
+
+ props->vendor_id = be32_to_cpup((__be32 *)(out_mad->data + 36)) &
+ 0xffffff;
+ props->vendor_part_id = be16_to_cpup((__be16 *)(out_mad->data + 30));
+ props->hw_ver = be32_to_cpup((__be32 *)(out_mad->data + 32));
+ memcpy(&props->sys_image_guid, out_mad->data + 4, 8);
+
+ props->max_mr_size = ~0ull;
+ props->page_size_cap = dev->mdev.caps.min_page_sz;
+ props->max_qp = 1 << dev->mdev.caps.log_max_qp;
+ props->max_qp_wr = dev->mdev.caps.max_wqes;
+ max_rq_sg = dev->mdev.caps.max_rq_desc_sz / sizeof(struct mlx5_wqe_data_seg);
+ max_sq_sg = (dev->mdev.caps.max_sq_desc_sz - sizeof(struct mlx5_wqe_ctrl_seg)) /
+ sizeof(struct mlx5_wqe_data_seg);
+ props->max_sge = min(max_rq_sg, max_sq_sg);
+ props->max_cq = 1 << dev->mdev.caps.log_max_cq;
+ props->max_cqe = dev->mdev.caps.max_cqes - 1;
+ props->max_mr = 1 << dev->mdev.caps.log_max_mkey;
+ props->max_pd = 1 << dev->mdev.caps.log_max_pd;
+ props->max_qp_rd_atom = dev->mdev.caps.max_ra_req_qp;
+ props->max_qp_init_rd_atom = dev->mdev.caps.max_ra_res_qp;
+ props->max_res_rd_atom = props->max_qp_rd_atom * props->max_qp;
+ props->max_srq = 1 << dev->mdev.caps.log_max_srq;
+ props->max_srq_wr = dev->mdev.caps.max_srq_wqes - 1;
+ props->max_srq_sge = max_rq_sg - 1;
+ props->max_fast_reg_page_list_len = (unsigned int)-1;
+ props->local_ca_ack_delay = dev->mdev.caps.local_ca_ack_delay;
+ props->atomic_cap = dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_ATOMIC ?
+ IB_ATOMIC_HCA : IB_ATOMIC_NONE;
+ props->masked_atomic_cap = IB_ATOMIC_HCA;
+ props->max_pkeys = be16_to_cpup((__be16 *)(out_mad->data + 28));
+ props->max_mcast_grp = 1 << dev->mdev.caps.log_max_mcg;
+ props->max_mcast_qp_attach = dev->mdev.caps.max_qp_mcg;
+ props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
+ props->max_mcast_grp;
+ props->max_map_per_fmr = INT_MAX; /* no limit in ConnectIB */
+
+out:
+ kfree(in_mad);
+ kfree(out_mad);
+
+ return err;
+}
+
+int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
+ struct ib_port_attr *props)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int ext_active_speed;
+ int err = -ENOMEM;
+
+ if (port < 1 || port > dev->mdev.caps.num_ports) {
+ mlx5_ib_warn(dev, "invalid port number %d\n", port);
+ return -EINVAL;
+ }
+
+ in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+ out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ memset(props, 0, sizeof(*props));
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx5_MAD_IFC(dev, 1, 1, port, NULL, NULL, in_mad, out_mad);
+ if (err) {
+ mlx5_ib_warn(dev, "err %d\n", err);
+ goto out;
+ }
+
+
+ props->lid = be16_to_cpup((__be16 *)(out_mad->data + 16));
+ props->lmc = out_mad->data[34] & 0x7;
+ props->sm_lid = be16_to_cpup((__be16 *)(out_mad->data + 18));
+ props->sm_sl = out_mad->data[36] & 0xf;
+ props->state = out_mad->data[32] & 0xf;
+ props->phys_state = out_mad->data[33] >> 4;
+ props->port_cap_flags = be32_to_cpup((__be32 *)(out_mad->data + 20));
+ props->gid_tbl_len = out_mad->data[50];
+ props->max_msg_sz = 1 << to_mdev(ibdev)->mdev.caps.log_max_msg;
+ props->pkey_tbl_len = to_mdev(ibdev)->mdev.caps.port[port - 1].pkey_table_len;
+ props->bad_pkey_cntr = be16_to_cpup((__be16 *)(out_mad->data + 46));
+ props->qkey_viol_cntr = be16_to_cpup((__be16 *)(out_mad->data + 48));
+ props->active_width = out_mad->data[31] & 0xf;
+ props->active_speed = out_mad->data[35] >> 4;
+ props->max_mtu = out_mad->data[41] & 0xf;
+ props->active_mtu = out_mad->data[36] >> 4;
+ props->subnet_timeout = out_mad->data[51] & 0x1f;
+ props->max_vl_num = out_mad->data[37] >> 4;
+ props->init_type_reply = out_mad->data[41] >> 4;
+
+ /* Check if extended speeds (EDR/FDR/...) are supported */
+ if (props->port_cap_flags & IB_PORT_EXTENDED_SPEEDS_SUP) {
+ ext_active_speed = out_mad->data[62] >> 4;
+
+ switch (ext_active_speed) {
+ case 1:
+ props->active_speed = 16; /* FDR */
+ break;
+ case 2:
+ props->active_speed = 32; /* EDR */
+ break;
+ }
+ }
+
+ /* If reported active speed is QDR, check if is FDR-10 */
+ if (props->active_speed == 4) {
+ if (dev->mdev.caps.ext_port_cap[port - 1] &
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO) {
+ init_query_mad(in_mad);
+ in_mad->attr_id = MLX5_ATTR_EXTENDED_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx5_MAD_IFC(dev, 1, 1, port,
+ NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ /* Checking LinkSpeedActive for FDR-10 */
+ if (out_mad->data[15] & 0x1)
+ props->active_speed = 8;
+ }
+ }
+
+out:
+ kfree(in_mad);
+ kfree(out_mad);
+
+ return err;
+}
+
+static int mlx5_ib_query_gid(struct ib_device *ibdev, u8 port, int index,
+ union ib_gid *gid)
+{
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int err = -ENOMEM;
+
+ in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+ out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_PORT_INFO;
+ in_mad->attr_mod = cpu_to_be32(port);
+
+ err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ memcpy(gid->raw, out_mad->data + 8, 8);
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_GUID_INFO;
+ in_mad->attr_mod = cpu_to_be32(index / 8);
+
+ err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ memcpy(gid->raw + 8, out_mad->data + (index % 8) * 8, 8);
+
+out:
+ kfree(in_mad);
+ kfree(out_mad);
+ return err;
+}
+
+static int mlx5_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index,
+ u16 *pkey)
+{
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int err = -ENOMEM;
+
+ in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+ out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_PKEY_TABLE;
+ in_mad->attr_mod = cpu_to_be32(index / 32);
+
+ err = mlx5_MAD_IFC(to_mdev(ibdev), 1, 1, port, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ *pkey = be16_to_cpu(((__be16 *)out_mad->data)[index % 32]);
+
+out:
+ kfree(in_mad);
+ kfree(out_mad);
+ return err;
+}
+
+struct mlx5_reg_node_desc {
+ u8 desc[64];
+};
+
+static int mlx5_ib_modify_device(struct ib_device *ibdev, int mask,
+ struct ib_device_modify *props)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct mlx5_reg_node_desc in;
+ struct mlx5_reg_node_desc out;
+ int err;
+
+ if (mask & ~IB_DEVICE_MODIFY_NODE_DESC)
+ return -EOPNOTSUPP;
+
+ if (!(mask & IB_DEVICE_MODIFY_NODE_DESC))
+ return 0;
+
+ /*
+ * If possible, pass node desc to FW, so it can generate
+ * a 144 trap. If cmd fails, just ignore.
+ */
+ memcpy(&in, props->node_desc, 64);
+ err = mlx5_core_access_reg(&dev->mdev, &in, sizeof(in), &out,
+ sizeof(out), MLX5_REG_NODE_DESC, 0, 1);
+ if (err)
+ return err;
+
+ memcpy(ibdev->node_desc, props->node_desc, 64);
+
+ return err;
+}
+
+static int mlx5_ib_modify_port(struct ib_device *ibdev, u8 port, int mask,
+ struct ib_port_modify *props)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct ib_port_attr attr;
+ u32 tmp;
+ int err;
+
+ mutex_lock(&dev->cap_mask_mutex);
+
+ err = mlx5_ib_query_port(ibdev, port, &attr);
+ if (err)
+ goto out;
+
+ tmp = (attr.port_cap_flags | props->set_port_cap_mask) &
+ ~props->clr_port_cap_mask;
+
+ err = mlx5_set_port_caps(&dev->mdev, port, tmp);
+
+out:
+ mutex_unlock(&dev->cap_mask_mutex);
+ return err;
+}
+
+static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
+ struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct mlx5_ib_alloc_ucontext_req req;
+ struct mlx5_ib_alloc_ucontext_resp resp;
+ struct mlx5_ib_ucontext *context;
+ struct mlx5_uuar_info *uuari;
+ struct mlx5_uar *uars;
+ int num_uars;
+ int uuarn;
+ int err;
+ int i;
+
+ if (!dev->ib_active)
+ return ERR_PTR(-EAGAIN);
+
+ err = ib_copy_from_udata(&req, udata, sizeof(req));
+ if (err)
+ return ERR_PTR(err);
+
+ if (req.total_num_uuars > MLX5_MAX_UUARS)
+ return ERR_PTR(-ENOMEM);
+
+ if (req.total_num_uuars == 0)
+ return ERR_PTR(-EINVAL);
+
+ req.total_num_uuars = ALIGN(req.total_num_uuars, MLX5_BF_REGS_PER_PAGE);
+ if (req.num_low_latency_uuars > req.total_num_uuars - 1)
+ return ERR_PTR(-EINVAL);
+
+ num_uars = req.total_num_uuars / MLX5_BF_REGS_PER_PAGE;
+ resp.qp_tab_size = 1 << dev->mdev.caps.log_max_qp;
+ resp.bf_reg_size = dev->mdev.caps.bf_reg_size;
+ resp.cache_line_size = L1_CACHE_BYTES;
+ resp.max_sq_desc_sz = dev->mdev.caps.max_sq_desc_sz;
+ resp.max_rq_desc_sz = dev->mdev.caps.max_rq_desc_sz;
+ resp.max_send_wqebb = dev->mdev.caps.max_wqes;
+ resp.max_recv_wr = dev->mdev.caps.max_wqes;
+ resp.max_srq_recv_wr = dev->mdev.caps.max_srq_wqes;
+
+ context = kzalloc(sizeof(*context), GFP_KERNEL);
+ if (!context)
+ return ERR_PTR(-ENOMEM);
+
+ uuari = &context->uuari;
+ mutex_init(&uuari->lock);
+ uars = kcalloc(num_uars, sizeof(*uars), GFP_KERNEL);
+ if (!uars) {
+ err = -ENOMEM;
+ goto out_ctx;
+ }
+
+ uuari->bitmap = kcalloc(BITS_TO_LONGS(req.total_num_uuars),
+ sizeof(*uuari->bitmap),
+ GFP_KERNEL);
+ if (!uuari->bitmap) {
+ err = -ENOMEM;
+ goto out_uar_ctx;
+ }
+ /*
+ * clear all fast path uuars
+ */
+ for (i = 0; i < req.total_num_uuars; i++) {
+ uuarn = i & 3;
+ if (uuarn == 2 || uuarn == 3)
+ set_bit(i, uuari->bitmap);
+ }
+
+ uuari->count = kcalloc(req.total_num_uuars, sizeof(*uuari->count), GFP_KERNEL);
+ if (!uuari->count) {
+ err = -ENOMEM;
+ goto out_bitmap;
+ }
+
+ for (i = 0; i < num_uars; i++) {
+ err = mlx5_cmd_alloc_uar(&dev->mdev, &uars[i].index);
+ if (err)
+ goto out_count;
+ }
+
+ INIT_LIST_HEAD(&context->db_page_list);
+ mutex_init(&context->db_page_mutex);
+
+ resp.tot_uuars = req.total_num_uuars;
+ resp.num_ports = dev->mdev.caps.num_ports;
+ err = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ if (err)
+ goto out_uars;
+
+ uuari->num_low_latency_uuars = req.num_low_latency_uuars;
+ uuari->uars = uars;
+ uuari->num_uars = num_uars;
+ return &context->ibucontext;
+
+out_uars:
+ for (i--; i >= 0; i--)
+ mlx5_cmd_free_uar(&dev->mdev, uars[i].index);
+out_count:
+ kfree(uuari->count);
+
+out_bitmap:
+ kfree(uuari->bitmap);
+
+out_uar_ctx:
+ kfree(uars);
+
+out_ctx:
+ kfree(context);
+ return ERR_PTR(err);
+}
+
+static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
+{
+ struct mlx5_ib_ucontext *context = to_mucontext(ibcontext);
+ struct mlx5_ib_dev *dev = to_mdev(ibcontext->device);
+ struct mlx5_uuar_info *uuari = &context->uuari;
+ int i;
+
+ for (i = 0; i < uuari->num_uars; i++) {
+ if (mlx5_cmd_free_uar(&dev->mdev, uuari->uars[i].index))
+ mlx5_ib_warn(dev, "failed to free UAR 0x%x\n", uuari->uars[i].index);
+ }
+
+ kfree(uuari->count);
+ kfree(uuari->bitmap);
+ kfree(uuari->uars);
+ kfree(context);
+
+ return 0;
+}
+
+static phys_addr_t uar_index2pfn(struct mlx5_ib_dev *dev, int index)
+{
+ return (pci_resource_start(dev->mdev.pdev, 0) >> PAGE_SHIFT) + index;
+}
+
+static int get_command(unsigned long offset)
+{
+ return (offset >> MLX5_IB_MMAP_CMD_SHIFT) & MLX5_IB_MMAP_CMD_MASK;
+}
+
+static int get_arg(unsigned long offset)
+{
+ return offset & ((1 << MLX5_IB_MMAP_CMD_SHIFT) - 1);
+}
+
+static int get_index(unsigned long offset)
+{
+ return get_arg(offset);
+}
+
+static int mlx5_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma)
+{
+ struct mlx5_ib_ucontext *context = to_mucontext(ibcontext);
+ struct mlx5_ib_dev *dev = to_mdev(ibcontext->device);
+ struct mlx5_uuar_info *uuari = &context->uuari;
+ unsigned long command;
+ unsigned long idx;
+ phys_addr_t pfn;
+
+ command = get_command(vma->vm_pgoff);
+ switch (command) {
+ case MLX5_IB_MMAP_REGULAR_PAGE:
+ if (vma->vm_end - vma->vm_start != PAGE_SIZE)
+ return -EINVAL;
+
+ idx = get_index(vma->vm_pgoff);
+ pfn = uar_index2pfn(dev, uuari->uars[idx].index);
+ mlx5_ib_dbg(dev, "uar idx 0x%lx, pfn 0x%llx\n", idx,
+ (unsigned long long)pfn);
+
+ if (idx >= uuari->num_uars)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ if (io_remap_pfn_range(vma, vma->vm_start, pfn,
+ PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+
+ mlx5_ib_dbg(dev, "mapped WC at 0x%lx, PA 0x%llx\n",
+ vma->vm_start,
+ (unsigned long long)pfn << PAGE_SHIFT);
+ break;
+
+ case MLX5_IB_MMAP_GET_CONTIGUOUS_PAGES:
+ return -ENOSYS;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int alloc_pa_mkey(struct mlx5_ib_dev *dev, u32 *key, u32 pdn)
+{
+ struct mlx5_create_mkey_mbox_in *in;
+ struct mlx5_mkey_seg *seg;
+ struct mlx5_core_mr mr;
+ int err;
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ seg = &in->seg;
+ seg->flags = MLX5_PERM_LOCAL_READ | MLX5_ACCESS_MODE_PA;
+ seg->flags_pd = cpu_to_be32(pdn | MLX5_MKEY_LEN64);
+ seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+ seg->start_addr = 0;
+
+ err = mlx5_core_create_mkey(&dev->mdev, &mr, in, sizeof(*in));
+ if (err) {
+ mlx5_ib_warn(dev, "failed to create mkey, %d\n", err);
+ goto err_in;
+ }
+
+ kfree(in);
+ *key = mr.key;
+
+ return 0;
+
+err_in:
+ kfree(in);
+
+ return err;
+}
+
+static void free_pa_mkey(struct mlx5_ib_dev *dev, u32 key)
+{
+ struct mlx5_core_mr mr;
+ int err;
+
+ memset(&mr, 0, sizeof(mr));
+ mr.key = key;
+ err = mlx5_core_destroy_mkey(&dev->mdev, &mr);
+ if (err)
+ mlx5_ib_warn(dev, "failed to destroy mkey 0x%x\n", key);
+}
+
+static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
+{
+ struct mlx5_ib_alloc_pd_resp resp;
+ struct mlx5_ib_pd *pd;
+ int err;
+
+ pd = kmalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return ERR_PTR(-ENOMEM);
+
+ err = mlx5_core_alloc_pd(&to_mdev(ibdev)->mdev, &pd->pdn);
+ if (err) {
+ kfree(pd);
+ return ERR_PTR(err);
+ }
+
+ if (context) {
+ resp.pdn = pd->pdn;
+ if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
+ mlx5_core_dealloc_pd(&to_mdev(ibdev)->mdev, pd->pdn);
+ kfree(pd);
+ return ERR_PTR(-EFAULT);
+ }
+ } else {
+ err = alloc_pa_mkey(to_mdev(ibdev), &pd->pa_lkey, pd->pdn);
+ if (err) {
+ mlx5_core_dealloc_pd(&to_mdev(ibdev)->mdev, pd->pdn);
+ kfree(pd);
+ return ERR_PTR(err);
+ }
+ }
+
+ return &pd->ibpd;
+}
+
+static int mlx5_ib_dealloc_pd(struct ib_pd *pd)
+{
+ struct mlx5_ib_dev *mdev = to_mdev(pd->device);
+ struct mlx5_ib_pd *mpd = to_mpd(pd);
+
+ if (!pd->uobject)
+ free_pa_mkey(mdev, mpd->pa_lkey);
+
+ mlx5_core_dealloc_pd(&mdev->mdev, mpd->pdn);
+ kfree(mpd);
+
+ return 0;
+}
+
+static int mlx5_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
+ int err;
+
+ err = mlx5_core_attach_mcg(&dev->mdev, gid, ibqp->qp_num);
+ if (err)
+ mlx5_ib_warn(dev, "failed attaching QPN 0x%x, MGID %pI6\n",
+ ibqp->qp_num, gid->raw);
+
+ return err;
+}
+
+static int mlx5_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
+ int err;
+
+ err = mlx5_core_detach_mcg(&dev->mdev, gid, ibqp->qp_num);
+ if (err)
+ mlx5_ib_warn(dev, "failed detaching QPN 0x%x, MGID %pI6\n",
+ ibqp->qp_num, gid->raw);
+
+ return err;
+}
+
+static int init_node_data(struct mlx5_ib_dev *dev)
+{
+ struct ib_smp *in_mad = NULL;
+ struct ib_smp *out_mad = NULL;
+ int err = -ENOMEM;
+
+ in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
+ out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
+ if (!in_mad || !out_mad)
+ goto out;
+
+ init_query_mad(in_mad);
+ in_mad->attr_id = IB_SMP_ATTR_NODE_DESC;
+
+ err = mlx5_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ memcpy(dev->ib_dev.node_desc, out_mad->data, 64);
+
+ in_mad->attr_id = IB_SMP_ATTR_NODE_INFO;
+
+ err = mlx5_MAD_IFC(dev, 1, 1, 1, NULL, NULL, in_mad, out_mad);
+ if (err)
+ goto out;
+
+ dev->mdev.rev_id = be32_to_cpup((__be32 *)(out_mad->data + 32));
+ memcpy(&dev->ib_dev.node_guid, out_mad->data + 12, 8);
+
+out:
+ kfree(in_mad);
+ kfree(out_mad);
+ return err;
+}
+
+static ssize_t show_fw_pages(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct mlx5_ib_dev *dev =
+ container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+
+ return sprintf(buf, "%d\n", dev->mdev.priv.fw_pages);
+}
+
+static ssize_t show_reg_pages(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct mlx5_ib_dev *dev =
+ container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+
+ return sprintf(buf, "%d\n", dev->mdev.priv.reg_pages);
+}
+
+static ssize_t show_hca(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct mlx5_ib_dev *dev =
+ container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ return sprintf(buf, "MT%d\n", dev->mdev.pdev->device);
+}
+
+static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct mlx5_ib_dev *dev =
+ container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ return sprintf(buf, "%d.%d.%d\n", fw_rev_maj(&dev->mdev),
+ fw_rev_min(&dev->mdev), fw_rev_sub(&dev->mdev));
+}
+
+static ssize_t show_rev(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct mlx5_ib_dev *dev =
+ container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ return sprintf(buf, "%x\n", dev->mdev.rev_id);
+}
+
+static ssize_t show_board(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct mlx5_ib_dev *dev =
+ container_of(device, struct mlx5_ib_dev, ib_dev.dev);
+ return sprintf(buf, "%.*s\n", MLX5_BOARD_ID_LEN,
+ dev->mdev.board_id);
+}
+
+static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
+static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
+static DEVICE_ATTR(hca_type, S_IRUGO, show_hca, NULL);
+static DEVICE_ATTR(board_id, S_IRUGO, show_board, NULL);
+static DEVICE_ATTR(fw_pages, S_IRUGO, show_fw_pages, NULL);
+static DEVICE_ATTR(reg_pages, S_IRUGO, show_reg_pages, NULL);
+
+static struct device_attribute *mlx5_class_attributes[] = {
+ &dev_attr_hw_rev,
+ &dev_attr_fw_ver,
+ &dev_attr_hca_type,
+ &dev_attr_board_id,
+ &dev_attr_fw_pages,
+ &dev_attr_reg_pages,
+};
+
+static void mlx5_ib_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
+ void *data)
+{
+ struct mlx5_ib_dev *ibdev = container_of(dev, struct mlx5_ib_dev, mdev);
+ struct ib_event ibev;
+ u8 port = 0;
+
+ switch (event) {
+ case MLX5_DEV_EVENT_SYS_ERROR:
+ ibdev->ib_active = false;
+ ibev.event = IB_EVENT_DEVICE_FATAL;
+ break;
+
+ case MLX5_DEV_EVENT_PORT_UP:
+ ibev.event = IB_EVENT_PORT_ACTIVE;
+ port = *(u8 *)data;
+ break;
+
+ case MLX5_DEV_EVENT_PORT_DOWN:
+ ibev.event = IB_EVENT_PORT_ERR;
+ port = *(u8 *)data;
+ break;
+
+ case MLX5_DEV_EVENT_PORT_INITIALIZED:
+ /* not used by ULPs */
+ return;
+
+ case MLX5_DEV_EVENT_LID_CHANGE:
+ ibev.event = IB_EVENT_LID_CHANGE;
+ port = *(u8 *)data;
+ break;
+
+ case MLX5_DEV_EVENT_PKEY_CHANGE:
+ ibev.event = IB_EVENT_PKEY_CHANGE;
+ port = *(u8 *)data;
+ break;
+
+ case MLX5_DEV_EVENT_GUID_CHANGE:
+ ibev.event = IB_EVENT_GID_CHANGE;
+ port = *(u8 *)data;
+ break;
+
+ case MLX5_DEV_EVENT_CLIENT_REREG:
+ ibev.event = IB_EVENT_CLIENT_REREGISTER;
+ port = *(u8 *)data;
+ break;
+ }
+
+ ibev.device = &ibdev->ib_dev;
+ ibev.element.port_num = port;
+
+ if (ibdev->ib_active)
+ ib_dispatch_event(&ibev);
+}
+
+static void get_ext_port_caps(struct mlx5_ib_dev *dev)
+{
+ int port;
+
+ for (port = 1; port <= dev->mdev.caps.num_ports; port++)
+ mlx5_query_ext_port_caps(dev, port);
+}
+
+static int get_port_caps(struct mlx5_ib_dev *dev)
+{
+ struct ib_device_attr *dprops = NULL;
+ struct ib_port_attr *pprops = NULL;
+ int err = 0;
+ int port;
+
+ pprops = kmalloc(sizeof(*pprops), GFP_KERNEL);
+ if (!pprops)
+ goto out;
+
+ dprops = kmalloc(sizeof(*dprops), GFP_KERNEL);
+ if (!dprops)
+ goto out;
+
+ err = mlx5_ib_query_device(&dev->ib_dev, dprops);
+ if (err) {
+ mlx5_ib_warn(dev, "query_device failed %d\n", err);
+ goto out;
+ }
+
+ for (port = 1; port <= dev->mdev.caps.num_ports; port++) {
+ err = mlx5_ib_query_port(&dev->ib_dev, port, pprops);
+ if (err) {
+ mlx5_ib_warn(dev, "query_port %d failed %d\n", port, err);
+ break;
+ }
+ dev->mdev.caps.port[port - 1].pkey_table_len = dprops->max_pkeys;
+ dev->mdev.caps.port[port - 1].gid_table_len = pprops->gid_tbl_len;
+ mlx5_ib_dbg(dev, "pkey_table_len %d, gid_table_len %d\n",
+ dprops->max_pkeys, pprops->gid_tbl_len);
+ }
+
+out:
+ kfree(pprops);
+ kfree(dprops);
+
+ return err;
+}
+
+static void destroy_umrc_res(struct mlx5_ib_dev *dev)
+{
+ int err;
+
+ err = mlx5_mr_cache_cleanup(dev);
+ if (err)
+ mlx5_ib_warn(dev, "mr cache cleanup failed\n");
+
+ mlx5_ib_destroy_qp(dev->umrc.qp);
+ ib_destroy_cq(dev->umrc.cq);
+ ib_dereg_mr(dev->umrc.mr);
+ ib_dealloc_pd(dev->umrc.pd);
+}
+
+enum {
+ MAX_UMR_WR = 128,
+};
+
+static int create_umr_res(struct mlx5_ib_dev *dev)
+{
+ struct ib_qp_init_attr *init_attr = NULL;
+ struct ib_qp_attr *attr = NULL;
+ struct ib_pd *pd;
+ struct ib_cq *cq;
+ struct ib_qp *qp;
+ struct ib_mr *mr;
+ int ret;
+
+ attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+ init_attr = kzalloc(sizeof(*init_attr), GFP_KERNEL);
+ if (!attr || !init_attr) {
+ ret = -ENOMEM;
+ goto error_0;
+ }
+
+ pd = ib_alloc_pd(&dev->ib_dev);
+ if (IS_ERR(pd)) {
+ mlx5_ib_dbg(dev, "Couldn't create PD for sync UMR QP\n");
+ ret = PTR_ERR(pd);
+ goto error_0;
+ }
+
+ mr = ib_get_dma_mr(pd, IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(mr)) {
+ mlx5_ib_dbg(dev, "Couldn't create DMA MR for sync UMR QP\n");
+ ret = PTR_ERR(mr);
+ goto error_1;
+ }
+
+ cq = ib_create_cq(&dev->ib_dev, mlx5_umr_cq_handler, NULL, NULL, 128,
+ 0);
+ if (IS_ERR(cq)) {
+ mlx5_ib_dbg(dev, "Couldn't create CQ for sync UMR QP\n");
+ ret = PTR_ERR(cq);
+ goto error_2;
+ }
+ ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+
+ init_attr->send_cq = cq;
+ init_attr->recv_cq = cq;
+ init_attr->sq_sig_type = IB_SIGNAL_ALL_WR;
+ init_attr->cap.max_send_wr = MAX_UMR_WR;
+ init_attr->cap.max_send_sge = 1;
+ init_attr->qp_type = MLX5_IB_QPT_REG_UMR;
+ init_attr->port_num = 1;
+ qp = mlx5_ib_create_qp(pd, init_attr, NULL);
+ if (IS_ERR(qp)) {
+ mlx5_ib_dbg(dev, "Couldn't create sync UMR QP\n");
+ ret = PTR_ERR(qp);
+ goto error_3;
+ }
+ qp->device = &dev->ib_dev;
+ qp->real_qp = qp;
+ qp->uobject = NULL;
+ qp->qp_type = MLX5_IB_QPT_REG_UMR;
+
+ attr->qp_state = IB_QPS_INIT;
+ attr->port_num = 1;
+ ret = mlx5_ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_PKEY_INDEX |
+ IB_QP_PORT, NULL);
+ if (ret) {
+ mlx5_ib_dbg(dev, "Couldn't modify UMR QP\n");
+ goto error_4;
+ }
+
+ memset(attr, 0, sizeof(*attr));
+ attr->qp_state = IB_QPS_RTR;
+ attr->path_mtu = IB_MTU_256;
+
+ ret = mlx5_ib_modify_qp(qp, attr, IB_QP_STATE, NULL);
+ if (ret) {
+ mlx5_ib_dbg(dev, "Couldn't modify umr QP to rtr\n");
+ goto error_4;
+ }
+
+ memset(attr, 0, sizeof(*attr));
+ attr->qp_state = IB_QPS_RTS;
+ ret = mlx5_ib_modify_qp(qp, attr, IB_QP_STATE, NULL);
+ if (ret) {
+ mlx5_ib_dbg(dev, "Couldn't modify umr QP to rts\n");
+ goto error_4;
+ }
+
+ dev->umrc.qp = qp;
+ dev->umrc.cq = cq;
+ dev->umrc.mr = mr;
+ dev->umrc.pd = pd;
+
+ sema_init(&dev->umrc.sem, MAX_UMR_WR);
+ ret = mlx5_mr_cache_init(dev);
+ if (ret) {
+ mlx5_ib_warn(dev, "mr cache init failed %d\n", ret);
+ goto error_4;
+ }
+
+ kfree(attr);
+ kfree(init_attr);
+
+ return 0;
+
+error_4:
+ mlx5_ib_destroy_qp(qp);
+
+error_3:
+ ib_destroy_cq(cq);
+
+error_2:
+ ib_dereg_mr(mr);
+
+error_1:
+ ib_dealloc_pd(pd);
+
+error_0:
+ kfree(attr);
+ kfree(init_attr);
+ return ret;
+}
+
+static int create_dev_resources(struct mlx5_ib_resources *devr)
+{
+ struct ib_srq_init_attr attr;
+ struct mlx5_ib_dev *dev;
+ int ret = 0;
+
+ dev = container_of(devr, struct mlx5_ib_dev, devr);
+
+ devr->p0 = mlx5_ib_alloc_pd(&dev->ib_dev, NULL, NULL);
+ if (IS_ERR(devr->p0)) {
+ ret = PTR_ERR(devr->p0);
+ goto error0;
+ }
+ devr->p0->device = &dev->ib_dev;
+ devr->p0->uobject = NULL;
+ atomic_set(&devr->p0->usecnt, 0);
+
+ devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, 1, 0, NULL, NULL);
+ if (IS_ERR(devr->c0)) {
+ ret = PTR_ERR(devr->c0);
+ goto error1;
+ }
+ devr->c0->device = &dev->ib_dev;
+ devr->c0->uobject = NULL;
+ devr->c0->comp_handler = NULL;
+ devr->c0->event_handler = NULL;
+ devr->c0->cq_context = NULL;
+ atomic_set(&devr->c0->usecnt, 0);
+
+ devr->x0 = mlx5_ib_alloc_xrcd(&dev->ib_dev, NULL, NULL);
+ if (IS_ERR(devr->x0)) {
+ ret = PTR_ERR(devr->x0);
+ goto error2;
+ }
+ devr->x0->device = &dev->ib_dev;
+ devr->x0->inode = NULL;
+ atomic_set(&devr->x0->usecnt, 0);
+ mutex_init(&devr->x0->tgt_qp_mutex);
+ INIT_LIST_HEAD(&devr->x0->tgt_qp_list);
+
+ devr->x1 = mlx5_ib_alloc_xrcd(&dev->ib_dev, NULL, NULL);
+ if (IS_ERR(devr->x1)) {
+ ret = PTR_ERR(devr->x1);
+ goto error3;
+ }
+ devr->x1->device = &dev->ib_dev;
+ devr->x1->inode = NULL;
+ atomic_set(&devr->x1->usecnt, 0);
+ mutex_init(&devr->x1->tgt_qp_mutex);
+ INIT_LIST_HEAD(&devr->x1->tgt_qp_list);
+
+ memset(&attr, 0, sizeof(attr));
+ attr.attr.max_sge = 1;
+ attr.attr.max_wr = 1;
+ attr.srq_type = IB_SRQT_XRC;
+ attr.ext.xrc.cq = devr->c0;
+ attr.ext.xrc.xrcd = devr->x0;
+
+ devr->s0 = mlx5_ib_create_srq(devr->p0, &attr, NULL);
+ if (IS_ERR(devr->s0)) {
+ ret = PTR_ERR(devr->s0);
+ goto error4;
+ }
+ devr->s0->device = &dev->ib_dev;
+ devr->s0->pd = devr->p0;
+ devr->s0->uobject = NULL;
+ devr->s0->event_handler = NULL;
+ devr->s0->srq_context = NULL;
+ devr->s0->srq_type = IB_SRQT_XRC;
+ devr->s0->ext.xrc.xrcd = devr->x0;
+ devr->s0->ext.xrc.cq = devr->c0;
+ atomic_inc(&devr->s0->ext.xrc.xrcd->usecnt);
+ atomic_inc(&devr->s0->ext.xrc.cq->usecnt);
+ atomic_inc(&devr->p0->usecnt);
+ atomic_set(&devr->s0->usecnt, 0);
+
+ return 0;
+
+error4:
+ mlx5_ib_dealloc_xrcd(devr->x1);
+error3:
+ mlx5_ib_dealloc_xrcd(devr->x0);
+error2:
+ mlx5_ib_destroy_cq(devr->c0);
+error1:
+ mlx5_ib_dealloc_pd(devr->p0);
+error0:
+ return ret;
+}
+
+static void destroy_dev_resources(struct mlx5_ib_resources *devr)
+{
+ mlx5_ib_destroy_srq(devr->s0);
+ mlx5_ib_dealloc_xrcd(devr->x0);
+ mlx5_ib_dealloc_xrcd(devr->x1);
+ mlx5_ib_destroy_cq(devr->c0);
+ mlx5_ib_dealloc_pd(devr->p0);
+}
+
+static int init_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct mlx5_core_dev *mdev;
+ struct mlx5_ib_dev *dev;
+ int err;
+ int i;
+
+ printk_once(KERN_INFO "%s", mlx5_version);
+
+ dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev));
+ if (!dev)
+ return -ENOMEM;
+
+ mdev = &dev->mdev;
+ mdev->event = mlx5_ib_event;
+ if (prof_sel >= ARRAY_SIZE(profile)) {
+ pr_warn("selected pofile out of range, selceting default\n");
+ prof_sel = 0;
+ }
+ mdev->profile = &profile[prof_sel];
+ err = mlx5_dev_init(mdev, pdev);
+ if (err)
+ goto err_free;
+
+ err = get_port_caps(dev);
+ if (err)
+ goto err_cleanup;
+
+ get_ext_port_caps(dev);
+
+ err = alloc_comp_eqs(dev);
+ if (err)
+ goto err_cleanup;
+
+ MLX5_INIT_DOORBELL_LOCK(&dev->uar_lock);
+
+ strlcpy(dev->ib_dev.name, "mlx5_%d", IB_DEVICE_NAME_MAX);
+ dev->ib_dev.owner = THIS_MODULE;
+ dev->ib_dev.node_type = RDMA_NODE_IB_CA;
+ dev->ib_dev.local_dma_lkey = mdev->caps.reserved_lkey;
+ dev->num_ports = mdev->caps.num_ports;
+ dev->ib_dev.phys_port_cnt = dev->num_ports;
+ dev->ib_dev.num_comp_vectors = dev->num_comp_vectors;
+ dev->ib_dev.dma_device = &mdev->pdev->dev;
+
+ dev->ib_dev.uverbs_abi_ver = MLX5_IB_UVERBS_ABI_VERSION;
+ dev->ib_dev.uverbs_cmd_mask =
+ (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) |
+ (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) |
+ (1ull << IB_USER_VERBS_CMD_QUERY_PORT) |
+ (1ull << IB_USER_VERBS_CMD_ALLOC_PD) |
+ (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) |
+ (1ull << IB_USER_VERBS_CMD_REG_MR) |
+ (1ull << IB_USER_VERBS_CMD_DEREG_MR) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_CQ) |
+ (1ull << IB_USER_VERBS_CMD_RESIZE_CQ) |
+ (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_QP) |
+ (1ull << IB_USER_VERBS_CMD_MODIFY_QP) |
+ (1ull << IB_USER_VERBS_CMD_QUERY_QP) |
+ (1ull << IB_USER_VERBS_CMD_DESTROY_QP) |
+ (1ull << IB_USER_VERBS_CMD_ATTACH_MCAST) |
+ (1ull << IB_USER_VERBS_CMD_DETACH_MCAST) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_SRQ) |
+ (1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) |
+ (1ull << IB_USER_VERBS_CMD_QUERY_SRQ) |
+ (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) |
+ (1ull << IB_USER_VERBS_CMD_CREATE_XSRQ) |
+ (1ull << IB_USER_VERBS_CMD_OPEN_QP);
+
+ dev->ib_dev.query_device = mlx5_ib_query_device;
+ dev->ib_dev.query_port = mlx5_ib_query_port;
+ dev->ib_dev.query_gid = mlx5_ib_query_gid;
+ dev->ib_dev.query_pkey = mlx5_ib_query_pkey;
+ dev->ib_dev.modify_device = mlx5_ib_modify_device;
+ dev->ib_dev.modify_port = mlx5_ib_modify_port;
+ dev->ib_dev.alloc_ucontext = mlx5_ib_alloc_ucontext;
+ dev->ib_dev.dealloc_ucontext = mlx5_ib_dealloc_ucontext;
+ dev->ib_dev.mmap = mlx5_ib_mmap;
+ dev->ib_dev.alloc_pd = mlx5_ib_alloc_pd;
+ dev->ib_dev.dealloc_pd = mlx5_ib_dealloc_pd;
+ dev->ib_dev.create_ah = mlx5_ib_create_ah;
+ dev->ib_dev.query_ah = mlx5_ib_query_ah;
+ dev->ib_dev.destroy_ah = mlx5_ib_destroy_ah;
+ dev->ib_dev.create_srq = mlx5_ib_create_srq;
+ dev->ib_dev.modify_srq = mlx5_ib_modify_srq;
+ dev->ib_dev.query_srq = mlx5_ib_query_srq;
+ dev->ib_dev.destroy_srq = mlx5_ib_destroy_srq;
+ dev->ib_dev.post_srq_recv = mlx5_ib_post_srq_recv;
+ dev->ib_dev.create_qp = mlx5_ib_create_qp;
+ dev->ib_dev.modify_qp = mlx5_ib_modify_qp;
+ dev->ib_dev.query_qp = mlx5_ib_query_qp;
+ dev->ib_dev.destroy_qp = mlx5_ib_destroy_qp;
+ dev->ib_dev.post_send = mlx5_ib_post_send;
+ dev->ib_dev.post_recv = mlx5_ib_post_recv;
+ dev->ib_dev.create_cq = mlx5_ib_create_cq;
+ dev->ib_dev.modify_cq = mlx5_ib_modify_cq;
+ dev->ib_dev.resize_cq = mlx5_ib_resize_cq;
+ dev->ib_dev.destroy_cq = mlx5_ib_destroy_cq;
+ dev->ib_dev.poll_cq = mlx5_ib_poll_cq;
+ dev->ib_dev.req_notify_cq = mlx5_ib_arm_cq;
+ dev->ib_dev.get_dma_mr = mlx5_ib_get_dma_mr;
+ dev->ib_dev.reg_user_mr = mlx5_ib_reg_user_mr;
+ dev->ib_dev.dereg_mr = mlx5_ib_dereg_mr;
+ dev->ib_dev.attach_mcast = mlx5_ib_mcg_attach;
+ dev->ib_dev.detach_mcast = mlx5_ib_mcg_detach;
+ dev->ib_dev.process_mad = mlx5_ib_process_mad;
+ dev->ib_dev.alloc_fast_reg_mr = mlx5_ib_alloc_fast_reg_mr;
+ dev->ib_dev.alloc_fast_reg_page_list = mlx5_ib_alloc_fast_reg_page_list;
+ dev->ib_dev.free_fast_reg_page_list = mlx5_ib_free_fast_reg_page_list;
+
+ if (mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC) {
+ dev->ib_dev.alloc_xrcd = mlx5_ib_alloc_xrcd;
+ dev->ib_dev.dealloc_xrcd = mlx5_ib_dealloc_xrcd;
+ dev->ib_dev.uverbs_cmd_mask |=
+ (1ull << IB_USER_VERBS_CMD_OPEN_XRCD) |
+ (1ull << IB_USER_VERBS_CMD_CLOSE_XRCD);
+ }
+
+ err = init_node_data(dev);
+ if (err)
+ goto err_eqs;
+
+ mutex_init(&dev->cap_mask_mutex);
+ spin_lock_init(&dev->mr_lock);
+
+ err = create_dev_resources(&dev->devr);
+ if (err)
+ goto err_eqs;
+
+ if (ib_register_device(&dev->ib_dev, NULL))
+ goto err_rsrc;
+
+ err = create_umr_res(dev);
+ if (err)
+ goto err_dev;
+
+ for (i = 0; i < ARRAY_SIZE(mlx5_class_attributes); i++) {
+ if (device_create_file(&dev->ib_dev.dev,
+ mlx5_class_attributes[i]))
+ goto err_umrc;
+ }
+
+ dev->ib_active = true;
+
+ return 0;
+
+err_umrc:
+ destroy_umrc_res(dev);
+
+err_dev:
+ ib_unregister_device(&dev->ib_dev);
+
+err_rsrc:
+ destroy_dev_resources(&dev->devr);
+
+err_eqs:
+ free_comp_eqs(dev);
+
+err_cleanup:
+ mlx5_dev_cleanup(mdev);
+
+err_free:
+ ib_dealloc_device((struct ib_device *)dev);
+
+ return err;
+}
+
+static void remove_one(struct pci_dev *pdev)
+{
+ struct mlx5_ib_dev *dev = mlx5_pci2ibdev(pdev);
+
+ destroy_umrc_res(dev);
+ ib_unregister_device(&dev->ib_dev);
+ destroy_dev_resources(&dev->devr);
+ free_comp_eqs(dev);
+ mlx5_dev_cleanup(&dev->mdev);
+ ib_dealloc_device(&dev->ib_dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(mlx5_ib_pci_table) = {
+ { PCI_VDEVICE(MELLANOX, 4113) }, /* MT4113 Connect-IB */
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, mlx5_ib_pci_table);
+
+static struct pci_driver mlx5_ib_driver = {
+ .name = DRIVER_NAME,
+ .id_table = mlx5_ib_pci_table,
+ .probe = init_one,
+ .remove = remove_one
+};
+
+static int __init mlx5_ib_init(void)
+{
+ return pci_register_driver(&mlx5_ib_driver);
+}
+
+static void __exit mlx5_ib_cleanup(void)
+{
+ pci_unregister_driver(&mlx5_ib_driver);
+}
+
+module_init(mlx5_ib_init);
+module_exit(mlx5_ib_cleanup);
diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c
new file mode 100644
index 0000000..3a53228
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/mem.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/module.h>
+#include <rdma/ib_umem.h>
+#include "mlx5_ib.h"
+
+/* @umem: umem object to scan
+ * @addr: ib virtual address requested by the user
+ * @count: number of PAGE_SIZE pages covered by umem
+ * @shift: page shift for the compound pages found in the region
+ * @ncont: number of compund pages
+ * @order: log2 of the number of compound pages
+ */
+void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
+ int *ncont, int *order)
+{
+ struct ib_umem_chunk *chunk;
+ unsigned long tmp;
+ unsigned long m;
+ int i, j, k;
+ u64 base = 0;
+ int p = 0;
+ int skip;
+ int mask;
+ u64 len;
+ u64 pfn;
+
+ addr = addr >> PAGE_SHIFT;
+ tmp = (unsigned long)addr;
+ m = find_first_bit(&tmp, sizeof(tmp));
+ skip = 1 << m;
+ mask = skip - 1;
+ i = 0;
+ list_for_each_entry(chunk, &umem->chunk_list, list)
+ for (j = 0; j < chunk->nmap; j++) {
+ len = sg_dma_len(&chunk->page_list[j]) >> PAGE_SHIFT;
+ pfn = sg_dma_address(&chunk->page_list[j]) >> PAGE_SHIFT;
+ for (k = 0; k < len; k++) {
+ if (!(i & mask)) {
+ tmp = (unsigned long)pfn;
+ m = min(m, find_first_bit(&tmp, sizeof(tmp)));
+ skip = 1 << m;
+ mask = skip - 1;
+ base = pfn;
+ p = 0;
+ } else {
+ if (base + p != pfn) {
+ tmp = (unsigned long)p;
+ m = find_first_bit(&tmp, sizeof(tmp));
+ skip = 1 << m;
+ mask = skip - 1;
+ base = pfn;
+ p = 0;
+ }
+ }
+ p++;
+ i++;
+ }
+ }
+
+ if (i) {
+ m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m);
+
+ if (order)
+ *order = ilog2(roundup_pow_of_two(i) >> m);
+
+ *ncont = DIV_ROUND_UP(i, (1 << m));
+ } else {
+ m = 0;
+
+ if (order)
+ *order = 0;
+
+ *ncont = 0;
+ }
+ *shift = PAGE_SHIFT + m;
+ *count = i;
+}
+
+void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
+ int page_shift, __be64 *pas, int umr)
+{
+ int shift = page_shift - PAGE_SHIFT;
+ int mask = (1 << shift) - 1;
+ struct ib_umem_chunk *chunk;
+ int i, j, k;
+ u64 cur = 0;
+ u64 base;
+ int len;
+
+ i = 0;
+ list_for_each_entry(chunk, &umem->chunk_list, list)
+ for (j = 0; j < chunk->nmap; j++) {
+ len = sg_dma_len(&chunk->page_list[j]) >> PAGE_SHIFT;
+ base = sg_dma_address(&chunk->page_list[j]);
+ for (k = 0; k < len; k++) {
+ if (!(i & mask)) {
+ cur = base + (k << PAGE_SHIFT);
+ if (umr)
+ cur |= 3;
+
+ pas[i >> shift] = cpu_to_be64(cur);
+ mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n",
+ i >> shift, be64_to_cpu(pas[i >> shift]));
+ } else
+ mlx5_ib_dbg(dev, "=====> 0x%llx\n",
+ base + (k << PAGE_SHIFT));
+ i++;
+ }
+ }
+}
+
+int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset)
+{
+ u64 page_size;
+ u64 page_mask;
+ u64 off_size;
+ u64 off_mask;
+ u64 buf_off;
+
+ page_size = 1 << page_shift;
+ page_mask = page_size - 1;
+ buf_off = addr & page_mask;
+ off_size = page_size >> 6;
+ off_mask = off_size - 1;
+
+ if (buf_off & off_mask)
+ return -EINVAL;
+
+ *offset = buf_off >> ilog2(off_size);
+ return 0;
+}
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
new file mode 100644
index 0000000..836be91
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_IB_H
+#define MLX5_IB_H
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_smi.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cq.h>
+#include <linux/mlx5/qp.h>
+#include <linux/mlx5/srq.h>
+#include <linux/types.h>
+
+#define mlx5_ib_dbg(dev, format, arg...) \
+pr_debug("%s:%s:%d:(pid %d): " format, (dev)->ib_dev.name, __func__, \
+ __LINE__, current->pid, ##arg)
+
+#define mlx5_ib_err(dev, format, arg...) \
+pr_err("%s:%s:%d:(pid %d): " format, (dev)->ib_dev.name, __func__, \
+ __LINE__, current->pid, ##arg)
+
+#define mlx5_ib_warn(dev, format, arg...) \
+pr_warn("%s:%s:%d:(pid %d): " format, (dev)->ib_dev.name, __func__, \
+ __LINE__, current->pid, ##arg)
+
+enum {
+ MLX5_IB_MMAP_CMD_SHIFT = 8,
+ MLX5_IB_MMAP_CMD_MASK = 0xff,
+};
+
+enum mlx5_ib_mmap_cmd {
+ MLX5_IB_MMAP_REGULAR_PAGE = 0,
+ MLX5_IB_MMAP_GET_CONTIGUOUS_PAGES = 1, /* always last */
+};
+
+enum {
+ MLX5_RES_SCAT_DATA32_CQE = 0x1,
+ MLX5_RES_SCAT_DATA64_CQE = 0x2,
+ MLX5_REQ_SCAT_DATA32_CQE = 0x11,
+ MLX5_REQ_SCAT_DATA64_CQE = 0x22,
+};
+
+enum mlx5_ib_latency_class {
+ MLX5_IB_LATENCY_CLASS_LOW,
+ MLX5_IB_LATENCY_CLASS_MEDIUM,
+ MLX5_IB_LATENCY_CLASS_HIGH,
+ MLX5_IB_LATENCY_CLASS_FAST_PATH
+};
+
+enum mlx5_ib_mad_ifc_flags {
+ MLX5_MAD_IFC_IGNORE_MKEY = 1,
+ MLX5_MAD_IFC_IGNORE_BKEY = 2,
+ MLX5_MAD_IFC_NET_VIEW = 4,
+};
+
+struct mlx5_ib_ucontext {
+ struct ib_ucontext ibucontext;
+ struct list_head db_page_list;
+
+ /* protect doorbell record alloc/free
+ */
+ struct mutex db_page_mutex;
+ struct mlx5_uuar_info uuari;
+};
+
+static inline struct mlx5_ib_ucontext *to_mucontext(struct ib_ucontext *ibucontext)
+{
+ return container_of(ibucontext, struct mlx5_ib_ucontext, ibucontext);
+}
+
+struct mlx5_ib_pd {
+ struct ib_pd ibpd;
+ u32 pdn;
+ u32 pa_lkey;
+};
+
+/* Use macros here so that don't have to duplicate
+ * enum ib_send_flags and enum ib_qp_type for low-level driver
+ */
+
+#define MLX5_IB_SEND_UMR_UNREG IB_SEND_RESERVED_START
+#define MLX5_IB_QPT_REG_UMR IB_QPT_RESERVED1
+#define MLX5_IB_WR_UMR IB_WR_RESERVED1
+
+struct wr_list {
+ u16 opcode;
+ u16 next;
+};
+
+struct mlx5_ib_wq {
+ u64 *wrid;
+ u32 *wr_data;
+ struct wr_list *w_list;
+ unsigned *wqe_head;
+ u16 unsig_count;
+
+ /* serialize post to the work queue
+ */
+ spinlock_t lock;
+ int wqe_cnt;
+ int max_post;
+ int max_gs;
+ int offset;
+ int wqe_shift;
+ unsigned head;
+ unsigned tail;
+ u16 cur_post;
+ u16 last_poll;
+ void *qend;
+};
+
+enum {
+ MLX5_QP_USER,
+ MLX5_QP_KERNEL,
+ MLX5_QP_EMPTY
+};
+
+struct mlx5_ib_qp {
+ struct ib_qp ibqp;
+ struct mlx5_core_qp mqp;
+ struct mlx5_buf buf;
+
+ struct mlx5_db db;
+ struct mlx5_ib_wq rq;
+
+ u32 doorbell_qpn;
+ u8 sq_signal_bits;
+ u8 fm_cache;
+ int sq_max_wqes_per_wr;
+ int sq_spare_wqes;
+ struct mlx5_ib_wq sq;
+
+ struct ib_umem *umem;
+ int buf_size;
+
+ /* serialize qp state modifications
+ */
+ struct mutex mutex;
+ u16 xrcdn;
+ u32 flags;
+ u8 port;
+ u8 alt_port;
+ u8 atomic_rd_en;
+ u8 resp_depth;
+ u8 state;
+ int mlx_type;
+ int wq_sig;
+ int scat_cqe;
+ int max_inline_data;
+ struct mlx5_bf *bf;
+ int has_rq;
+
+ /* only for user space QPs. For kernel
+ * we have it from the bf object
+ */
+ int uuarn;
+
+ int create_type;
+ u32 pa_lkey;
+};
+
+struct mlx5_ib_cq_buf {
+ struct mlx5_buf buf;
+ struct ib_umem *umem;
+ int cqe_size;
+};
+
+enum mlx5_ib_qp_flags {
+ MLX5_IB_QP_BLOCK_MULTICAST_LOOPBACK = 1 << 0,
+ MLX5_IB_QP_SIGNATURE_HANDLING = 1 << 1,
+};
+
+struct mlx5_shared_mr_info {
+ int mr_id;
+ struct ib_umem *umem;
+};
+
+struct mlx5_ib_cq {
+ struct ib_cq ibcq;
+ struct mlx5_core_cq mcq;
+ struct mlx5_ib_cq_buf buf;
+ struct mlx5_db db;
+
+ /* serialize access to the CQ
+ */
+ spinlock_t lock;
+
+ /* protect resize cq
+ */
+ struct mutex resize_mutex;
+ struct mlx5_ib_cq_resize *resize_buf;
+ struct ib_umem *resize_umem;
+ int cqe_size;
+};
+
+struct mlx5_ib_srq {
+ struct ib_srq ibsrq;
+ struct mlx5_core_srq msrq;
+ struct mlx5_buf buf;
+ struct mlx5_db db;
+ u64 *wrid;
+ /* protect SRQ hanlding
+ */
+ spinlock_t lock;
+ int head;
+ int tail;
+ u16 wqe_ctr;
+ struct ib_umem *umem;
+ /* serialize arming a SRQ
+ */
+ struct mutex mutex;
+ int wq_sig;
+};
+
+struct mlx5_ib_xrcd {
+ struct ib_xrcd ibxrcd;
+ u32 xrcdn;
+};
+
+struct mlx5_ib_mr {
+ struct ib_mr ibmr;
+ struct mlx5_core_mr mmr;
+ struct ib_umem *umem;
+ struct mlx5_shared_mr_info *smr_info;
+ struct list_head list;
+ int order;
+ int umred;
+ __be64 *pas;
+ dma_addr_t dma;
+ int npages;
+ struct completion done;
+ enum ib_wc_status status;
+};
+
+struct mlx5_ib_fast_reg_page_list {
+ struct ib_fast_reg_page_list ibfrpl;
+ __be64 *mapped_page_list;
+ dma_addr_t map;
+};
+
+struct umr_common {
+ struct ib_pd *pd;
+ struct ib_cq *cq;
+ struct ib_qp *qp;
+ struct ib_mr *mr;
+ /* control access to UMR QP
+ */
+ struct semaphore sem;
+};
+
+enum {
+ MLX5_FMR_INVALID,
+ MLX5_FMR_VALID,
+ MLX5_FMR_BUSY,
+};
+
+struct mlx5_ib_fmr {
+ struct ib_fmr ibfmr;
+ struct mlx5_core_mr mr;
+ int access_flags;
+ int state;
+ /* protect fmr state
+ */
+ spinlock_t lock;
+ u64 wrid;
+ struct ib_send_wr wr[2];
+ u8 page_shift;
+ struct ib_fast_reg_page_list page_list;
+};
+
+struct mlx5_cache_ent {
+ struct list_head head;
+ /* sync access to the cahce entry
+ */
+ spinlock_t lock;
+
+
+ struct dentry *dir;
+ char name[4];
+ u32 order;
+ u32 size;
+ u32 cur;
+ u32 miss;
+ u32 limit;
+
+ struct dentry *fsize;
+ struct dentry *fcur;
+ struct dentry *fmiss;
+ struct dentry *flimit;
+
+ struct mlx5_ib_dev *dev;
+ struct work_struct work;
+ struct delayed_work dwork;
+};
+
+struct mlx5_mr_cache {
+ struct workqueue_struct *wq;
+ struct mlx5_cache_ent ent[MAX_MR_CACHE_ENTRIES];
+ int stopped;
+ struct dentry *root;
+ unsigned long last_add;
+};
+
+struct mlx5_ib_resources {
+ struct ib_cq *c0;
+ struct ib_xrcd *x0;
+ struct ib_xrcd *x1;
+ struct ib_pd *p0;
+ struct ib_srq *s0;
+};
+
+struct mlx5_ib_dev {
+ struct ib_device ib_dev;
+ struct mlx5_core_dev mdev;
+ MLX5_DECLARE_DOORBELL_LOCK(uar_lock);
+ struct list_head eqs_list;
+ int num_ports;
+ int num_comp_vectors;
+ /* serialize update of capability mask
+ */
+ struct mutex cap_mask_mutex;
+ bool ib_active;
+ struct umr_common umrc;
+ /* sync used page count stats
+ */
+ spinlock_t mr_lock;
+ struct mlx5_ib_resources devr;
+ struct mlx5_mr_cache cache;
+};
+
+static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
+{
+ return container_of(mcq, struct mlx5_ib_cq, mcq);
+}
+
+static inline struct mlx5_ib_xrcd *to_mxrcd(struct ib_xrcd *ibxrcd)
+{
+ return container_of(ibxrcd, struct mlx5_ib_xrcd, ibxrcd);
+}
+
+static inline struct mlx5_ib_dev *to_mdev(struct ib_device *ibdev)
+{
+ return container_of(ibdev, struct mlx5_ib_dev, ib_dev);
+}
+
+static inline struct mlx5_ib_fmr *to_mfmr(struct ib_fmr *ibfmr)
+{
+ return container_of(ibfmr, struct mlx5_ib_fmr, ibfmr);
+}
+
+static inline struct mlx5_ib_cq *to_mcq(struct ib_cq *ibcq)
+{
+ return container_of(ibcq, struct mlx5_ib_cq, ibcq);
+}
+
+static inline struct mlx5_ib_qp *to_mibqp(struct mlx5_core_qp *mqp)
+{
+ return container_of(mqp, struct mlx5_ib_qp, mqp);
+}
+
+static inline struct mlx5_ib_pd *to_mpd(struct ib_pd *ibpd)
+{
+ return container_of(ibpd, struct mlx5_ib_pd, ibpd);
+}
+
+static inline struct mlx5_ib_srq *to_msrq(struct ib_srq *ibsrq)
+{
+ return container_of(ibsrq, struct mlx5_ib_srq, ibsrq);
+}
+
+static inline struct mlx5_ib_qp *to_mqp(struct ib_qp *ibqp)
+{
+ return container_of(ibqp, struct mlx5_ib_qp, ibqp);
+}
+
+static inline struct mlx5_ib_srq *to_mibsrq(struct mlx5_core_srq *msrq)
+{
+ return container_of(msrq, struct mlx5_ib_srq, msrq);
+}
+
+static inline struct mlx5_ib_mr *to_mmr(struct ib_mr *ibmr)
+{
+ return container_of(ibmr, struct mlx5_ib_mr, ibmr);
+}
+
+static inline struct mlx5_ib_fast_reg_page_list *to_mfrpl(struct ib_fast_reg_page_list *ibfrpl)
+{
+ return container_of(ibfrpl, struct mlx5_ib_fast_reg_page_list, ibfrpl);
+}
+
+struct mlx5_ib_ah {
+ struct ib_ah ibah;
+ struct mlx5_av av;
+};
+
+static inline struct mlx5_ib_ah *to_mah(struct ib_ah *ibah)
+{
+ return container_of(ibah, struct mlx5_ib_ah, ibah);
+}
+
+static inline struct mlx5_ib_dev *mlx5_core2ibdev(struct mlx5_core_dev *dev)
+{
+ return container_of(dev, struct mlx5_ib_dev, mdev);
+}
+
+static inline struct mlx5_ib_dev *mlx5_pci2ibdev(struct pci_dev *pdev)
+{
+ return mlx5_core2ibdev(pci2mlx5_core_dev(pdev));
+}
+
+int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt,
+ struct mlx5_db *db);
+void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db);
+void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
+void mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
+void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index);
+int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
+ int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
+ void *in_mad, void *response_mad);
+struct ib_ah *create_ib_ah(struct ib_ah_attr *ah_attr,
+ struct mlx5_ib_ah *ah);
+struct ib_ah *mlx5_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr);
+int mlx5_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr);
+int mlx5_ib_destroy_ah(struct ib_ah *ah);
+struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
+ struct ib_srq_init_attr *init_attr,
+ struct ib_udata *udata);
+int mlx5_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
+ enum ib_srq_attr_mask attr_mask, struct ib_udata *udata);
+int mlx5_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr);
+int mlx5_ib_destroy_srq(struct ib_srq *srq);
+int mlx5_ib_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
+ struct ib_recv_wr **bad_wr);
+struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd,
+ struct ib_qp_init_attr *init_attr,
+ struct ib_udata *udata);
+int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+ int attr_mask, struct ib_udata *udata);
+int mlx5_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_mask,
+ struct ib_qp_init_attr *qp_init_attr);
+int mlx5_ib_destroy_qp(struct ib_qp *qp);
+int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+ struct ib_send_wr **bad_wr);
+int mlx5_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+ struct ib_recv_wr **bad_wr);
+void *mlx5_get_send_wqe(struct mlx5_ib_qp *qp, int n);
+struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
+ int vector, struct ib_ucontext *context,
+ struct ib_udata *udata);
+int mlx5_ib_destroy_cq(struct ib_cq *cq);
+int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
+int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
+int mlx5_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period);
+int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata);
+struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc);
+struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
+ u64 virt_addr, int access_flags,
+ struct ib_udata *udata);
+int mlx5_ib_dereg_mr(struct ib_mr *ibmr);
+struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd,
+ int max_page_list_len);
+struct ib_fast_reg_page_list *mlx5_ib_alloc_fast_reg_page_list(struct ib_device *ibdev,
+ int page_list_len);
+void mlx5_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list);
+struct ib_fmr *mlx5_ib_fmr_alloc(struct ib_pd *pd, int acc,
+ struct ib_fmr_attr *fmr_attr);
+int mlx5_ib_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list,
+ int npages, u64 iova);
+int mlx5_ib_unmap_fmr(struct list_head *fmr_list);
+int mlx5_ib_fmr_dealloc(struct ib_fmr *ibfmr);
+int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
+ struct ib_wc *in_wc, struct ib_grh *in_grh,
+ struct ib_mad *in_mad, struct ib_mad *out_mad);
+struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata);
+int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd);
+int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn);
+int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset);
+int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port);
+int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
+ struct ib_port_attr *props);
+int mlx5_ib_init_fmr(struct mlx5_ib_dev *dev);
+void mlx5_ib_cleanup_fmr(struct mlx5_ib_dev *dev);
+void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
+ int *ncont, int *order);
+void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
+ int page_shift, __be64 *pas, int umr);
+void mlx5_ib_copy_pas(u64 *old, u64 *new, int step, int num);
+int mlx5_ib_get_cqe_size(struct mlx5_ib_dev *dev, struct ib_cq *ibcq);
+int mlx5_mr_cache_init(struct mlx5_ib_dev *dev);
+int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev);
+int mlx5_mr_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift);
+void mlx5_umr_cq_handler(struct ib_cq *cq, void *cq_context);
+
+static inline void init_query_mad(struct ib_smp *mad)
+{
+ mad->base_version = 1;
+ mad->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+ mad->class_version = 1;
+ mad->method = IB_MGMT_METHOD_GET;
+}
+
+static inline u8 convert_access(int acc)
+{
+ return (acc & IB_ACCESS_REMOTE_ATOMIC ? MLX5_PERM_ATOMIC : 0) |
+ (acc & IB_ACCESS_REMOTE_WRITE ? MLX5_PERM_REMOTE_WRITE : 0) |
+ (acc & IB_ACCESS_REMOTE_READ ? MLX5_PERM_REMOTE_READ : 0) |
+ (acc & IB_ACCESS_LOCAL_WRITE ? MLX5_PERM_LOCAL_WRITE : 0) |
+ MLX5_PERM_LOCAL_READ;
+}
+
+#endif /* MLX5_IB_H */
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
new file mode 100644
index 0000000..bd41df9
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+
+#include <linux/kref.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <rdma/ib_umem.h>
+#include "mlx5_ib.h"
+
+enum {
+ DEF_CACHE_SIZE = 10,
+};
+
+static __be64 *mr_align(__be64 *ptr, int align)
+{
+ unsigned long mask = align - 1;
+
+ return (__be64 *)(((unsigned long)ptr + mask) & ~mask);
+}
+
+static int order2idx(struct mlx5_ib_dev *dev, int order)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+
+ if (order < cache->ent[0].order)
+ return 0;
+ else
+ return order - cache->ent[0].order;
+}
+
+static int add_keys(struct mlx5_ib_dev *dev, int c, int num)
+{
+ struct device *ddev = dev->ib_dev.dma_device;
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent = &cache->ent[c];
+ struct mlx5_create_mkey_mbox_in *in;
+ struct mlx5_ib_mr *mr;
+ int npages = 1 << ent->order;
+ int size = sizeof(u64) * npages;
+ int err = 0;
+ int i;
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++) {
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mr->order = ent->order;
+ mr->umred = 1;
+ mr->pas = kmalloc(size + 0x3f, GFP_KERNEL);
+ if (!mr->pas) {
+ kfree(mr);
+ err = -ENOMEM;
+ goto out;
+ }
+ mr->dma = dma_map_single(ddev, mr_align(mr->pas, 0x40), size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(ddev, mr->dma)) {
+ kfree(mr->pas);
+ kfree(mr);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in->seg.status = 1 << 6;
+ in->seg.xlt_oct_size = cpu_to_be32((npages + 1) / 2);
+ in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+ in->seg.flags = MLX5_ACCESS_MODE_MTT | MLX5_PERM_UMR_EN;
+ in->seg.log2_page_size = 12;
+
+ err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in,
+ sizeof(*in));
+ if (err) {
+ mlx5_ib_warn(dev, "create mkey failed %d\n", err);
+ dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
+ kfree(mr->pas);
+ kfree(mr);
+ goto out;
+ }
+ cache->last_add = jiffies;
+
+ spin_lock(&ent->lock);
+ list_add_tail(&mr->list, &ent->head);
+ ent->cur++;
+ ent->size++;
+ spin_unlock(&ent->lock);
+ }
+
+out:
+ kfree(in);
+ return err;
+}
+
+static void remove_keys(struct mlx5_ib_dev *dev, int c, int num)
+{
+ struct device *ddev = dev->ib_dev.dma_device;
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent = &cache->ent[c];
+ struct mlx5_ib_mr *mr;
+ int size;
+ int err;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ spin_lock(&ent->lock);
+ if (list_empty(&ent->head)) {
+ spin_unlock(&ent->lock);
+ return;
+ }
+ mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list);
+ list_del(&mr->list);
+ ent->cur--;
+ ent->size--;
+ spin_unlock(&ent->lock);
+ err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
+ if (err) {
+ mlx5_ib_warn(dev, "failed destroy mkey\n");
+ } else {
+ size = ALIGN(sizeof(u64) * (1 << mr->order), 0x40);
+ dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
+ kfree(mr->pas);
+ kfree(mr);
+ }
+ }
+}
+
+static ssize_t size_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mlx5_cache_ent *ent = filp->private_data;
+ struct mlx5_ib_dev *dev = ent->dev;
+ char lbuf[20];
+ u32 var;
+ int err;
+ int c;
+
+ if (copy_from_user(lbuf, buf, sizeof(lbuf)))
+ return -EFAULT;
+
+ c = order2idx(dev, ent->order);
+ lbuf[sizeof(lbuf) - 1] = 0;
+
+ if (sscanf(lbuf, "%u", &var) != 1)
+ return -EINVAL;
+
+ if (var < ent->limit)
+ return -EINVAL;
+
+ if (var > ent->size) {
+ err = add_keys(dev, c, var - ent->size);
+ if (err)
+ return err;
+ } else if (var < ent->size) {
+ remove_keys(dev, c, ent->size - var);
+ }
+
+ return count;
+}
+
+static ssize_t size_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct mlx5_cache_ent *ent = filp->private_data;
+ char lbuf[20];
+ int err;
+
+ if (*pos)
+ return 0;
+
+ err = snprintf(lbuf, sizeof(lbuf), "%d\n", ent->size);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(buf, lbuf, err))
+ return -EFAULT;
+
+ *pos += err;
+
+ return err;
+}
+
+static const struct file_operations size_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = size_write,
+ .read = size_read,
+};
+
+static ssize_t limit_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mlx5_cache_ent *ent = filp->private_data;
+ struct mlx5_ib_dev *dev = ent->dev;
+ char lbuf[20];
+ u32 var;
+ int err;
+ int c;
+
+ if (copy_from_user(lbuf, buf, sizeof(lbuf)))
+ return -EFAULT;
+
+ c = order2idx(dev, ent->order);
+ lbuf[sizeof(lbuf) - 1] = 0;
+
+ if (sscanf(lbuf, "%u", &var) != 1)
+ return -EINVAL;
+
+ if (var > ent->size)
+ return -EINVAL;
+
+ ent->limit = var;
+
+ if (ent->cur < ent->limit) {
+ err = add_keys(dev, c, 2 * ent->limit - ent->cur);
+ if (err)
+ return err;
+ }
+
+ return count;
+}
+
+static ssize_t limit_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct mlx5_cache_ent *ent = filp->private_data;
+ char lbuf[20];
+ int err;
+
+ if (*pos)
+ return 0;
+
+ err = snprintf(lbuf, sizeof(lbuf), "%d\n", ent->limit);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(buf, lbuf, err))
+ return -EFAULT;
+
+ *pos += err;
+
+ return err;
+}
+
+static const struct file_operations limit_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = limit_write,
+ .read = limit_read,
+};
+
+static int someone_adding(struct mlx5_mr_cache *cache)
+{
+ int i;
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ if (cache->ent[i].cur < cache->ent[i].limit)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void __cache_work_func(struct mlx5_cache_ent *ent)
+{
+ struct mlx5_ib_dev *dev = ent->dev;
+ struct mlx5_mr_cache *cache = &dev->cache;
+ int i = order2idx(dev, ent->order);
+
+ if (cache->stopped)
+ return;
+
+ ent = &dev->cache.ent[i];
+ if (ent->cur < 2 * ent->limit) {
+ add_keys(dev, i, 1);
+ if (ent->cur < 2 * ent->limit)
+ queue_work(cache->wq, &ent->work);
+ } else if (ent->cur > 2 * ent->limit) {
+ if (!someone_adding(cache) &&
+ time_after(jiffies, cache->last_add + 60 * HZ)) {
+ remove_keys(dev, i, 1);
+ if (ent->cur > ent->limit)
+ queue_work(cache->wq, &ent->work);
+ } else {
+ queue_delayed_work(cache->wq, &ent->dwork, 60 * HZ);
+ }
+ }
+}
+
+static void delayed_cache_work_func(struct work_struct *work)
+{
+ struct mlx5_cache_ent *ent;
+
+ ent = container_of(work, struct mlx5_cache_ent, dwork.work);
+ __cache_work_func(ent);
+}
+
+static void cache_work_func(struct work_struct *work)
+{
+ struct mlx5_cache_ent *ent;
+
+ ent = container_of(work, struct mlx5_cache_ent, work);
+ __cache_work_func(ent);
+}
+
+static struct mlx5_ib_mr *alloc_cached_mr(struct mlx5_ib_dev *dev, int order)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_ib_mr *mr = NULL;
+ struct mlx5_cache_ent *ent;
+ int c;
+ int i;
+
+ c = order2idx(dev, order);
+ if (c < 0 || c >= MAX_MR_CACHE_ENTRIES) {
+ mlx5_ib_warn(dev, "order %d, cache index %d\n", order, c);
+ return NULL;
+ }
+
+ for (i = c; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+
+ mlx5_ib_dbg(dev, "order %d, cache index %d\n", ent->order, i);
+
+ spin_lock(&ent->lock);
+ if (!list_empty(&ent->head)) {
+ mr = list_first_entry(&ent->head, struct mlx5_ib_mr,
+ list);
+ list_del(&mr->list);
+ ent->cur--;
+ spin_unlock(&ent->lock);
+ if (ent->cur < ent->limit)
+ queue_work(cache->wq, &ent->work);
+ break;
+ }
+ spin_unlock(&ent->lock);
+
+ queue_work(cache->wq, &ent->work);
+
+ if (mr)
+ break;
+ }
+
+ if (!mr)
+ cache->ent[c].miss++;
+
+ return mr;
+}
+
+static void free_cached_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent;
+ int shrink = 0;
+ int c;
+
+ c = order2idx(dev, mr->order);
+ if (c < 0 || c >= MAX_MR_CACHE_ENTRIES) {
+ mlx5_ib_warn(dev, "order %d, cache index %d\n", mr->order, c);
+ return;
+ }
+ ent = &cache->ent[c];
+ spin_lock(&ent->lock);
+ list_add_tail(&mr->list, &ent->head);
+ ent->cur++;
+ if (ent->cur > 2 * ent->limit)
+ shrink = 1;
+ spin_unlock(&ent->lock);
+
+ if (shrink)
+ queue_work(cache->wq, &ent->work);
+}
+
+static void clean_keys(struct mlx5_ib_dev *dev, int c)
+{
+ struct device *ddev = dev->ib_dev.dma_device;
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent = &cache->ent[c];
+ struct mlx5_ib_mr *mr;
+ int size;
+ int err;
+
+ while (1) {
+ spin_lock(&ent->lock);
+ if (list_empty(&ent->head)) {
+ spin_unlock(&ent->lock);
+ return;
+ }
+ mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list);
+ list_del(&mr->list);
+ ent->cur--;
+ ent->size--;
+ spin_unlock(&ent->lock);
+ err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
+ if (err) {
+ mlx5_ib_warn(dev, "failed destroy mkey\n");
+ } else {
+ size = ALIGN(sizeof(u64) * (1 << mr->order), 0x40);
+ dma_unmap_single(ddev, mr->dma, size, DMA_TO_DEVICE);
+ kfree(mr->pas);
+ kfree(mr);
+ }
+ }
+}
+
+static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent;
+ int i;
+
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ cache->root = debugfs_create_dir("mr_cache", dev->mdev.priv.dbg_root);
+ if (!cache->root)
+ return -ENOMEM;
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ sprintf(ent->name, "%d", ent->order);
+ ent->dir = debugfs_create_dir(ent->name, cache->root);
+ if (!ent->dir)
+ return -ENOMEM;
+
+ ent->fsize = debugfs_create_file("size", 0600, ent->dir, ent,
+ &size_fops);
+ if (!ent->fsize)
+ return -ENOMEM;
+
+ ent->flimit = debugfs_create_file("limit", 0600, ent->dir, ent,
+ &limit_fops);
+ if (!ent->flimit)
+ return -ENOMEM;
+
+ ent->fcur = debugfs_create_u32("cur", 0400, ent->dir,
+ &ent->cur);
+ if (!ent->fcur)
+ return -ENOMEM;
+
+ ent->fmiss = debugfs_create_u32("miss", 0600, ent->dir,
+ &ent->miss);
+ if (!ent->fmiss)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ debugfs_remove_recursive(dev->cache.root);
+}
+
+int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent;
+ int limit;
+ int size;
+ int err;
+ int i;
+
+ cache->wq = create_singlethread_workqueue("mkey_cache");
+ if (!cache->wq) {
+ mlx5_ib_warn(dev, "failed to create work queue\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ INIT_LIST_HEAD(&cache->ent[i].head);
+ spin_lock_init(&cache->ent[i].lock);
+
+ ent = &cache->ent[i];
+ INIT_LIST_HEAD(&ent->head);
+ spin_lock_init(&ent->lock);
+ ent->order = i + 2;
+ ent->dev = dev;
+
+ if (dev->mdev.profile->mask & MLX5_PROF_MASK_MR_CACHE) {
+ size = dev->mdev.profile->mr_cache[i].size;
+ limit = dev->mdev.profile->mr_cache[i].limit;
+ } else {
+ size = DEF_CACHE_SIZE;
+ limit = 0;
+ }
+ INIT_WORK(&ent->work, cache_work_func);
+ INIT_DELAYED_WORK(&ent->dwork, delayed_cache_work_func);
+ ent->limit = limit;
+ queue_work(cache->wq, &ent->work);
+ }
+
+ err = mlx5_mr_cache_debugfs_init(dev);
+ if (err)
+ mlx5_ib_warn(dev, "cache debugfs failure\n");
+
+ return 0;
+}
+
+int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
+{
+ int i;
+
+ dev->cache.stopped = 1;
+ destroy_workqueue(dev->cache.wq);
+
+ mlx5_mr_cache_debugfs_cleanup(dev);
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++)
+ clean_keys(dev, i);
+
+ return 0;
+}
+
+struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_core_dev *mdev = &dev->mdev;
+ struct mlx5_create_mkey_mbox_in *in;
+ struct mlx5_mkey_seg *seg;
+ struct mlx5_ib_mr *mr;
+ int err;
+
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ seg = &in->seg;
+ seg->flags = convert_access(acc) | MLX5_ACCESS_MODE_PA;
+ seg->flags_pd = cpu_to_be32(to_mpd(pd)->pdn | MLX5_MKEY_LEN64);
+ seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+ seg->start_addr = 0;
+
+ err = mlx5_core_create_mkey(mdev, &mr->mmr, in, sizeof(*in));
+ if (err)
+ goto err_in;
+
+ kfree(in);
+ mr->ibmr.lkey = mr->mmr.key;
+ mr->ibmr.rkey = mr->mmr.key;
+ mr->umem = NULL;
+
+ return &mr->ibmr;
+
+err_in:
+ kfree(in);
+
+err_free:
+ kfree(mr);
+
+ return ERR_PTR(err);
+}
+
+static int get_octo_len(u64 addr, u64 len, int page_size)
+{
+ u64 offset;
+ int npages;
+
+ offset = addr & (page_size - 1);
+ npages = ALIGN(len + offset, page_size) >> ilog2(page_size);
+ return (npages + 1) / 2;
+}
+
+static int use_umr(int order)
+{
+ return order <= 17;
+}
+
+static void prep_umr_reg_wqe(struct ib_pd *pd, struct ib_send_wr *wr,
+ struct ib_sge *sg, u64 dma, int n, u32 key,
+ int page_shift, u64 virt_addr, u64 len,
+ int access_flags)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct ib_mr *mr = dev->umrc.mr;
+
+ sg->addr = dma;
+ sg->length = ALIGN(sizeof(u64) * n, 64);
+ sg->lkey = mr->lkey;
+
+ wr->next = NULL;
+ wr->send_flags = 0;
+ wr->sg_list = sg;
+ if (n)
+ wr->num_sge = 1;
+ else
+ wr->num_sge = 0;
+
+ wr->opcode = MLX5_IB_WR_UMR;
+ wr->wr.fast_reg.page_list_len = n;
+ wr->wr.fast_reg.page_shift = page_shift;
+ wr->wr.fast_reg.rkey = key;
+ wr->wr.fast_reg.iova_start = virt_addr;
+ wr->wr.fast_reg.length = len;
+ wr->wr.fast_reg.access_flags = access_flags;
+ wr->wr.fast_reg.page_list = (struct ib_fast_reg_page_list *)pd;
+}
+
+static void prep_umr_unreg_wqe(struct mlx5_ib_dev *dev,
+ struct ib_send_wr *wr, u32 key)
+{
+ wr->send_flags = MLX5_IB_SEND_UMR_UNREG;
+ wr->opcode = MLX5_IB_WR_UMR;
+ wr->wr.fast_reg.rkey = key;
+}
+
+void mlx5_umr_cq_handler(struct ib_cq *cq, void *cq_context)
+{
+ struct mlx5_ib_mr *mr;
+ struct ib_wc wc;
+ int err;
+
+ while (1) {
+ err = ib_poll_cq(cq, 1, &wc);
+ if (err < 0) {
+ pr_warn("poll cq error %d\n", err);
+ return;
+ }
+ if (err == 0)
+ break;
+
+ mr = (struct mlx5_ib_mr *)(unsigned long)wc.wr_id;
+ mr->status = wc.status;
+ complete(&mr->done);
+ }
+ ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+}
+
+static struct mlx5_ib_mr *reg_umr(struct ib_pd *pd, struct ib_umem *umem,
+ u64 virt_addr, u64 len, int npages,
+ int page_shift, int order, int access_flags)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct umr_common *umrc = &dev->umrc;
+ struct ib_send_wr wr, *bad;
+ struct mlx5_ib_mr *mr;
+ struct ib_sge sg;
+ int err;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ mr = alloc_cached_mr(dev, order);
+ if (mr)
+ break;
+
+ err = add_keys(dev, order2idx(dev, order), 1);
+ if (err) {
+ mlx5_ib_warn(dev, "add_keys failed\n");
+ break;
+ }
+ }
+
+ if (!mr)
+ return ERR_PTR(-EAGAIN);
+
+ mlx5_ib_populate_pas(dev, umem, page_shift, mr_align(mr->pas, 0x40), 1);
+
+ memset(&wr, 0, sizeof(wr));
+ wr.wr_id = (u64)(unsigned long)mr;
+ prep_umr_reg_wqe(pd, &wr, &sg, mr->dma, npages, mr->mmr.key, page_shift, virt_addr, len, access_flags);
+
+ /* We serialize polls so one process does not kidnap another's
+ * completion. This is not a problem since wr is completed in
+ * around 1 usec
+ */
+ down(&umrc->sem);
+ init_completion(&mr->done);
+ err = ib_post_send(umrc->qp, &wr, &bad);
+ if (err) {
+ mlx5_ib_warn(dev, "post send failed, err %d\n", err);
+ up(&umrc->sem);
+ goto error;
+ }
+ wait_for_completion(&mr->done);
+ up(&umrc->sem);
+
+ if (mr->status != IB_WC_SUCCESS) {
+ mlx5_ib_warn(dev, "reg umr failed\n");
+ err = -EFAULT;
+ goto error;
+ }
+
+ return mr;
+
+error:
+ free_cached_mr(dev, mr);
+ return ERR_PTR(err);
+}
+
+static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
+ u64 length, struct ib_umem *umem,
+ int npages, int page_shift,
+ int access_flags)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_create_mkey_mbox_in *in;
+ struct mlx5_ib_mr *mr;
+ int inlen;
+ int err;
+
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ inlen = sizeof(*in) + sizeof(*in->pas) * ((npages + 1) / 2) * 2;
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_1;
+ }
+ mlx5_ib_populate_pas(dev, umem, page_shift, in->pas, 0);
+
+ in->seg.flags = convert_access(access_flags) |
+ MLX5_ACCESS_MODE_MTT;
+ in->seg.flags_pd = cpu_to_be32(to_mpd(pd)->pdn);
+ in->seg.start_addr = cpu_to_be64(virt_addr);
+ in->seg.len = cpu_to_be64(length);
+ in->seg.bsfs_octo_size = 0;
+ in->seg.xlt_oct_size = cpu_to_be32(get_octo_len(virt_addr, length, 1 << page_shift));
+ in->seg.log2_page_size = page_shift;
+ in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+ in->xlat_oct_act_size = cpu_to_be32(get_octo_len(virt_addr, length, 1 << page_shift));
+ err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, inlen);
+ if (err) {
+ mlx5_ib_warn(dev, "create mkey failed\n");
+ goto err_2;
+ }
+ mr->umem = umem;
+ mlx5_vfree(in);
+
+ mlx5_ib_dbg(dev, "mkey = 0x%x\n", mr->mmr.key);
+
+ return mr;
+
+err_2:
+ mlx5_vfree(in);
+
+err_1:
+ kfree(mr);
+
+ return ERR_PTR(err);
+}
+
+struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
+ u64 virt_addr, int access_flags,
+ struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_ib_mr *mr = NULL;
+ struct ib_umem *umem;
+ int page_shift;
+ int npages;
+ int ncont;
+ int order;
+ int err;
+
+ mlx5_ib_dbg(dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx\n",
+ start, virt_addr, length);
+ umem = ib_umem_get(pd->uobject->context, start, length, access_flags,
+ 0);
+ if (IS_ERR(umem)) {
+ mlx5_ib_dbg(dev, "umem get failed\n");
+ return (void *)umem;
+ }
+
+ mlx5_ib_cont_pages(umem, start, &npages, &page_shift, &ncont, &order);
+ if (!npages) {
+ mlx5_ib_warn(dev, "avoid zero region\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ mlx5_ib_dbg(dev, "npages %d, ncont %d, order %d, page_shift %d\n",
+ npages, ncont, order, page_shift);
+
+ if (use_umr(order)) {
+ mr = reg_umr(pd, umem, virt_addr, length, ncont, page_shift,
+ order, access_flags);
+ if (PTR_ERR(mr) == -EAGAIN) {
+ mlx5_ib_dbg(dev, "cache empty for order %d", order);
+ mr = NULL;
+ }
+ }
+
+ if (!mr)
+ mr = reg_create(pd, virt_addr, length, umem, ncont, page_shift,
+ access_flags);
+
+ if (IS_ERR(mr)) {
+ err = PTR_ERR(mr);
+ goto error;
+ }
+
+ mlx5_ib_dbg(dev, "mkey 0x%x\n", mr->mmr.key);
+
+ mr->umem = umem;
+ mr->npages = npages;
+ spin_lock(&dev->mr_lock);
+ dev->mdev.priv.reg_pages += npages;
+ spin_unlock(&dev->mr_lock);
+ mr->ibmr.lkey = mr->mmr.key;
+ mr->ibmr.rkey = mr->mmr.key;
+
+ return &mr->ibmr;
+
+error:
+ ib_umem_release(umem);
+ return ERR_PTR(err);
+}
+
+static int unreg_umr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr)
+{
+ struct umr_common *umrc = &dev->umrc;
+ struct ib_send_wr wr, *bad;
+ int err;
+
+ memset(&wr, 0, sizeof(wr));
+ wr.wr_id = (u64)(unsigned long)mr;
+ prep_umr_unreg_wqe(dev, &wr, mr->mmr.key);
+
+ down(&umrc->sem);
+ init_completion(&mr->done);
+ err = ib_post_send(umrc->qp, &wr, &bad);
+ if (err) {
+ up(&umrc->sem);
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ goto error;
+ }
+ wait_for_completion(&mr->done);
+ up(&umrc->sem);
+ if (mr->status != IB_WC_SUCCESS) {
+ mlx5_ib_warn(dev, "unreg umr failed\n");
+ err = -EFAULT;
+ goto error;
+ }
+ return 0;
+
+error:
+ return err;
+}
+
+int mlx5_ib_dereg_mr(struct ib_mr *ibmr)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibmr->device);
+ struct mlx5_ib_mr *mr = to_mmr(ibmr);
+ struct ib_umem *umem = mr->umem;
+ int npages = mr->npages;
+ int umred = mr->umred;
+ int err;
+
+ if (!umred) {
+ err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr);
+ if (err) {
+ mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n",
+ mr->mmr.key, err);
+ return err;
+ }
+ } else {
+ err = unreg_umr(dev, mr);
+ if (err) {
+ mlx5_ib_warn(dev, "failed unregister\n");
+ return err;
+ }
+ free_cached_mr(dev, mr);
+ }
+
+ if (umem) {
+ ib_umem_release(umem);
+ spin_lock(&dev->mr_lock);
+ dev->mdev.priv.reg_pages -= npages;
+ spin_unlock(&dev->mr_lock);
+ }
+
+ if (!umred)
+ kfree(mr);
+
+ return 0;
+}
+
+struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd,
+ int max_page_list_len)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_create_mkey_mbox_in *in;
+ struct mlx5_ib_mr *mr;
+ int err;
+
+ mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ in->seg.status = 1 << 6; /* free */
+ in->seg.xlt_oct_size = cpu_to_be32((max_page_list_len + 1) / 2);
+ in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+ in->seg.flags = MLX5_PERM_UMR_EN | MLX5_ACCESS_MODE_MTT;
+ in->seg.flags_pd = cpu_to_be32(to_mpd(pd)->pdn);
+ /*
+ * TBD not needed - issue 197292 */
+ in->seg.log2_page_size = PAGE_SHIFT;
+
+ err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, sizeof(*in));
+ kfree(in);
+ if (err)
+ goto err_free;
+
+ mr->ibmr.lkey = mr->mmr.key;
+ mr->ibmr.rkey = mr->mmr.key;
+ mr->umem = NULL;
+
+ return &mr->ibmr;
+
+err_free:
+ kfree(mr);
+ return ERR_PTR(err);
+}
+
+struct ib_fast_reg_page_list *mlx5_ib_alloc_fast_reg_page_list(struct ib_device *ibdev,
+ int page_list_len)
+{
+ struct mlx5_ib_fast_reg_page_list *mfrpl;
+ int size = page_list_len * sizeof(u64);
+
+ mfrpl = kmalloc(sizeof(*mfrpl), GFP_KERNEL);
+ if (!mfrpl)
+ return ERR_PTR(-ENOMEM);
+
+ mfrpl->ibfrpl.page_list = kmalloc(size, GFP_KERNEL);
+ if (!mfrpl->ibfrpl.page_list)
+ goto err_free;
+
+ mfrpl->mapped_page_list = dma_alloc_coherent(ibdev->dma_device,
+ size, &mfrpl->map,
+ GFP_KERNEL);
+ if (!mfrpl->mapped_page_list)
+ goto err_free;
+
+ WARN_ON(mfrpl->map & 0x3f);
+
+ return &mfrpl->ibfrpl;
+
+err_free:
+ kfree(mfrpl->ibfrpl.page_list);
+ kfree(mfrpl);
+ return ERR_PTR(-ENOMEM);
+}
+
+void mlx5_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list)
+{
+ struct mlx5_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list);
+ struct mlx5_ib_dev *dev = to_mdev(page_list->device);
+ int size = page_list->max_page_list_len * sizeof(u64);
+
+ dma_free_coherent(&dev->mdev.pdev->dev, size, mfrpl->mapped_page_list,
+ mfrpl->map);
+ kfree(mfrpl->ibfrpl.page_list);
+ kfree(mfrpl);
+}
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
new file mode 100644
index 0000000..16ac54c
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -0,0 +1,2524 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/module.h>
+#include <rdma/ib_umem.h>
+#include "mlx5_ib.h"
+#include "user.h"
+
+/* not supported currently */
+static int wq_signature;
+
+enum {
+ MLX5_IB_ACK_REQ_FREQ = 8,
+};
+
+enum {
+ MLX5_IB_DEFAULT_SCHED_QUEUE = 0x83,
+ MLX5_IB_DEFAULT_QP0_SCHED_QUEUE = 0x3f,
+ MLX5_IB_LINK_TYPE_IB = 0,
+ MLX5_IB_LINK_TYPE_ETH = 1
+};
+
+enum {
+ MLX5_IB_SQ_STRIDE = 6,
+ MLX5_IB_CACHE_LINE_SIZE = 64,
+};
+
+static const u32 mlx5_ib_opcode[] = {
+ [IB_WR_SEND] = MLX5_OPCODE_SEND,
+ [IB_WR_SEND_WITH_IMM] = MLX5_OPCODE_SEND_IMM,
+ [IB_WR_RDMA_WRITE] = MLX5_OPCODE_RDMA_WRITE,
+ [IB_WR_RDMA_WRITE_WITH_IMM] = MLX5_OPCODE_RDMA_WRITE_IMM,
+ [IB_WR_RDMA_READ] = MLX5_OPCODE_RDMA_READ,
+ [IB_WR_ATOMIC_CMP_AND_SWP] = MLX5_OPCODE_ATOMIC_CS,
+ [IB_WR_ATOMIC_FETCH_AND_ADD] = MLX5_OPCODE_ATOMIC_FA,
+ [IB_WR_SEND_WITH_INV] = MLX5_OPCODE_SEND_INVAL,
+ [IB_WR_LOCAL_INV] = MLX5_OPCODE_UMR,
+ [IB_WR_FAST_REG_MR] = MLX5_OPCODE_UMR,
+ [IB_WR_MASKED_ATOMIC_CMP_AND_SWP] = MLX5_OPCODE_ATOMIC_MASKED_CS,
+ [IB_WR_MASKED_ATOMIC_FETCH_AND_ADD] = MLX5_OPCODE_ATOMIC_MASKED_FA,
+ [MLX5_IB_WR_UMR] = MLX5_OPCODE_UMR,
+};
+
+struct umr_wr {
+ u64 virt_addr;
+ struct ib_pd *pd;
+ unsigned int page_shift;
+ unsigned int npages;
+ u32 length;
+ int access_flags;
+ u32 mkey;
+};
+
+static int is_qp0(enum ib_qp_type qp_type)
+{
+ return qp_type == IB_QPT_SMI;
+}
+
+static int is_qp1(enum ib_qp_type qp_type)
+{
+ return qp_type == IB_QPT_GSI;
+}
+
+static int is_sqp(enum ib_qp_type qp_type)
+{
+ return is_qp0(qp_type) || is_qp1(qp_type);
+}
+
+static void *get_wqe(struct mlx5_ib_qp *qp, int offset)
+{
+ return mlx5_buf_offset(&qp->buf, offset);
+}
+
+static void *get_recv_wqe(struct mlx5_ib_qp *qp, int n)
+{
+ return get_wqe(qp, qp->rq.offset + (n << qp->rq.wqe_shift));
+}
+
+void *mlx5_get_send_wqe(struct mlx5_ib_qp *qp, int n)
+{
+ return get_wqe(qp, qp->sq.offset + (n << MLX5_IB_SQ_STRIDE));
+}
+
+static void mlx5_ib_qp_event(struct mlx5_core_qp *qp, int type)
+{
+ struct ib_qp *ibqp = &to_mibqp(qp)->ibqp;
+ struct ib_event event;
+
+ if (type == MLX5_EVENT_TYPE_PATH_MIG)
+ to_mibqp(qp)->port = to_mibqp(qp)->alt_port;
+
+ if (ibqp->event_handler) {
+ event.device = ibqp->device;
+ event.element.qp = ibqp;
+ switch (type) {
+ case MLX5_EVENT_TYPE_PATH_MIG:
+ event.event = IB_EVENT_PATH_MIG;
+ break;
+ case MLX5_EVENT_TYPE_COMM_EST:
+ event.event = IB_EVENT_COMM_EST;
+ break;
+ case MLX5_EVENT_TYPE_SQ_DRAINED:
+ event.event = IB_EVENT_SQ_DRAINED;
+ break;
+ case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
+ event.event = IB_EVENT_QP_LAST_WQE_REACHED;
+ break;
+ case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
+ event.event = IB_EVENT_QP_FATAL;
+ break;
+ case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
+ event.event = IB_EVENT_PATH_MIG_ERR;
+ break;
+ case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+ event.event = IB_EVENT_QP_REQ_ERR;
+ break;
+ case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
+ event.event = IB_EVENT_QP_ACCESS_ERR;
+ break;
+ default:
+ pr_warn("mlx5_ib: Unexpected event type %d on QP %06x\n", type, qp->qpn);
+ return;
+ }
+
+ ibqp->event_handler(&event, ibqp->qp_context);
+ }
+}
+
+static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap,
+ int has_rq, struct mlx5_ib_qp *qp, struct mlx5_ib_create_qp *ucmd)
+{
+ int wqe_size;
+ int wq_size;
+
+ /* Sanity check RQ size before proceeding */
+ if (cap->max_recv_wr > dev->mdev.caps.max_wqes)
+ return -EINVAL;
+
+ if (!has_rq) {
+ qp->rq.max_gs = 0;
+ qp->rq.wqe_cnt = 0;
+ qp->rq.wqe_shift = 0;
+ } else {
+ if (ucmd) {
+ qp->rq.wqe_cnt = ucmd->rq_wqe_count;
+ qp->rq.wqe_shift = ucmd->rq_wqe_shift;
+ qp->rq.max_gs = (1 << qp->rq.wqe_shift) / sizeof(struct mlx5_wqe_data_seg) - qp->wq_sig;
+ qp->rq.max_post = qp->rq.wqe_cnt;
+ } else {
+ wqe_size = qp->wq_sig ? sizeof(struct mlx5_wqe_signature_seg) : 0;
+ wqe_size += cap->max_recv_sge * sizeof(struct mlx5_wqe_data_seg);
+ wqe_size = roundup_pow_of_two(wqe_size);
+ wq_size = roundup_pow_of_two(cap->max_recv_wr) * wqe_size;
+ wq_size = max_t(int, wq_size, MLX5_SEND_WQE_BB);
+ qp->rq.wqe_cnt = wq_size / wqe_size;
+ if (wqe_size > dev->mdev.caps.max_rq_desc_sz) {
+ mlx5_ib_dbg(dev, "wqe_size %d, max %d\n",
+ wqe_size,
+ dev->mdev.caps.max_rq_desc_sz);
+ return -EINVAL;
+ }
+ qp->rq.wqe_shift = ilog2(wqe_size);
+ qp->rq.max_gs = (1 << qp->rq.wqe_shift) / sizeof(struct mlx5_wqe_data_seg) - qp->wq_sig;
+ qp->rq.max_post = qp->rq.wqe_cnt;
+ }
+ }
+
+ return 0;
+}
+
+static int sq_overhead(enum ib_qp_type qp_type)
+{
+ int size;
+
+ switch (qp_type) {
+ case IB_QPT_XRC_INI:
+ size = sizeof(struct mlx5_wqe_xrc_seg);
+ /* fall through */
+ case IB_QPT_RC:
+ size += sizeof(struct mlx5_wqe_ctrl_seg) +
+ sizeof(struct mlx5_wqe_atomic_seg) +
+ sizeof(struct mlx5_wqe_raddr_seg);
+ break;
+
+ case IB_QPT_UC:
+ size = sizeof(struct mlx5_wqe_ctrl_seg) +
+ sizeof(struct mlx5_wqe_raddr_seg);
+ break;
+
+ case IB_QPT_UD:
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ size = sizeof(struct mlx5_wqe_ctrl_seg) +
+ sizeof(struct mlx5_wqe_datagram_seg);
+ break;
+
+ case MLX5_IB_QPT_REG_UMR:
+ size = sizeof(struct mlx5_wqe_ctrl_seg) +
+ sizeof(struct mlx5_wqe_umr_ctrl_seg) +
+ sizeof(struct mlx5_mkey_seg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+static int calc_send_wqe(struct ib_qp_init_attr *attr)
+{
+ int inl_size = 0;
+ int size;
+
+ size = sq_overhead(attr->qp_type);
+ if (size < 0)
+ return size;
+
+ if (attr->cap.max_inline_data) {
+ inl_size = size + sizeof(struct mlx5_wqe_inline_seg) +
+ attr->cap.max_inline_data;
+ }
+
+ size += attr->cap.max_send_sge * sizeof(struct mlx5_wqe_data_seg);
+
+ return ALIGN(max_t(int, inl_size, size), MLX5_SEND_WQE_BB);
+}
+
+static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
+ struct mlx5_ib_qp *qp)
+{
+ int wqe_size;
+ int wq_size;
+
+ if (!attr->cap.max_send_wr)
+ return 0;
+
+ wqe_size = calc_send_wqe(attr);
+ mlx5_ib_dbg(dev, "wqe_size %d\n", wqe_size);
+ if (wqe_size < 0)
+ return wqe_size;
+
+ if (wqe_size > dev->mdev.caps.max_sq_desc_sz) {
+ mlx5_ib_dbg(dev, "\n");
+ return -EINVAL;
+ }
+
+ qp->max_inline_data = wqe_size - sq_overhead(attr->qp_type) -
+ sizeof(struct mlx5_wqe_inline_seg);
+ attr->cap.max_inline_data = qp->max_inline_data;
+
+ wq_size = roundup_pow_of_two(attr->cap.max_send_wr * wqe_size);
+ qp->sq.wqe_cnt = wq_size / MLX5_SEND_WQE_BB;
+ qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
+ qp->sq.max_gs = attr->cap.max_send_sge;
+ qp->sq.max_post = 1 << ilog2(wq_size / wqe_size);
+
+ return wq_size;
+}
+
+static int set_user_buf_size(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_qp *qp,
+ struct mlx5_ib_create_qp *ucmd)
+{
+ int desc_sz = 1 << qp->sq.wqe_shift;
+
+ if (desc_sz > dev->mdev.caps.max_sq_desc_sz) {
+ mlx5_ib_warn(dev, "desc_sz %d, max_sq_desc_sz %d\n",
+ desc_sz, dev->mdev.caps.max_sq_desc_sz);
+ return -EINVAL;
+ }
+
+ if (ucmd->sq_wqe_count && ((1 << ilog2(ucmd->sq_wqe_count)) != ucmd->sq_wqe_count)) {
+ mlx5_ib_warn(dev, "sq_wqe_count %d, sq_wqe_count %d\n",
+ ucmd->sq_wqe_count, ucmd->sq_wqe_count);
+ return -EINVAL;
+ }
+
+ qp->sq.wqe_cnt = ucmd->sq_wqe_count;
+
+ if (qp->sq.wqe_cnt > dev->mdev.caps.max_wqes) {
+ mlx5_ib_warn(dev, "wqe_cnt %d, max_wqes %d\n",
+ qp->sq.wqe_cnt, dev->mdev.caps.max_wqes);
+ return -EINVAL;
+ }
+
+ qp->buf_size = (qp->rq.wqe_cnt << qp->rq.wqe_shift) +
+ (qp->sq.wqe_cnt << 6);
+
+ return 0;
+}
+
+static int qp_has_rq(struct ib_qp_init_attr *attr)
+{
+ if (attr->qp_type == IB_QPT_XRC_INI ||
+ attr->qp_type == IB_QPT_XRC_TGT || attr->srq ||
+ attr->qp_type == MLX5_IB_QPT_REG_UMR ||
+ !attr->cap.max_recv_wr)
+ return 0;
+
+ return 1;
+}
+
+static int alloc_high_class_uuar(struct mlx5_uuar_info *uuari)
+{
+ int nuuars = uuari->num_uars * MLX5_BF_REGS_PER_PAGE;
+ int start_uuar;
+ int i;
+
+ start_uuar = nuuars - uuari->num_low_latency_uuars;
+ for (i = start_uuar; i < nuuars; i++) {
+ if (!test_bit(i, uuari->bitmap)) {
+ set_bit(i, uuari->bitmap);
+ uuari->count[i]++;
+ return i;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+static int alloc_med_class_uuar(struct mlx5_uuar_info *uuari)
+{
+ int nuuars = uuari->num_uars * MLX5_BF_REGS_PER_PAGE;
+ int minidx = 1;
+ int uuarn;
+ int end;
+ int i;
+
+ end = nuuars - uuari->num_low_latency_uuars;
+
+ for (i = 1; i < end; i++) {
+ uuarn = i & 3;
+ if (uuarn == 2 || uuarn == 3)
+ continue;
+
+ if (uuari->count[i] < uuari->count[minidx])
+ minidx = i;
+ }
+
+ uuari->count[minidx]++;
+ return minidx;
+}
+
+static int alloc_uuar(struct mlx5_uuar_info *uuari,
+ enum mlx5_ib_latency_class lat)
+{
+ int uuarn = -EINVAL;
+
+ mutex_lock(&uuari->lock);
+ switch (lat) {
+ case MLX5_IB_LATENCY_CLASS_LOW:
+ uuarn = 0;
+ uuari->count[uuarn]++;
+ break;
+
+ case MLX5_IB_LATENCY_CLASS_MEDIUM:
+ uuarn = alloc_med_class_uuar(uuari);
+ break;
+
+ case MLX5_IB_LATENCY_CLASS_HIGH:
+ uuarn = alloc_high_class_uuar(uuari);
+ break;
+
+ case MLX5_IB_LATENCY_CLASS_FAST_PATH:
+ uuarn = 2;
+ break;
+ }
+ mutex_unlock(&uuari->lock);
+
+ return uuarn;
+}
+
+static void free_med_class_uuar(struct mlx5_uuar_info *uuari, int uuarn)
+{
+ clear_bit(uuarn, uuari->bitmap);
+ --uuari->count[uuarn];
+}
+
+static void free_high_class_uuar(struct mlx5_uuar_info *uuari, int uuarn)
+{
+ clear_bit(uuarn, uuari->bitmap);
+ --uuari->count[uuarn];
+}
+
+static void free_uuar(struct mlx5_uuar_info *uuari, int uuarn)
+{
+ int nuuars = uuari->num_uars * MLX5_BF_REGS_PER_PAGE;
+ int high_uuar = nuuars - uuari->num_low_latency_uuars;
+
+ mutex_lock(&uuari->lock);
+ if (uuarn == 0) {
+ --uuari->count[uuarn];
+ goto out;
+ }
+
+ if (uuarn < high_uuar) {
+ free_med_class_uuar(uuari, uuarn);
+ goto out;
+ }
+
+ free_high_class_uuar(uuari, uuarn);
+
+out:
+ mutex_unlock(&uuari->lock);
+}
+
+static enum mlx5_qp_state to_mlx5_state(enum ib_qp_state state)
+{
+ switch (state) {
+ case IB_QPS_RESET: return MLX5_QP_STATE_RST;
+ case IB_QPS_INIT: return MLX5_QP_STATE_INIT;
+ case IB_QPS_RTR: return MLX5_QP_STATE_RTR;
+ case IB_QPS_RTS: return MLX5_QP_STATE_RTS;
+ case IB_QPS_SQD: return MLX5_QP_STATE_SQD;
+ case IB_QPS_SQE: return MLX5_QP_STATE_SQER;
+ case IB_QPS_ERR: return MLX5_QP_STATE_ERR;
+ default: return -1;
+ }
+}
+
+static int to_mlx5_st(enum ib_qp_type type)
+{
+ switch (type) {
+ case IB_QPT_RC: return MLX5_QP_ST_RC;
+ case IB_QPT_UC: return MLX5_QP_ST_UC;
+ case IB_QPT_UD: return MLX5_QP_ST_UD;
+ case MLX5_IB_QPT_REG_UMR: return MLX5_QP_ST_REG_UMR;
+ case IB_QPT_XRC_INI:
+ case IB_QPT_XRC_TGT: return MLX5_QP_ST_XRC;
+ case IB_QPT_SMI: return MLX5_QP_ST_QP0;
+ case IB_QPT_GSI: return MLX5_QP_ST_QP1;
+ case IB_QPT_RAW_IPV6: return MLX5_QP_ST_RAW_IPV6;
+ case IB_QPT_RAW_ETHERTYPE: return MLX5_QP_ST_RAW_ETHERTYPE;
+ case IB_QPT_RAW_PACKET:
+ case IB_QPT_MAX:
+ default: return -EINVAL;
+ }
+}
+
+static int uuarn_to_uar_index(struct mlx5_uuar_info *uuari, int uuarn)
+{
+ return uuari->uars[uuarn / MLX5_BF_REGS_PER_PAGE].index;
+}
+
+static int create_user_qp(struct mlx5_ib_dev *dev, struct ib_pd *pd,
+ struct mlx5_ib_qp *qp, struct ib_udata *udata,
+ struct mlx5_create_qp_mbox_in **in,
+ struct mlx5_ib_create_qp_resp *resp, int *inlen)
+{
+ struct mlx5_ib_ucontext *context;
+ struct mlx5_ib_create_qp ucmd;
+ int page_shift;
+ int uar_index;
+ int npages;
+ u32 offset;
+ int uuarn;
+ int ncont;
+ int err;
+
+ err = ib_copy_from_udata(&ucmd, udata, sizeof(ucmd));
+ if (err) {
+ mlx5_ib_dbg(dev, "copy failed\n");
+ return err;
+ }
+
+ context = to_mucontext(pd->uobject->context);
+ /*
+ * TBD: should come from the verbs when we have the API
+ */
+ uuarn = alloc_uuar(&context->uuari, MLX5_IB_LATENCY_CLASS_HIGH);
+ if (uuarn < 0) {
+ mlx5_ib_dbg(dev, "failed to allocate low latency UUAR\n");
+ mlx5_ib_dbg(dev, "reverting to high latency\n");
+ uuarn = alloc_uuar(&context->uuari, MLX5_IB_LATENCY_CLASS_LOW);
+ if (uuarn < 0) {
+ mlx5_ib_dbg(dev, "uuar allocation failed\n");
+ return uuarn;
+ }
+ }
+
+ uar_index = uuarn_to_uar_index(&context->uuari, uuarn);
+ mlx5_ib_dbg(dev, "uuarn 0x%x, uar_index 0x%x\n", uuarn, uar_index);
+
+ err = set_user_buf_size(dev, qp, &ucmd);
+ if (err)
+ goto err_uuar;
+
+ qp->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr,
+ qp->buf_size, 0, 0);
+ if (IS_ERR(qp->umem)) {
+ mlx5_ib_dbg(dev, "umem_get failed\n");
+ err = PTR_ERR(qp->umem);
+ goto err_uuar;
+ }
+
+ mlx5_ib_cont_pages(qp->umem, ucmd.buf_addr, &npages, &page_shift,
+ &ncont, NULL);
+ err = mlx5_ib_get_buf_offset(ucmd.buf_addr, page_shift, &offset);
+ if (err) {
+ mlx5_ib_warn(dev, "bad offset\n");
+ goto err_umem;
+ }
+ mlx5_ib_dbg(dev, "addr 0x%llx, size %d, npages %d, page_shift %d, ncont %d, offset %d\n",
+ ucmd.buf_addr, qp->buf_size, npages, page_shift, ncont, offset);
+
+ *inlen = sizeof(**in) + sizeof(*(*in)->pas) * ncont;
+ *in = mlx5_vzalloc(*inlen);
+ if (!*in) {
+ err = -ENOMEM;
+ goto err_umem;
+ }
+ mlx5_ib_populate_pas(dev, qp->umem, page_shift, (*in)->pas, 0);
+ (*in)->ctx.log_pg_sz_remote_qpn =
+ cpu_to_be32((page_shift - PAGE_SHIFT) << 24);
+ (*in)->ctx.params2 = cpu_to_be32(offset << 6);
+
+ (*in)->ctx.qp_counter_set_usr_page = cpu_to_be32(uar_index);
+ resp->uuar_index = uuarn;
+ qp->uuarn = uuarn;
+
+ err = mlx5_ib_db_map_user(context, ucmd.db_addr, &qp->db);
+ if (err) {
+ mlx5_ib_dbg(dev, "map failed\n");
+ goto err_free;
+ }
+
+ err = ib_copy_to_udata(udata, resp, sizeof(*resp));
+ if (err) {
+ mlx5_ib_dbg(dev, "copy failed\n");
+ goto err_unmap;
+ }
+ qp->create_type = MLX5_QP_USER;
+
+ return 0;
+
+err_unmap:
+ mlx5_ib_db_unmap_user(context, &qp->db);
+
+err_free:
+ mlx5_vfree(*in);
+
+err_umem:
+ ib_umem_release(qp->umem);
+
+err_uuar:
+ free_uuar(&context->uuari, uuarn);
+ return err;
+}
+
+static void destroy_qp_user(struct ib_pd *pd, struct mlx5_ib_qp *qp)
+{
+ struct mlx5_ib_ucontext *context;
+
+ context = to_mucontext(pd->uobject->context);
+ mlx5_ib_db_unmap_user(context, &qp->db);
+ ib_umem_release(qp->umem);
+ free_uuar(&context->uuari, qp->uuarn);
+}
+
+static int create_kernel_qp(struct mlx5_ib_dev *dev,
+ struct ib_qp_init_attr *init_attr,
+ struct mlx5_ib_qp *qp,
+ struct mlx5_create_qp_mbox_in **in, int *inlen)
+{
+ enum mlx5_ib_latency_class lc = MLX5_IB_LATENCY_CLASS_LOW;
+ struct mlx5_uuar_info *uuari;
+ int uar_index;
+ int uuarn;
+ int err;
+
+ uuari = &dev->mdev.priv.uuari;
+ if (init_attr->create_flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK)
+ qp->flags |= MLX5_IB_QP_BLOCK_MULTICAST_LOOPBACK;
+
+ if (init_attr->qp_type == MLX5_IB_QPT_REG_UMR)
+ lc = MLX5_IB_LATENCY_CLASS_FAST_PATH;
+
+ uuarn = alloc_uuar(uuari, lc);
+ if (uuarn < 0) {
+ mlx5_ib_dbg(dev, "\n");
+ return -ENOMEM;
+ }
+
+ qp->bf = &uuari->bfs[uuarn];
+ uar_index = qp->bf->uar->index;
+
+ err = calc_sq_size(dev, init_attr, qp);
+ if (err < 0) {
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ goto err_uuar;
+ }
+
+ qp->rq.offset = 0;
+ qp->sq.offset = qp->rq.wqe_cnt << qp->rq.wqe_shift;
+ qp->buf_size = err + (qp->rq.wqe_cnt << qp->rq.wqe_shift);
+
+ err = mlx5_buf_alloc(&dev->mdev, qp->buf_size, PAGE_SIZE * 2, &qp->buf);
+ if (err) {
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ goto err_uuar;
+ }
+
+ qp->sq.qend = mlx5_get_send_wqe(qp, qp->sq.wqe_cnt);
+ *inlen = sizeof(**in) + sizeof(*(*in)->pas) * qp->buf.npages;
+ *in = mlx5_vzalloc(*inlen);
+ if (!*in) {
+ err = -ENOMEM;
+ goto err_buf;
+ }
+ (*in)->ctx.qp_counter_set_usr_page = cpu_to_be32(uar_index);
+ (*in)->ctx.log_pg_sz_remote_qpn = cpu_to_be32((qp->buf.page_shift - PAGE_SHIFT) << 24);
+ /* Set "fast registration enabled" for all kernel QPs */
+ (*in)->ctx.params1 |= cpu_to_be32(1 << 11);
+ (*in)->ctx.sq_crq_size |= cpu_to_be16(1 << 4);
+
+ mlx5_fill_page_array(&qp->buf, (*in)->pas);
+
+ err = mlx5_db_alloc(&dev->mdev, &qp->db);
+ if (err) {
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ goto err_free;
+ }
+
+ qp->db.db[0] = 0;
+ qp->db.db[1] = 0;
+
+ qp->sq.wrid = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wrid), GFP_KERNEL);
+ qp->sq.wr_data = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wr_data), GFP_KERNEL);
+ qp->rq.wrid = kmalloc(qp->rq.wqe_cnt * sizeof(*qp->rq.wrid), GFP_KERNEL);
+ qp->sq.w_list = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.w_list), GFP_KERNEL);
+ qp->sq.wqe_head = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wqe_head), GFP_KERNEL);
+
+ if (!qp->sq.wrid || !qp->sq.wr_data || !qp->rq.wrid ||
+ !qp->sq.w_list || !qp->sq.wqe_head) {
+ err = -ENOMEM;
+ goto err_wrid;
+ }
+ qp->create_type = MLX5_QP_KERNEL;
+
+ return 0;
+
+err_wrid:
+ mlx5_db_free(&dev->mdev, &qp->db);
+ kfree(qp->sq.wqe_head);
+ kfree(qp->sq.w_list);
+ kfree(qp->sq.wrid);
+ kfree(qp->sq.wr_data);
+ kfree(qp->rq.wrid);
+
+err_free:
+ mlx5_vfree(*in);
+
+err_buf:
+ mlx5_buf_free(&dev->mdev, &qp->buf);
+
+err_uuar:
+ free_uuar(&dev->mdev.priv.uuari, uuarn);
+ return err;
+}
+
+static void destroy_qp_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp)
+{
+ mlx5_db_free(&dev->mdev, &qp->db);
+ kfree(qp->sq.wqe_head);
+ kfree(qp->sq.w_list);
+ kfree(qp->sq.wrid);
+ kfree(qp->sq.wr_data);
+ kfree(qp->rq.wrid);
+ mlx5_buf_free(&dev->mdev, &qp->buf);
+ free_uuar(&dev->mdev.priv.uuari, qp->bf->uuarn);
+}
+
+static __be32 get_rx_type(struct mlx5_ib_qp *qp, struct ib_qp_init_attr *attr)
+{
+ if (attr->srq || (attr->qp_type == IB_QPT_XRC_TGT) ||
+ (attr->qp_type == IB_QPT_XRC_INI))
+ return cpu_to_be32(MLX5_SRQ_RQ);
+ else if (!qp->has_rq)
+ return cpu_to_be32(MLX5_ZERO_LEN_RQ);
+ else
+ return cpu_to_be32(MLX5_NON_ZERO_RQ);
+}
+
+static int is_connected(enum ib_qp_type qp_type)
+{
+ if (qp_type == IB_QPT_RC || qp_type == IB_QPT_UC)
+ return 1;
+
+ return 0;
+}
+
+static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
+ struct ib_qp_init_attr *init_attr,
+ struct ib_udata *udata, struct mlx5_ib_qp *qp)
+{
+ struct mlx5_ib_resources *devr = &dev->devr;
+ struct mlx5_ib_create_qp_resp resp;
+ struct mlx5_create_qp_mbox_in *in;
+ struct mlx5_ib_create_qp ucmd;
+ int inlen = sizeof(*in);
+ int err;
+
+ mutex_init(&qp->mutex);
+ spin_lock_init(&qp->sq.lock);
+ spin_lock_init(&qp->rq.lock);
+
+ if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
+ qp->sq_signal_bits = MLX5_WQE_CTRL_CQ_UPDATE;
+
+ if (pd && pd->uobject) {
+ if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) {
+ mlx5_ib_dbg(dev, "copy failed\n");
+ return -EFAULT;
+ }
+
+ qp->wq_sig = !!(ucmd.flags & MLX5_QP_FLAG_SIGNATURE);
+ qp->scat_cqe = !!(ucmd.flags & MLX5_QP_FLAG_SCATTER_CQE);
+ } else {
+ qp->wq_sig = !!wq_signature;
+ }
+
+ qp->has_rq = qp_has_rq(init_attr);
+ err = set_rq_size(dev, &init_attr->cap, qp->has_rq,
+ qp, (pd && pd->uobject) ? &ucmd : NULL);
+ if (err) {
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ return err;
+ }
+
+ if (pd) {
+ if (pd->uobject) {
+ mlx5_ib_dbg(dev, "requested sq_wqe_count (%d)\n", ucmd.sq_wqe_count);
+ if (ucmd.rq_wqe_shift != qp->rq.wqe_shift ||
+ ucmd.rq_wqe_count != qp->rq.wqe_cnt) {
+ mlx5_ib_dbg(dev, "invalid rq params\n");
+ return -EINVAL;
+ }
+ if (ucmd.sq_wqe_count > dev->mdev.caps.max_wqes) {
+ mlx5_ib_dbg(dev, "requested sq_wqe_count (%d) > max allowed (%d)\n",
+ ucmd.sq_wqe_count, dev->mdev.caps.max_wqes);
+ return -EINVAL;
+ }
+ err = create_user_qp(dev, pd, qp, udata, &in, &resp, &inlen);
+ if (err)
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ } else {
+ err = create_kernel_qp(dev, init_attr, qp, &in, &inlen);
+ if (err)
+ mlx5_ib_dbg(dev, "err %d\n", err);
+ else
+ qp->pa_lkey = to_mpd(pd)->pa_lkey;
+ }
+
+ if (err)
+ return err;
+ } else {
+ in = mlx5_vzalloc(sizeof(*in));
+ if (!in)
+ return -ENOMEM;
+
+ qp->create_type = MLX5_QP_EMPTY;
+ }
+
+ if (is_sqp(init_attr->qp_type))
+ qp->port = init_attr->port_num;
+
+ in->ctx.flags = cpu_to_be32(to_mlx5_st(init_attr->qp_type) << 16 |
+ MLX5_QP_PM_MIGRATED << 11);
+
+ if (init_attr->qp_type != MLX5_IB_QPT_REG_UMR)
+ in->ctx.flags_pd = cpu_to_be32(to_mpd(pd ? pd : devr->p0)->pdn);
+ else
+ in->ctx.flags_pd = cpu_to_be32(MLX5_QP_LAT_SENSITIVE);
+
+ if (qp->wq_sig)
+ in->ctx.flags_pd |= cpu_to_be32(MLX5_QP_ENABLE_SIG);
+
+ if (qp->scat_cqe && is_connected(init_attr->qp_type)) {
+ int rcqe_sz;
+ int scqe_sz;
+
+ rcqe_sz = mlx5_ib_get_cqe_size(dev, init_attr->recv_cq);
+ scqe_sz = mlx5_ib_get_cqe_size(dev, init_attr->send_cq);
+
+ if (rcqe_sz == 128)
+ in->ctx.cs_res = MLX5_RES_SCAT_DATA64_CQE;
+ else
+ in->ctx.cs_res = MLX5_RES_SCAT_DATA32_CQE;
+
+ if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) {
+ if (scqe_sz == 128)
+ in->ctx.cs_req = MLX5_REQ_SCAT_DATA64_CQE;
+ else
+ in->ctx.cs_req = MLX5_REQ_SCAT_DATA32_CQE;
+ }
+ }
+
+ if (qp->rq.wqe_cnt) {
+ in->ctx.rq_size_stride = (qp->rq.wqe_shift - 4);
+ in->ctx.rq_size_stride |= ilog2(qp->rq.wqe_cnt) << 3;
+ }
+
+ in->ctx.rq_type_srqn = get_rx_type(qp, init_attr);
+
+ if (qp->sq.wqe_cnt)
+ in->ctx.sq_crq_size |= cpu_to_be16(ilog2(qp->sq.wqe_cnt) << 11);
+ else
+ in->ctx.sq_crq_size |= cpu_to_be16(0x8000);
+
+ /* Set default resources */
+ switch (init_attr->qp_type) {
+ case IB_QPT_XRC_TGT:
+ in->ctx.cqn_recv = cpu_to_be32(to_mcq(devr->c0)->mcq.cqn);
+ in->ctx.cqn_send = cpu_to_be32(to_mcq(devr->c0)->mcq.cqn);
+ in->ctx.rq_type_srqn |= cpu_to_be32(to_msrq(devr->s0)->msrq.srqn);
+ in->ctx.xrcd = cpu_to_be32(to_mxrcd(init_attr->xrcd)->xrcdn);
+ break;
+ case IB_QPT_XRC_INI:
+ in->ctx.cqn_recv = cpu_to_be32(to_mcq(devr->c0)->mcq.cqn);
+ in->ctx.xrcd = cpu_to_be32(to_mxrcd(devr->x1)->xrcdn);
+ in->ctx.rq_type_srqn |= cpu_to_be32(to_msrq(devr->s0)->msrq.srqn);
+ break;
+ default:
+ if (init_attr->srq) {
+ in->ctx.xrcd = cpu_to_be32(to_mxrcd(devr->x0)->xrcdn);
+ in->ctx.rq_type_srqn |= cpu_to_be32(to_msrq(init_attr->srq)->msrq.srqn);
+ } else {
+ in->ctx.xrcd = cpu_to_be32(to_mxrcd(devr->x1)->xrcdn);
+ in->ctx.rq_type_srqn |= cpu_to_be32(to_msrq(devr->s0)->msrq.srqn);
+ }
+ }
+
+ if (init_attr->send_cq)
+ in->ctx.cqn_send = cpu_to_be32(to_mcq(init_attr->send_cq)->mcq.cqn);
+
+ if (init_attr->recv_cq)
+ in->ctx.cqn_recv = cpu_to_be32(to_mcq(init_attr->recv_cq)->mcq.cqn);
+
+ in->ctx.db_rec_addr = cpu_to_be64(qp->db.dma);
+
+ err = mlx5_core_create_qp(&dev->mdev, &qp->mqp, in, inlen);
+ if (err) {
+ mlx5_ib_dbg(dev, "create qp failed\n");
+ goto err_create;
+ }
+
+ mlx5_vfree(in);
+ /* Hardware wants QPN written in big-endian order (after
+ * shifting) for send doorbell. Precompute this value to save
+ * a little bit when posting sends.
+ */
+ qp->doorbell_qpn = swab32(qp->mqp.qpn << 8);
+
+ qp->mqp.event = mlx5_ib_qp_event;
+
+ return 0;
+
+err_create:
+ if (qp->create_type == MLX5_QP_USER)
+ destroy_qp_user(pd, qp);
+ else if (qp->create_type == MLX5_QP_KERNEL)
+ destroy_qp_kernel(dev, qp);
+
+ mlx5_vfree(in);
+ return err;
+}
+
+static void mlx5_ib_lock_cqs(struct mlx5_ib_cq *send_cq, struct mlx5_ib_cq *recv_cq)
+ __acquires(&send_cq->lock) __acquires(&recv_cq->lock)
+{
+ if (send_cq) {
+ if (recv_cq) {
+ if (send_cq->mcq.cqn < recv_cq->mcq.cqn) {
+ spin_lock_irq(&send_cq->lock);
+ spin_lock_nested(&recv_cq->lock,
+ SINGLE_DEPTH_NESTING);
+ } else if (send_cq->mcq.cqn == recv_cq->mcq.cqn) {
+ spin_lock_irq(&send_cq->lock);
+ __acquire(&recv_cq->lock);
+ } else {
+ spin_lock_irq(&recv_cq->lock);
+ spin_lock_nested(&send_cq->lock,
+ SINGLE_DEPTH_NESTING);
+ }
+ } else {
+ spin_lock_irq(&send_cq->lock);
+ }
+ } else if (recv_cq) {
+ spin_lock_irq(&recv_cq->lock);
+ }
+}
+
+static void mlx5_ib_unlock_cqs(struct mlx5_ib_cq *send_cq, struct mlx5_ib_cq *recv_cq)
+ __releases(&send_cq->lock) __releases(&recv_cq->lock)
+{
+ if (send_cq) {
+ if (recv_cq) {
+ if (send_cq->mcq.cqn < recv_cq->mcq.cqn) {
+ spin_unlock(&recv_cq->lock);
+ spin_unlock_irq(&send_cq->lock);
+ } else if (send_cq->mcq.cqn == recv_cq->mcq.cqn) {
+ __release(&recv_cq->lock);
+ spin_unlock_irq(&send_cq->lock);
+ } else {
+ spin_unlock(&send_cq->lock);
+ spin_unlock_irq(&recv_cq->lock);
+ }
+ } else {
+ spin_unlock_irq(&send_cq->lock);
+ }
+ } else if (recv_cq) {
+ spin_unlock_irq(&recv_cq->lock);
+ }
+}
+
+static struct mlx5_ib_pd *get_pd(struct mlx5_ib_qp *qp)
+{
+ return to_mpd(qp->ibqp.pd);
+}
+
+static void get_cqs(struct mlx5_ib_qp *qp,
+ struct mlx5_ib_cq **send_cq, struct mlx5_ib_cq **recv_cq)
+{
+ switch (qp->ibqp.qp_type) {
+ case IB_QPT_XRC_TGT:
+ *send_cq = NULL;
+ *recv_cq = NULL;
+ break;
+ case MLX5_IB_QPT_REG_UMR:
+ case IB_QPT_XRC_INI:
+ *send_cq = to_mcq(qp->ibqp.send_cq);
+ *recv_cq = NULL;
+ break;
+
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ case IB_QPT_RC:
+ case IB_QPT_UC:
+ case IB_QPT_UD:
+ case IB_QPT_RAW_IPV6:
+ case IB_QPT_RAW_ETHERTYPE:
+ *send_cq = to_mcq(qp->ibqp.send_cq);
+ *recv_cq = to_mcq(qp->ibqp.recv_cq);
+ break;
+
+ case IB_QPT_RAW_PACKET:
+ case IB_QPT_MAX:
+ default:
+ *send_cq = NULL;
+ *recv_cq = NULL;
+ break;
+ }
+}
+
+static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp)
+{
+ struct mlx5_ib_cq *send_cq, *recv_cq;
+ struct mlx5_modify_qp_mbox_in *in;
+ int err;
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ return;
+ if (qp->state != IB_QPS_RESET)
+ if (mlx5_core_qp_modify(&dev->mdev, to_mlx5_state(qp->state),
+ MLX5_QP_STATE_RST, in, sizeof(*in), &qp->mqp))
+ mlx5_ib_warn(dev, "mlx5_ib: modify QP %06x to RESET failed\n",
+ qp->mqp.qpn);
+
+ get_cqs(qp, &send_cq, &recv_cq);
+
+ if (qp->create_type == MLX5_QP_KERNEL) {
+ mlx5_ib_lock_cqs(send_cq, recv_cq);
+ __mlx5_ib_cq_clean(recv_cq, qp->mqp.qpn,
+ qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL);
+ if (send_cq != recv_cq)
+ __mlx5_ib_cq_clean(send_cq, qp->mqp.qpn, NULL);
+ mlx5_ib_unlock_cqs(send_cq, recv_cq);
+ }
+
+ err = mlx5_core_destroy_qp(&dev->mdev, &qp->mqp);
+ if (err)
+ mlx5_ib_warn(dev, "failed to destroy QP 0x%x\n", qp->mqp.qpn);
+ kfree(in);
+
+
+ if (qp->create_type == MLX5_QP_KERNEL)
+ destroy_qp_kernel(dev, qp);
+ else if (qp->create_type == MLX5_QP_USER)
+ destroy_qp_user(&get_pd(qp)->ibpd, qp);
+}
+
+static const char *ib_qp_type_str(enum ib_qp_type type)
+{
+ switch (type) {
+ case IB_QPT_SMI:
+ return "IB_QPT_SMI";
+ case IB_QPT_GSI:
+ return "IB_QPT_GSI";
+ case IB_QPT_RC:
+ return "IB_QPT_RC";
+ case IB_QPT_UC:
+ return "IB_QPT_UC";
+ case IB_QPT_UD:
+ return "IB_QPT_UD";
+ case IB_QPT_RAW_IPV6:
+ return "IB_QPT_RAW_IPV6";
+ case IB_QPT_RAW_ETHERTYPE:
+ return "IB_QPT_RAW_ETHERTYPE";
+ case IB_QPT_XRC_INI:
+ return "IB_QPT_XRC_INI";
+ case IB_QPT_XRC_TGT:
+ return "IB_QPT_XRC_TGT";
+ case IB_QPT_RAW_PACKET:
+ return "IB_QPT_RAW_PACKET";
+ case MLX5_IB_QPT_REG_UMR:
+ return "MLX5_IB_QPT_REG_UMR";
+ case IB_QPT_MAX:
+ default:
+ return "Invalid QP type";
+ }
+}
+
+struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd,
+ struct ib_qp_init_attr *init_attr,
+ struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev;
+ struct mlx5_ib_qp *qp;
+ u16 xrcdn = 0;
+ int err;
+
+ if (pd) {
+ dev = to_mdev(pd->device);
+ } else {
+ /* being cautious here */
+ if (init_attr->qp_type != IB_QPT_XRC_TGT &&
+ init_attr->qp_type != MLX5_IB_QPT_REG_UMR) {
+ pr_warn("%s: no PD for transport %s\n", __func__,
+ ib_qp_type_str(init_attr->qp_type));
+ return ERR_PTR(-EINVAL);
+ }
+ dev = to_mdev(to_mxrcd(init_attr->xrcd)->ibxrcd.device);
+ }
+
+ switch (init_attr->qp_type) {
+ case IB_QPT_XRC_TGT:
+ case IB_QPT_XRC_INI:
+ if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_XRC)) {
+ mlx5_ib_dbg(dev, "XRC not supported\n");
+ return ERR_PTR(-ENOSYS);
+ }
+ init_attr->recv_cq = NULL;
+ if (init_attr->qp_type == IB_QPT_XRC_TGT) {
+ xrcdn = to_mxrcd(init_attr->xrcd)->xrcdn;
+ init_attr->send_cq = NULL;
+ }
+
+ /* fall through */
+ case IB_QPT_RC:
+ case IB_QPT_UC:
+ case IB_QPT_UD:
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ case MLX5_IB_QPT_REG_UMR:
+ qp = kzalloc(sizeof(*qp), GFP_KERNEL);
+ if (!qp)
+ return ERR_PTR(-ENOMEM);
+
+ err = create_qp_common(dev, pd, init_attr, udata, qp);
+ if (err) {
+ mlx5_ib_dbg(dev, "create_qp_common failed\n");
+ kfree(qp);
+ return ERR_PTR(err);
+ }
+
+ if (is_qp0(init_attr->qp_type))
+ qp->ibqp.qp_num = 0;
+ else if (is_qp1(init_attr->qp_type))
+ qp->ibqp.qp_num = 1;
+ else
+ qp->ibqp.qp_num = qp->mqp.qpn;
+
+ mlx5_ib_dbg(dev, "ib qpnum 0x%x, mlx qpn 0x%x, rcqn 0x%x, scqn 0x%x\n",
+ qp->ibqp.qp_num, qp->mqp.qpn, to_mcq(init_attr->recv_cq)->mcq.cqn,
+ to_mcq(init_attr->send_cq)->mcq.cqn);
+
+ qp->xrcdn = xrcdn;
+
+ break;
+
+ case IB_QPT_RAW_IPV6:
+ case IB_QPT_RAW_ETHERTYPE:
+ case IB_QPT_RAW_PACKET:
+ case IB_QPT_MAX:
+ default:
+ mlx5_ib_dbg(dev, "unsupported qp type %d\n",
+ init_attr->qp_type);
+ /* Don't support raw QPs */
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &qp->ibqp;
+}
+
+int mlx5_ib_destroy_qp(struct ib_qp *qp)
+{
+ struct mlx5_ib_dev *dev = to_mdev(qp->device);
+ struct mlx5_ib_qp *mqp = to_mqp(qp);
+
+ destroy_qp_common(dev, mqp);
+
+ kfree(mqp);
+
+ return 0;
+}
+
+static __be32 to_mlx5_access_flags(struct mlx5_ib_qp *qp, const struct ib_qp_attr *attr,
+ int attr_mask)
+{
+ u32 hw_access_flags = 0;
+ u8 dest_rd_atomic;
+ u32 access_flags;
+
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
+ dest_rd_atomic = attr->max_dest_rd_atomic;
+ else
+ dest_rd_atomic = qp->resp_depth;
+
+ if (attr_mask & IB_QP_ACCESS_FLAGS)
+ access_flags = attr->qp_access_flags;
+ else
+ access_flags = qp->atomic_rd_en;
+
+ if (!dest_rd_atomic)
+ access_flags &= IB_ACCESS_REMOTE_WRITE;
+
+ if (access_flags & IB_ACCESS_REMOTE_READ)
+ hw_access_flags |= MLX5_QP_BIT_RRE;
+ if (access_flags & IB_ACCESS_REMOTE_ATOMIC)
+ hw_access_flags |= (MLX5_QP_BIT_RAE | MLX5_ATOMIC_MODE_CX);
+ if (access_flags & IB_ACCESS_REMOTE_WRITE)
+ hw_access_flags |= MLX5_QP_BIT_RWE;
+
+ return cpu_to_be32(hw_access_flags);
+}
+
+enum {
+ MLX5_PATH_FLAG_FL = 1 << 0,
+ MLX5_PATH_FLAG_FREE_AR = 1 << 1,
+ MLX5_PATH_FLAG_COUNTER = 1 << 2,
+};
+
+static int ib_rate_to_mlx5(struct mlx5_ib_dev *dev, u8 rate)
+{
+ if (rate == IB_RATE_PORT_CURRENT) {
+ return 0;
+ } else if (rate < IB_RATE_2_5_GBPS || rate > IB_RATE_300_GBPS) {
+ return -EINVAL;
+ } else {
+ while (rate != IB_RATE_2_5_GBPS &&
+ !(1 << (rate + MLX5_STAT_RATE_OFFSET) &
+ dev->mdev.caps.stat_rate_support))
+ --rate;
+ }
+
+ return rate + MLX5_STAT_RATE_OFFSET;
+}
+
+static int mlx5_set_path(struct mlx5_ib_dev *dev, const struct ib_ah_attr *ah,
+ struct mlx5_qp_path *path, u8 port, int attr_mask,
+ u32 path_flags, const struct ib_qp_attr *attr)
+{
+ int err;
+
+ path->fl = (path_flags & MLX5_PATH_FLAG_FL) ? 0x80 : 0;
+ path->free_ar = (path_flags & MLX5_PATH_FLAG_FREE_AR) ? 0x80 : 0;
+
+ if (attr_mask & IB_QP_PKEY_INDEX)
+ path->pkey_index = attr->pkey_index;
+
+ path->grh_mlid = ah->src_path_bits & 0x7f;
+ path->rlid = cpu_to_be16(ah->dlid);
+
+ if (ah->ah_flags & IB_AH_GRH) {
+ path->grh_mlid |= 1 << 7;
+ path->mgid_index = ah->grh.sgid_index;
+ path->hop_limit = ah->grh.hop_limit;
+ path->tclass_flowlabel =
+ cpu_to_be32((ah->grh.traffic_class << 20) |
+ (ah->grh.flow_label));
+ memcpy(path->rgid, ah->grh.dgid.raw, 16);
+ }
+
+ err = ib_rate_to_mlx5(dev, ah->static_rate);
+ if (err < 0)
+ return err;
+ path->static_rate = err;
+ path->port = port;
+
+ if (ah->ah_flags & IB_AH_GRH) {
+ if (ah->grh.sgid_index >= dev->mdev.caps.port[port - 1].gid_table_len) {
+ pr_err(KERN_ERR "sgid_index (%u) too large. max is %d\n",
+ ah->grh.sgid_index, dev->mdev.caps.port[port - 1].gid_table_len);
+ return -EINVAL;
+ }
+
+ path->grh_mlid |= 1 << 7;
+ path->mgid_index = ah->grh.sgid_index;
+ path->hop_limit = ah->grh.hop_limit;
+ path->tclass_flowlabel =
+ cpu_to_be32((ah->grh.traffic_class << 20) |
+ (ah->grh.flow_label));
+ memcpy(path->rgid, ah->grh.dgid.raw, 16);
+ }
+
+ if (attr_mask & IB_QP_TIMEOUT)
+ path->ackto_lt = attr->timeout << 3;
+
+ path->sl = ah->sl & 0xf;
+
+ return 0;
+}
+
+static enum mlx5_qp_optpar opt_mask[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE][MLX5_QP_ST_MAX] = {
+ [MLX5_QP_STATE_INIT] = {
+ [MLX5_QP_STATE_INIT] = {
+ [MLX5_QP_ST_RC] = MLX5_QP_OPTPAR_RRE |
+ MLX5_QP_OPTPAR_RAE |
+ MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PKEY_INDEX |
+ MLX5_QP_OPTPAR_PRI_PORT,
+ [MLX5_QP_ST_UC] = MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PKEY_INDEX |
+ MLX5_QP_OPTPAR_PRI_PORT,
+ [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_PKEY_INDEX |
+ MLX5_QP_OPTPAR_Q_KEY |
+ MLX5_QP_OPTPAR_PRI_PORT,
+ },
+ [MLX5_QP_STATE_RTR] = {
+ [MLX5_QP_ST_RC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH |
+ MLX5_QP_OPTPAR_RRE |
+ MLX5_QP_OPTPAR_RAE |
+ MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PKEY_INDEX,
+ [MLX5_QP_ST_UC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH |
+ MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PKEY_INDEX,
+ [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_PKEY_INDEX |
+ MLX5_QP_OPTPAR_Q_KEY,
+ [MLX5_QP_ST_MLX] = MLX5_QP_OPTPAR_PKEY_INDEX |
+ MLX5_QP_OPTPAR_Q_KEY,
+ },
+ },
+ [MLX5_QP_STATE_RTR] = {
+ [MLX5_QP_STATE_RTS] = {
+ [MLX5_QP_ST_RC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH |
+ MLX5_QP_OPTPAR_RRE |
+ MLX5_QP_OPTPAR_RAE |
+ MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PM_STATE |
+ MLX5_QP_OPTPAR_RNR_TIMEOUT,
+ [MLX5_QP_ST_UC] = MLX5_QP_OPTPAR_ALT_ADDR_PATH |
+ MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PM_STATE,
+ [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_Q_KEY,
+ },
+ },
+ [MLX5_QP_STATE_RTS] = {
+ [MLX5_QP_STATE_RTS] = {
+ [MLX5_QP_ST_RC] = MLX5_QP_OPTPAR_RRE |
+ MLX5_QP_OPTPAR_RAE |
+ MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_RNR_TIMEOUT |
+ MLX5_QP_OPTPAR_PM_STATE,
+ [MLX5_QP_ST_UC] = MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_PM_STATE,
+ [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_Q_KEY |
+ MLX5_QP_OPTPAR_SRQN |
+ MLX5_QP_OPTPAR_CQN_RCV,
+ },
+ },
+ [MLX5_QP_STATE_SQER] = {
+ [MLX5_QP_STATE_RTS] = {
+ [MLX5_QP_ST_UD] = MLX5_QP_OPTPAR_Q_KEY,
+ [MLX5_QP_ST_MLX] = MLX5_QP_OPTPAR_Q_KEY,
+ },
+ },
+};
+
+static int ib_nr_to_mlx5_nr(int ib_mask)
+{
+ switch (ib_mask) {
+ case IB_QP_STATE:
+ return 0;
+ case IB_QP_CUR_STATE:
+ return 0;
+ case IB_QP_EN_SQD_ASYNC_NOTIFY:
+ return 0;
+ case IB_QP_ACCESS_FLAGS:
+ return MLX5_QP_OPTPAR_RWE | MLX5_QP_OPTPAR_RRE |
+ MLX5_QP_OPTPAR_RAE;
+ case IB_QP_PKEY_INDEX:
+ return MLX5_QP_OPTPAR_PKEY_INDEX;
+ case IB_QP_PORT:
+ return MLX5_QP_OPTPAR_PRI_PORT;
+ case IB_QP_QKEY:
+ return MLX5_QP_OPTPAR_Q_KEY;
+ case IB_QP_AV:
+ return MLX5_QP_OPTPAR_PRIMARY_ADDR_PATH |
+ MLX5_QP_OPTPAR_PRI_PORT;
+ case IB_QP_PATH_MTU:
+ return 0;
+ case IB_QP_TIMEOUT:
+ return MLX5_QP_OPTPAR_ACK_TIMEOUT;
+ case IB_QP_RETRY_CNT:
+ return MLX5_QP_OPTPAR_RETRY_COUNT;
+ case IB_QP_RNR_RETRY:
+ return MLX5_QP_OPTPAR_RNR_RETRY;
+ case IB_QP_RQ_PSN:
+ return 0;
+ case IB_QP_MAX_QP_RD_ATOMIC:
+ return MLX5_QP_OPTPAR_SRA_MAX;
+ case IB_QP_ALT_PATH:
+ return MLX5_QP_OPTPAR_ALT_ADDR_PATH;
+ case IB_QP_MIN_RNR_TIMER:
+ return MLX5_QP_OPTPAR_RNR_TIMEOUT;
+ case IB_QP_SQ_PSN:
+ return 0;
+ case IB_QP_MAX_DEST_RD_ATOMIC:
+ return MLX5_QP_OPTPAR_RRA_MAX | MLX5_QP_OPTPAR_RWE |
+ MLX5_QP_OPTPAR_RRE | MLX5_QP_OPTPAR_RAE;
+ case IB_QP_PATH_MIG_STATE:
+ return MLX5_QP_OPTPAR_PM_STATE;
+ case IB_QP_CAP:
+ return 0;
+ case IB_QP_DEST_QPN:
+ return 0;
+ }
+ return 0;
+}
+
+static int ib_mask_to_mlx5_opt(int ib_mask)
+{
+ int result = 0;
+ int i;
+
+ for (i = 0; i < 8 * sizeof(int); i++) {
+ if ((1 << i) & ib_mask)
+ result |= ib_nr_to_mlx5_nr(1 << i);
+ }
+
+ return result;
+}
+
+static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
+ const struct ib_qp_attr *attr, int attr_mask,
+ enum ib_qp_state cur_state, enum ib_qp_state new_state)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
+ struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ struct mlx5_ib_cq *send_cq, *recv_cq;
+ struct mlx5_qp_context *context;
+ struct mlx5_modify_qp_mbox_in *in;
+ struct mlx5_ib_pd *pd;
+ enum mlx5_qp_state mlx5_cur, mlx5_new;
+ enum mlx5_qp_optpar optpar;
+ int sqd_event;
+ int mlx5_st;
+ int err;
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ context = &in->ctx;
+ err = to_mlx5_st(ibqp->qp_type);
+ if (err < 0)
+ goto out;
+
+ context->flags = cpu_to_be32(err << 16);
+
+ if (!(attr_mask & IB_QP_PATH_MIG_STATE)) {
+ context->flags |= cpu_to_be32(MLX5_QP_PM_MIGRATED << 11);
+ } else {
+ switch (attr->path_mig_state) {
+ case IB_MIG_MIGRATED:
+ context->flags |= cpu_to_be32(MLX5_QP_PM_MIGRATED << 11);
+ break;
+ case IB_MIG_REARM:
+ context->flags |= cpu_to_be32(MLX5_QP_PM_REARM << 11);
+ break;
+ case IB_MIG_ARMED:
+ context->flags |= cpu_to_be32(MLX5_QP_PM_ARMED << 11);
+ break;
+ }
+ }
+
+ if (ibqp->qp_type == IB_QPT_GSI || ibqp->qp_type == IB_QPT_SMI) {
+ context->mtu_msgmax = (IB_MTU_256 << 5) | 8;
+ } else if (ibqp->qp_type == IB_QPT_UD ||
+ ibqp->qp_type == MLX5_IB_QPT_REG_UMR) {
+ context->mtu_msgmax = (IB_MTU_4096 << 5) | 12;
+ } else if (attr_mask & IB_QP_PATH_MTU) {
+ if (attr->path_mtu < IB_MTU_256 ||
+ attr->path_mtu > IB_MTU_4096) {
+ mlx5_ib_warn(dev, "invalid mtu %d\n", attr->path_mtu);
+ err = -EINVAL;
+ goto out;
+ }
+ context->mtu_msgmax = (attr->path_mtu << 5) | dev->mdev.caps.log_max_msg;
+ }
+
+ if (attr_mask & IB_QP_DEST_QPN)
+ context->log_pg_sz_remote_qpn = cpu_to_be32(attr->dest_qp_num);
+
+ if (attr_mask & IB_QP_PKEY_INDEX)
+ context->pri_path.pkey_index = attr->pkey_index;
+
+ /* todo implement counter_index functionality */
+
+ if (is_sqp(ibqp->qp_type))
+ context->pri_path.port = qp->port;
+
+ if (attr_mask & IB_QP_PORT)
+ context->pri_path.port = attr->port_num;
+
+ if (attr_mask & IB_QP_AV) {
+ err = mlx5_set_path(dev, &attr->ah_attr, &context->pri_path,
+ attr_mask & IB_QP_PORT ? attr->port_num : qp->port,
+ attr_mask, 0, attr);
+ if (err)
+ goto out;
+ }
+
+ if (attr_mask & IB_QP_TIMEOUT)
+ context->pri_path.ackto_lt |= attr->timeout << 3;
+
+ if (attr_mask & IB_QP_ALT_PATH) {
+ err = mlx5_set_path(dev, &attr->alt_ah_attr, &context->alt_path,
+ attr->alt_port_num, attr_mask, 0, attr);
+ if (err)
+ goto out;
+ }
+
+ pd = get_pd(qp);
+ get_cqs(qp, &send_cq, &recv_cq);
+
+ context->flags_pd = cpu_to_be32(pd ? pd->pdn : to_mpd(dev->devr.p0)->pdn);
+ context->cqn_send = send_cq ? cpu_to_be32(send_cq->mcq.cqn) : 0;
+ context->cqn_recv = recv_cq ? cpu_to_be32(recv_cq->mcq.cqn) : 0;
+ context->params1 = cpu_to_be32(MLX5_IB_ACK_REQ_FREQ << 28);
+
+ if (attr_mask & IB_QP_RNR_RETRY)
+ context->params1 |= cpu_to_be32(attr->rnr_retry << 13);
+
+ if (attr_mask & IB_QP_RETRY_CNT)
+ context->params1 |= cpu_to_be32(attr->retry_cnt << 16);
+
+ if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
+ if (attr->max_rd_atomic)
+ context->params1 |=
+ cpu_to_be32(fls(attr->max_rd_atomic - 1) << 21);
+ }
+
+ if (attr_mask & IB_QP_SQ_PSN)
+ context->next_send_psn = cpu_to_be32(attr->sq_psn);
+
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
+ if (attr->max_dest_rd_atomic)
+ context->params2 |=
+ cpu_to_be32(fls(attr->max_dest_rd_atomic - 1) << 21);
+ }
+
+ if (attr_mask & (IB_QP_ACCESS_FLAGS | IB_QP_MAX_DEST_RD_ATOMIC))
+ context->params2 |= to_mlx5_access_flags(qp, attr, attr_mask);
+
+ if (attr_mask & IB_QP_MIN_RNR_TIMER)
+ context->rnr_nextrecvpsn |= cpu_to_be32(attr->min_rnr_timer << 24);
+
+ if (attr_mask & IB_QP_RQ_PSN)
+ context->rnr_nextrecvpsn |= cpu_to_be32(attr->rq_psn);
+
+ if (attr_mask & IB_QP_QKEY)
+ context->qkey = cpu_to_be32(attr->qkey);
+
+ if (qp->rq.wqe_cnt && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+ context->db_rec_addr = cpu_to_be64(qp->db.dma);
+
+ if (cur_state == IB_QPS_RTS && new_state == IB_QPS_SQD &&
+ attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY && attr->en_sqd_async_notify)
+ sqd_event = 1;
+ else
+ sqd_event = 0;
+
+ if (!ibqp->uobject && cur_state == IB_QPS_RESET && new_state == IB_QPS_INIT)
+ context->sq_crq_size |= cpu_to_be16(1 << 4);
+
+
+ mlx5_cur = to_mlx5_state(cur_state);
+ mlx5_new = to_mlx5_state(new_state);
+ mlx5_st = to_mlx5_st(ibqp->qp_type);
+ if (mlx5_cur < 0 || mlx5_new < 0 || mlx5_st < 0)
+ goto out;
+
+ optpar = ib_mask_to_mlx5_opt(attr_mask);
+ optpar &= opt_mask[mlx5_cur][mlx5_new][mlx5_st];
+ in->optparam = cpu_to_be32(optpar);
+ err = mlx5_core_qp_modify(&dev->mdev, to_mlx5_state(cur_state),
+ to_mlx5_state(new_state), in, sqd_event,
+ &qp->mqp);
+ if (err)
+ goto out;
+
+ qp->state = new_state;
+
+ if (attr_mask & IB_QP_ACCESS_FLAGS)
+ qp->atomic_rd_en = attr->qp_access_flags;
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
+ qp->resp_depth = attr->max_dest_rd_atomic;
+ if (attr_mask & IB_QP_PORT)
+ qp->port = attr->port_num;
+ if (attr_mask & IB_QP_ALT_PATH)
+ qp->alt_port = attr->alt_port_num;
+
+ /*
+ * If we moved a kernel QP to RESET, clean up all old CQ
+ * entries and reinitialize the QP.
+ */
+ if (new_state == IB_QPS_RESET && !ibqp->uobject) {
+ mlx5_ib_cq_clean(recv_cq, qp->mqp.qpn,
+ ibqp->srq ? to_msrq(ibqp->srq) : NULL);
+ if (send_cq != recv_cq)
+ mlx5_ib_cq_clean(send_cq, qp->mqp.qpn, NULL);
+
+ qp->rq.head = 0;
+ qp->rq.tail = 0;
+ qp->sq.head = 0;
+ qp->sq.tail = 0;
+ qp->sq.cur_post = 0;
+ qp->sq.last_poll = 0;
+ qp->db.db[MLX5_RCV_DBR] = 0;
+ qp->db.db[MLX5_SND_DBR] = 0;
+ }
+
+out:
+ kfree(in);
+ return err;
+}
+
+int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+ int attr_mask, struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
+ struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ enum ib_qp_state cur_state, new_state;
+ int err = -EINVAL;
+ int port;
+
+ mutex_lock(&qp->mutex);
+
+ cur_state = attr_mask & IB_QP_CUR_STATE ? attr->cur_qp_state : qp->state;
+ new_state = attr_mask & IB_QP_STATE ? attr->qp_state : cur_state;
+
+ if (ibqp->qp_type != MLX5_IB_QPT_REG_UMR &&
+ !ib_modify_qp_is_ok(cur_state, new_state, ibqp->qp_type, attr_mask))
+ goto out;
+
+ if ((attr_mask & IB_QP_PORT) &&
+ (attr->port_num == 0 || attr->port_num > dev->mdev.caps.num_ports))
+ goto out;
+
+ if (attr_mask & IB_QP_PKEY_INDEX) {
+ port = attr_mask & IB_QP_PORT ? attr->port_num : qp->port;
+ if (attr->pkey_index >= dev->mdev.caps.port[port - 1].pkey_table_len)
+ goto out;
+ }
+
+ if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
+ attr->max_rd_atomic > dev->mdev.caps.max_ra_res_qp)
+ goto out;
+
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC &&
+ attr->max_dest_rd_atomic > dev->mdev.caps.max_ra_req_qp)
+ goto out;
+
+ if (cur_state == new_state && cur_state == IB_QPS_RESET) {
+ err = 0;
+ goto out;
+ }
+
+ err = __mlx5_ib_modify_qp(ibqp, attr, attr_mask, cur_state, new_state);
+
+out:
+ mutex_unlock(&qp->mutex);
+ return err;
+}
+
+static int mlx5_wq_overflow(struct mlx5_ib_wq *wq, int nreq, struct ib_cq *ib_cq)
+{
+ struct mlx5_ib_cq *cq;
+ unsigned cur;
+
+ cur = wq->head - wq->tail;
+ if (likely(cur + nreq < wq->max_post))
+ return 0;
+
+ cq = to_mcq(ib_cq);
+ spin_lock(&cq->lock);
+ cur = wq->head - wq->tail;
+ spin_unlock(&cq->lock);
+
+ return cur + nreq >= wq->max_post;
+}
+
+static __always_inline void set_raddr_seg(struct mlx5_wqe_raddr_seg *rseg,
+ u64 remote_addr, u32 rkey)
+{
+ rseg->raddr = cpu_to_be64(remote_addr);
+ rseg->rkey = cpu_to_be32(rkey);
+ rseg->reserved = 0;
+}
+
+static void set_atomic_seg(struct mlx5_wqe_atomic_seg *aseg, struct ib_send_wr *wr)
+{
+ if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
+ aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap);
+ aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add);
+ } else if (wr->opcode == IB_WR_MASKED_ATOMIC_FETCH_AND_ADD) {
+ aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add);
+ aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add_mask);
+ } else {
+ aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add);
+ aseg->compare = 0;
+ }
+}
+
+static void set_masked_atomic_seg(struct mlx5_wqe_masked_atomic_seg *aseg,
+ struct ib_send_wr *wr)
+{
+ aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap);
+ aseg->swap_add_mask = cpu_to_be64(wr->wr.atomic.swap_mask);
+ aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add);
+ aseg->compare_mask = cpu_to_be64(wr->wr.atomic.compare_add_mask);
+}
+
+static void set_datagram_seg(struct mlx5_wqe_datagram_seg *dseg,
+ struct ib_send_wr *wr)
+{
+ memcpy(&dseg->av, &to_mah(wr->wr.ud.ah)->av, sizeof(struct mlx5_av));
+ dseg->av.dqp_dct = cpu_to_be32(wr->wr.ud.remote_qpn | MLX5_EXTENDED_UD_AV);
+ dseg->av.key.qkey.qkey = cpu_to_be32(wr->wr.ud.remote_qkey);
+}
+
+static void set_data_ptr_seg(struct mlx5_wqe_data_seg *dseg, struct ib_sge *sg)
+{
+ dseg->byte_count = cpu_to_be32(sg->length);
+ dseg->lkey = cpu_to_be32(sg->lkey);
+ dseg->addr = cpu_to_be64(sg->addr);
+}
+
+static __be16 get_klm_octo(int npages)
+{
+ return cpu_to_be16(ALIGN(npages, 8) / 2);
+}
+
+static __be64 frwr_mkey_mask(void)
+{
+ u64 result;
+
+ result = MLX5_MKEY_MASK_LEN |
+ MLX5_MKEY_MASK_PAGE_SIZE |
+ MLX5_MKEY_MASK_START_ADDR |
+ MLX5_MKEY_MASK_EN_RINVAL |
+ MLX5_MKEY_MASK_KEY |
+ MLX5_MKEY_MASK_LR |
+ MLX5_MKEY_MASK_LW |
+ MLX5_MKEY_MASK_RR |
+ MLX5_MKEY_MASK_RW |
+ MLX5_MKEY_MASK_A |
+ MLX5_MKEY_MASK_SMALL_FENCE |
+ MLX5_MKEY_MASK_FREE;
+
+ return cpu_to_be64(result);
+}
+
+static void set_frwr_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
+ struct ib_send_wr *wr, int li)
+{
+ memset(umr, 0, sizeof(*umr));
+
+ if (li) {
+ umr->mkey_mask = cpu_to_be64(MLX5_MKEY_MASK_FREE);
+ umr->flags = 1 << 7;
+ return;
+ }
+
+ umr->flags = (1 << 5); /* fail if not free */
+ umr->klm_octowords = get_klm_octo(wr->wr.fast_reg.page_list_len);
+ umr->mkey_mask = frwr_mkey_mask();
+}
+
+static void set_reg_umr_segment(struct mlx5_wqe_umr_ctrl_seg *umr,
+ struct ib_send_wr *wr)
+{
+ struct umr_wr *umrwr = (struct umr_wr *)&wr->wr.fast_reg;
+ u64 mask;
+
+ memset(umr, 0, sizeof(*umr));
+
+ if (!(wr->send_flags & MLX5_IB_SEND_UMR_UNREG)) {
+ umr->flags = 1 << 5; /* fail if not free */
+ umr->klm_octowords = get_klm_octo(umrwr->npages);
+ mask = MLX5_MKEY_MASK_LEN |
+ MLX5_MKEY_MASK_PAGE_SIZE |
+ MLX5_MKEY_MASK_START_ADDR |
+ MLX5_MKEY_MASK_PD |
+ MLX5_MKEY_MASK_LR |
+ MLX5_MKEY_MASK_LW |
+ MLX5_MKEY_MASK_RR |
+ MLX5_MKEY_MASK_RW |
+ MLX5_MKEY_MASK_A |
+ MLX5_MKEY_MASK_FREE;
+ umr->mkey_mask = cpu_to_be64(mask);
+ } else {
+ umr->flags = 2 << 5; /* fail if free */
+ mask = MLX5_MKEY_MASK_FREE;
+ umr->mkey_mask = cpu_to_be64(mask);
+ }
+
+ if (!wr->num_sge)
+ umr->flags |= (1 << 7); /* inline */
+}
+
+static u8 get_umr_flags(int acc)
+{
+ return (acc & IB_ACCESS_REMOTE_ATOMIC ? MLX5_PERM_ATOMIC : 0) |
+ (acc & IB_ACCESS_REMOTE_WRITE ? MLX5_PERM_REMOTE_WRITE : 0) |
+ (acc & IB_ACCESS_REMOTE_READ ? MLX5_PERM_REMOTE_READ : 0) |
+ (acc & IB_ACCESS_LOCAL_WRITE ? MLX5_PERM_LOCAL_WRITE : 0) |
+ MLX5_PERM_LOCAL_READ | MLX5_PERM_UMR_EN | MLX5_ACCESS_MODE_MTT;
+}
+
+static void set_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr,
+ int li, int *writ)
+{
+ memset(seg, 0, sizeof(*seg));
+ if (li) {
+ seg->status = 1 << 6;
+ return;
+ }
+
+ seg->flags = get_umr_flags(wr->wr.fast_reg.access_flags);
+ *writ = seg->flags & (MLX5_PERM_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE);
+ seg->qpn_mkey7_0 = cpu_to_be32((wr->wr.fast_reg.rkey & 0xff) | 0xffffff00);
+ seg->flags_pd = cpu_to_be32(MLX5_MKEY_REMOTE_INVAL);
+ seg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start);
+ seg->len = cpu_to_be64(wr->wr.fast_reg.length);
+ seg->xlt_oct_size = cpu_to_be32((wr->wr.fast_reg.page_list_len + 1) / 2);
+ seg->log2_page_size = wr->wr.fast_reg.page_shift;
+}
+
+static void set_reg_mkey_segment(struct mlx5_mkey_seg *seg, struct ib_send_wr *wr)
+{
+ memset(seg, 0, sizeof(*seg));
+ if (wr->send_flags & MLX5_IB_SEND_UMR_UNREG) {
+ seg->status = 1 << 6;
+ return;
+ }
+
+ seg->flags = convert_access(wr->wr.fast_reg.access_flags);
+ seg->flags_pd = cpu_to_be32(to_mpd((struct ib_pd *)wr->wr.fast_reg.page_list)->pdn);
+ seg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start);
+ seg->len = cpu_to_be64(wr->wr.fast_reg.length);
+ seg->log2_page_size = wr->wr.fast_reg.page_shift;
+ seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8);
+}
+
+static void set_frwr_pages(struct mlx5_wqe_data_seg *dseg,
+ struct ib_send_wr *wr,
+ struct mlx5_core_dev *mdev,
+ struct mlx5_ib_pd *pd,
+ int writ)
+{
+ struct mlx5_ib_fast_reg_page_list *mfrpl = to_mfrpl(wr->wr.fast_reg.page_list);
+ u64 *page_list = wr->wr.fast_reg.page_list->page_list;
+ u64 perm = MLX5_EN_RD | (writ ? MLX5_EN_WR : 0);
+ int i;
+
+ for (i = 0; i < wr->wr.fast_reg.page_list_len; i++)
+ mfrpl->mapped_page_list[i] = cpu_to_be64(page_list[i] | perm);
+ dseg->addr = cpu_to_be64(mfrpl->map);
+ dseg->byte_count = cpu_to_be32(ALIGN(sizeof(u64) * wr->wr.fast_reg.page_list_len, 64));
+ dseg->lkey = cpu_to_be32(pd->pa_lkey);
+}
+
+static __be32 send_ieth(struct ib_send_wr *wr)
+{
+ switch (wr->opcode) {
+ case IB_WR_SEND_WITH_IMM:
+ case IB_WR_RDMA_WRITE_WITH_IMM:
+ return wr->ex.imm_data;
+
+ case IB_WR_SEND_WITH_INV:
+ return cpu_to_be32(wr->ex.invalidate_rkey);
+
+ default:
+ return 0;
+ }
+}
+
+static u8 calc_sig(void *wqe, int size)
+{
+ u8 *p = wqe;
+ u8 res = 0;
+ int i;
+
+ for (i = 0; i < size; i++)
+ res ^= p[i];
+
+ return ~res;
+}
+
+static u8 wq_sig(void *wqe)
+{
+ return calc_sig(wqe, (*((u8 *)wqe + 8) & 0x3f) << 4);
+}
+
+static int set_data_inl_seg(struct mlx5_ib_qp *qp, struct ib_send_wr *wr,
+ void *wqe, int *sz)
+{
+ struct mlx5_wqe_inline_seg *seg;
+ void *qend = qp->sq.qend;
+ void *addr;
+ int inl = 0;
+ int copy;
+ int len;
+ int i;
+
+ seg = wqe;
+ wqe += sizeof(*seg);
+ for (i = 0; i < wr->num_sge; i++) {
+ addr = (void *)(unsigned long)(wr->sg_list[i].addr);
+ len = wr->sg_list[i].length;
+ inl += len;
+
+ if (unlikely(inl > qp->max_inline_data))
+ return -ENOMEM;
+
+ if (unlikely(wqe + len > qend)) {
+ copy = qend - wqe;
+ memcpy(wqe, addr, copy);
+ addr += copy;
+ len -= copy;
+ wqe = mlx5_get_send_wqe(qp, 0);
+ }
+ memcpy(wqe, addr, len);
+ wqe += len;
+ }
+
+ seg->byte_count = cpu_to_be32(inl | MLX5_INLINE_SEG);
+
+ *sz = ALIGN(inl + sizeof(seg->byte_count), 16) / 16;
+
+ return 0;
+}
+
+static int set_frwr_li_wr(void **seg, struct ib_send_wr *wr, int *size,
+ struct mlx5_core_dev *mdev, struct mlx5_ib_pd *pd, struct mlx5_ib_qp *qp)
+{
+ int writ = 0;
+ int li;
+
+ li = wr->opcode == IB_WR_LOCAL_INV ? 1 : 0;
+ if (unlikely(wr->send_flags & IB_SEND_INLINE))
+ return -EINVAL;
+
+ set_frwr_umr_segment(*seg, wr, li);
+ *seg += sizeof(struct mlx5_wqe_umr_ctrl_seg);
+ *size += sizeof(struct mlx5_wqe_umr_ctrl_seg) / 16;
+ if (unlikely((*seg == qp->sq.qend)))
+ *seg = mlx5_get_send_wqe(qp, 0);
+ set_mkey_segment(*seg, wr, li, &writ);
+ *seg += sizeof(struct mlx5_mkey_seg);
+ *size += sizeof(struct mlx5_mkey_seg) / 16;
+ if (unlikely((*seg == qp->sq.qend)))
+ *seg = mlx5_get_send_wqe(qp, 0);
+ if (!li) {
+ set_frwr_pages(*seg, wr, mdev, pd, writ);
+ *seg += sizeof(struct mlx5_wqe_data_seg);
+ *size += (sizeof(struct mlx5_wqe_data_seg) / 16);
+ }
+ return 0;
+}
+
+static void dump_wqe(struct mlx5_ib_qp *qp, int idx, int size_16)
+{
+ __be32 *p = NULL;
+ int tidx = idx;
+ int i, j;
+
+ pr_debug("dump wqe at %p\n", mlx5_get_send_wqe(qp, tidx));
+ for (i = 0, j = 0; i < size_16 * 4; i += 4, j += 4) {
+ if ((i & 0xf) == 0) {
+ void *buf = mlx5_get_send_wqe(qp, tidx);
+ tidx = (tidx + 1) & (qp->sq.wqe_cnt - 1);
+ p = buf;
+ j = 0;
+ }
+ pr_debug("%08x %08x %08x %08x\n", be32_to_cpu(p[j]),
+ be32_to_cpu(p[j + 1]), be32_to_cpu(p[j + 2]),
+ be32_to_cpu(p[j + 3]));
+ }
+}
+
+static void mlx5_bf_copy(u64 __iomem *dst, u64 *src,
+ unsigned bytecnt, struct mlx5_ib_qp *qp)
+{
+ while (bytecnt > 0) {
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ __iowrite64_copy(dst++, src++, 8);
+ bytecnt -= 64;
+ if (unlikely(src == qp->sq.qend))
+ src = mlx5_get_send_wqe(qp, 0);
+ }
+}
+
+static u8 get_fence(u8 fence, struct ib_send_wr *wr)
+{
+ if (unlikely(wr->opcode == IB_WR_LOCAL_INV &&
+ wr->send_flags & IB_SEND_FENCE))
+ return MLX5_FENCE_MODE_STRONG_ORDERING;
+
+ if (unlikely(fence)) {
+ if (wr->send_flags & IB_SEND_FENCE)
+ return MLX5_FENCE_MODE_SMALL_AND_FENCE;
+ else
+ return fence;
+
+ } else {
+ return 0;
+ }
+}
+
+int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+ struct ib_send_wr **bad_wr)
+{
+ struct mlx5_wqe_ctrl_seg *ctrl = NULL; /* compiler warning */
+ struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
+ struct mlx5_core_dev *mdev = &dev->mdev;
+ struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ struct mlx5_wqe_data_seg *dpseg;
+ struct mlx5_wqe_xrc_seg *xrc;
+ struct mlx5_bf *bf = qp->bf;
+ int uninitialized_var(size);
+ void *qend = qp->sq.qend;
+ unsigned long flags;
+ u32 mlx5_opcode;
+ unsigned idx;
+ int err = 0;
+ int inl = 0;
+ int num_sge;
+ void *seg;
+ int nreq;
+ int i;
+ u8 next_fence = 0;
+ u8 opmod = 0;
+ u8 fence;
+
+ spin_lock_irqsave(&qp->sq.lock, flags);
+
+ for (nreq = 0; wr; nreq++, wr = wr->next) {
+ if (unlikely(wr->opcode >= sizeof(mlx5_ib_opcode) / sizeof(mlx5_ib_opcode[0]))) {
+ mlx5_ib_warn(dev, "\n");
+ err = -EINVAL;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ if (unlikely(mlx5_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq))) {
+ mlx5_ib_warn(dev, "\n");
+ err = -ENOMEM;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ fence = qp->fm_cache;
+ num_sge = wr->num_sge;
+ if (unlikely(num_sge > qp->sq.max_gs)) {
+ mlx5_ib_warn(dev, "\n");
+ err = -ENOMEM;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ idx = qp->sq.cur_post & (qp->sq.wqe_cnt - 1);
+ seg = mlx5_get_send_wqe(qp, idx);
+ ctrl = seg;
+ *(uint32_t *)(seg + 8) = 0;
+ ctrl->imm = send_ieth(wr);
+ ctrl->fm_ce_se = qp->sq_signal_bits |
+ (wr->send_flags & IB_SEND_SIGNALED ?
+ MLX5_WQE_CTRL_CQ_UPDATE : 0) |
+ (wr->send_flags & IB_SEND_SOLICITED ?
+ MLX5_WQE_CTRL_SOLICITED : 0);
+
+ seg += sizeof(*ctrl);
+ size = sizeof(*ctrl) / 16;
+
+ switch (ibqp->qp_type) {
+ case IB_QPT_XRC_INI:
+ xrc = seg;
+ xrc->xrc_srqn = htonl(wr->xrc_remote_srq_num);
+ seg += sizeof(*xrc);
+ size += sizeof(*xrc) / 16;
+ /* fall through */
+ case IB_QPT_RC:
+ switch (wr->opcode) {
+ case IB_WR_RDMA_READ:
+ case IB_WR_RDMA_WRITE:
+ case IB_WR_RDMA_WRITE_WITH_IMM:
+ set_raddr_seg(seg, wr->wr.rdma.remote_addr,
+ wr->wr.rdma.rkey);
+ seg += sizeof(struct mlx5_wqe_raddr_seg);
+ size += sizeof(struct mlx5_wqe_raddr_seg) / 16;
+ break;
+
+ case IB_WR_ATOMIC_CMP_AND_SWP:
+ case IB_WR_ATOMIC_FETCH_AND_ADD:
+ set_raddr_seg(seg, wr->wr.atomic.remote_addr,
+ wr->wr.atomic.rkey);
+ seg += sizeof(struct mlx5_wqe_raddr_seg);
+
+ set_atomic_seg(seg, wr);
+ seg += sizeof(struct mlx5_wqe_atomic_seg);
+
+ size += (sizeof(struct mlx5_wqe_raddr_seg) +
+ sizeof(struct mlx5_wqe_atomic_seg)) / 16;
+ break;
+
+ case IB_WR_MASKED_ATOMIC_CMP_AND_SWP:
+ set_raddr_seg(seg, wr->wr.atomic.remote_addr,
+ wr->wr.atomic.rkey);
+ seg += sizeof(struct mlx5_wqe_raddr_seg);
+
+ set_masked_atomic_seg(seg, wr);
+ seg += sizeof(struct mlx5_wqe_masked_atomic_seg);
+
+ size += (sizeof(struct mlx5_wqe_raddr_seg) +
+ sizeof(struct mlx5_wqe_masked_atomic_seg)) / 16;
+ break;
+
+ case IB_WR_LOCAL_INV:
+ next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
+ qp->sq.wr_data[idx] = IB_WR_LOCAL_INV;
+ ctrl->imm = cpu_to_be32(wr->ex.invalidate_rkey);
+ err = set_frwr_li_wr(&seg, wr, &size, mdev, to_mpd(ibqp->pd), qp);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ *bad_wr = wr;
+ goto out;
+ }
+ num_sge = 0;
+ break;
+
+ case IB_WR_FAST_REG_MR:
+ next_fence = MLX5_FENCE_MODE_INITIATOR_SMALL;
+ qp->sq.wr_data[idx] = IB_WR_FAST_REG_MR;
+ ctrl->imm = cpu_to_be32(wr->wr.fast_reg.rkey);
+ err = set_frwr_li_wr(&seg, wr, &size, mdev, to_mpd(ibqp->pd), qp);
+ if (err) {
+ mlx5_ib_warn(dev, "\n");
+ *bad_wr = wr;
+ goto out;
+ }
+ num_sge = 0;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case IB_QPT_UC:
+ switch (wr->opcode) {
+ case IB_WR_RDMA_WRITE:
+ case IB_WR_RDMA_WRITE_WITH_IMM:
+ set_raddr_seg(seg, wr->wr.rdma.remote_addr,
+ wr->wr.rdma.rkey);
+ seg += sizeof(struct mlx5_wqe_raddr_seg);
+ size += sizeof(struct mlx5_wqe_raddr_seg) / 16;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case IB_QPT_UD:
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ set_datagram_seg(seg, wr);
+ seg += sizeof(struct mlx5_wqe_datagram_seg);
+ size += sizeof(struct mlx5_wqe_datagram_seg) / 16;
+ if (unlikely((seg == qend)))
+ seg = mlx5_get_send_wqe(qp, 0);
+ break;
+
+ case MLX5_IB_QPT_REG_UMR:
+ if (wr->opcode != MLX5_IB_WR_UMR) {
+ err = -EINVAL;
+ mlx5_ib_warn(dev, "bad opcode\n");
+ goto out;
+ }
+ qp->sq.wr_data[idx] = MLX5_IB_WR_UMR;
+ ctrl->imm = cpu_to_be32(wr->wr.fast_reg.rkey);
+ set_reg_umr_segment(seg, wr);
+ seg += sizeof(struct mlx5_wqe_umr_ctrl_seg);
+ size += sizeof(struct mlx5_wqe_umr_ctrl_seg) / 16;
+ if (unlikely((seg == qend)))
+ seg = mlx5_get_send_wqe(qp, 0);
+ set_reg_mkey_segment(seg, wr);
+ seg += sizeof(struct mlx5_mkey_seg);
+ size += sizeof(struct mlx5_mkey_seg) / 16;
+ if (unlikely((seg == qend)))
+ seg = mlx5_get_send_wqe(qp, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ if (wr->send_flags & IB_SEND_INLINE && num_sge) {
+ int uninitialized_var(sz);
+
+ err = set_data_inl_seg(qp, wr, seg, &sz);
+ if (unlikely(err)) {
+ mlx5_ib_warn(dev, "\n");
+ *bad_wr = wr;
+ goto out;
+ }
+ inl = 1;
+ size += sz;
+ } else {
+ dpseg = seg;
+ for (i = 0; i < num_sge; i++) {
+ if (unlikely(dpseg == qend)) {
+ seg = mlx5_get_send_wqe(qp, 0);
+ dpseg = seg;
+ }
+ if (likely(wr->sg_list[i].length)) {
+ set_data_ptr_seg(dpseg, wr->sg_list + i);
+ size += sizeof(struct mlx5_wqe_data_seg) / 16;
+ dpseg++;
+ }
+ }
+ }
+
+ mlx5_opcode = mlx5_ib_opcode[wr->opcode];
+ ctrl->opmod_idx_opcode = cpu_to_be32(((u32)(qp->sq.cur_post) << 8) |
+ mlx5_opcode |
+ ((u32)opmod << 24));
+ ctrl->qpn_ds = cpu_to_be32(size | (qp->mqp.qpn << 8));
+ ctrl->fm_ce_se |= get_fence(fence, wr);
+ qp->fm_cache = next_fence;
+ if (unlikely(qp->wq_sig))
+ ctrl->signature = wq_sig(ctrl);
+
+ qp->sq.wrid[idx] = wr->wr_id;
+ qp->sq.w_list[idx].opcode = mlx5_opcode;
+ qp->sq.wqe_head[idx] = qp->sq.head + nreq;
+ qp->sq.cur_post += DIV_ROUND_UP(size * 16, MLX5_SEND_WQE_BB);
+ qp->sq.w_list[idx].next = qp->sq.cur_post;
+
+ if (0)
+ dump_wqe(qp, idx, size);
+ }
+
+out:
+ if (likely(nreq)) {
+ qp->sq.head += nreq;
+
+ /* Make sure that descriptors are written before
+ * updating doorbell record and ringing the doorbell
+ */
+ wmb();
+
+ qp->db.db[MLX5_SND_DBR] = cpu_to_be32(qp->sq.cur_post);
+
+ if (bf->need_lock)
+ spin_lock(&bf->lock);
+
+ /* TBD enable WC */
+ if (0 && nreq == 1 && bf->uuarn && inl && size > 1 && size <= bf->buf_size / 16) {
+ mlx5_bf_copy(bf->reg + bf->offset, (u64 *)ctrl, ALIGN(size * 16, 64), qp);
+ /* wc_wmb(); */
+ } else {
+ mlx5_write64((__be32 *)ctrl, bf->regreg + bf->offset,
+ MLX5_GET_DOORBELL_LOCK(&bf->lock32));
+ /* Make sure doorbells don't leak out of SQ spinlock
+ * and reach the HCA out of order.
+ */
+ mmiowb();
+ }
+ bf->offset ^= bf->buf_size;
+ if (bf->need_lock)
+ spin_unlock(&bf->lock);
+ }
+
+ spin_unlock_irqrestore(&qp->sq.lock, flags);
+
+ return err;
+}
+
+static void set_sig_seg(struct mlx5_rwqe_sig *sig, int size)
+{
+ sig->signature = calc_sig(sig, size);
+}
+
+int mlx5_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+ struct ib_recv_wr **bad_wr)
+{
+ struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ struct mlx5_wqe_data_seg *scat;
+ struct mlx5_rwqe_sig *sig;
+ unsigned long flags;
+ int err = 0;
+ int nreq;
+ int ind;
+ int i;
+
+ spin_lock_irqsave(&qp->rq.lock, flags);
+
+ ind = qp->rq.head & (qp->rq.wqe_cnt - 1);
+
+ for (nreq = 0; wr; nreq++, wr = wr->next) {
+ if (mlx5_wq_overflow(&qp->rq, nreq, qp->ibqp.recv_cq)) {
+ err = -ENOMEM;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ if (unlikely(wr->num_sge > qp->rq.max_gs)) {
+ err = -EINVAL;
+ *bad_wr = wr;
+ goto out;
+ }
+
+ scat = get_recv_wqe(qp, ind);
+ if (qp->wq_sig)
+ scat++;
+
+ for (i = 0; i < wr->num_sge; i++)
+ set_data_ptr_seg(scat + i, wr->sg_list + i);
+
+ if (i < qp->rq.max_gs) {
+ scat[i].byte_count = 0;
+ scat[i].lkey = cpu_to_be32(MLX5_INVALID_LKEY);
+ scat[i].addr = 0;
+ }
+
+ if (qp->wq_sig) {
+ sig = (struct mlx5_rwqe_sig *)scat;
+ set_sig_seg(sig, (qp->rq.max_gs + 1) << 2);
+ }
+
+ qp->rq.wrid[ind] = wr->wr_id;
+
+ ind = (ind + 1) & (qp->rq.wqe_cnt - 1);
+ }
+
+out:
+ if (likely(nreq)) {
+ qp->rq.head += nreq;
+
+ /* Make sure that descriptors are written before
+ * doorbell record.
+ */
+ wmb();
+
+ *qp->db.db = cpu_to_be32(qp->rq.head & 0xffff);
+ }
+
+ spin_unlock_irqrestore(&qp->rq.lock, flags);
+
+ return err;
+}
+
+static inline enum ib_qp_state to_ib_qp_state(enum mlx5_qp_state mlx5_state)
+{
+ switch (mlx5_state) {
+ case MLX5_QP_STATE_RST: return IB_QPS_RESET;
+ case MLX5_QP_STATE_INIT: return IB_QPS_INIT;
+ case MLX5_QP_STATE_RTR: return IB_QPS_RTR;
+ case MLX5_QP_STATE_RTS: return IB_QPS_RTS;
+ case MLX5_QP_STATE_SQ_DRAINING:
+ case MLX5_QP_STATE_SQD: return IB_QPS_SQD;
+ case MLX5_QP_STATE_SQER: return IB_QPS_SQE;
+ case MLX5_QP_STATE_ERR: return IB_QPS_ERR;
+ default: return -1;
+ }
+}
+
+static inline enum ib_mig_state to_ib_mig_state(int mlx5_mig_state)
+{
+ switch (mlx5_mig_state) {
+ case MLX5_QP_PM_ARMED: return IB_MIG_ARMED;
+ case MLX5_QP_PM_REARM: return IB_MIG_REARM;
+ case MLX5_QP_PM_MIGRATED: return IB_MIG_MIGRATED;
+ default: return -1;
+ }
+}
+
+static int to_ib_qp_access_flags(int mlx5_flags)
+{
+ int ib_flags = 0;
+
+ if (mlx5_flags & MLX5_QP_BIT_RRE)
+ ib_flags |= IB_ACCESS_REMOTE_READ;
+ if (mlx5_flags & MLX5_QP_BIT_RWE)
+ ib_flags |= IB_ACCESS_REMOTE_WRITE;
+ if (mlx5_flags & MLX5_QP_BIT_RAE)
+ ib_flags |= IB_ACCESS_REMOTE_ATOMIC;
+
+ return ib_flags;
+}
+
+static void to_ib_ah_attr(struct mlx5_ib_dev *ibdev, struct ib_ah_attr *ib_ah_attr,
+ struct mlx5_qp_path *path)
+{
+ struct mlx5_core_dev *dev = &ibdev->mdev;
+
+ memset(ib_ah_attr, 0, sizeof(*ib_ah_attr));
+ ib_ah_attr->port_num = path->port;
+
+ if (ib_ah_attr->port_num == 0 || ib_ah_attr->port_num > dev->caps.num_ports)
+ return;
+
+ ib_ah_attr->sl = path->sl & 0xf;
+
+ ib_ah_attr->dlid = be16_to_cpu(path->rlid);
+ ib_ah_attr->src_path_bits = path->grh_mlid & 0x7f;
+ ib_ah_attr->static_rate = path->static_rate ? path->static_rate - 5 : 0;
+ ib_ah_attr->ah_flags = (path->grh_mlid & (1 << 7)) ? IB_AH_GRH : 0;
+ if (ib_ah_attr->ah_flags) {
+ ib_ah_attr->grh.sgid_index = path->mgid_index;
+ ib_ah_attr->grh.hop_limit = path->hop_limit;
+ ib_ah_attr->grh.traffic_class =
+ (be32_to_cpu(path->tclass_flowlabel) >> 20) & 0xff;
+ ib_ah_attr->grh.flow_label =
+ be32_to_cpu(path->tclass_flowlabel) & 0xfffff;
+ memcpy(ib_ah_attr->grh.dgid.raw,
+ path->rgid, sizeof(ib_ah_attr->grh.dgid.raw));
+ }
+}
+
+int mlx5_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_mask,
+ struct ib_qp_init_attr *qp_init_attr)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
+ struct mlx5_ib_qp *qp = to_mqp(ibqp);
+ struct mlx5_query_qp_mbox_out *outb;
+ struct mlx5_qp_context *context;
+ int mlx5_state;
+ int err = 0;
+
+ mutex_lock(&qp->mutex);
+ outb = kzalloc(sizeof(*outb), GFP_KERNEL);
+ if (!outb) {
+ err = -ENOMEM;
+ goto out;
+ }
+ context = &outb->ctx;
+ err = mlx5_core_qp_query(&dev->mdev, &qp->mqp, outb, sizeof(*outb));
+ if (err)
+ goto out_free;
+
+ mlx5_state = be32_to_cpu(context->flags) >> 28;
+
+ qp->state = to_ib_qp_state(mlx5_state);
+ qp_attr->qp_state = qp->state;
+ qp_attr->path_mtu = context->mtu_msgmax >> 5;
+ qp_attr->path_mig_state =
+ to_ib_mig_state((be32_to_cpu(context->flags) >> 11) & 0x3);
+ qp_attr->qkey = be32_to_cpu(context->qkey);
+ qp_attr->rq_psn = be32_to_cpu(context->rnr_nextrecvpsn) & 0xffffff;
+ qp_attr->sq_psn = be32_to_cpu(context->next_send_psn) & 0xffffff;
+ qp_attr->dest_qp_num = be32_to_cpu(context->log_pg_sz_remote_qpn) & 0xffffff;
+ qp_attr->qp_access_flags =
+ to_ib_qp_access_flags(be32_to_cpu(context->params2));
+
+ if (qp->ibqp.qp_type == IB_QPT_RC || qp->ibqp.qp_type == IB_QPT_UC) {
+ to_ib_ah_attr(dev, &qp_attr->ah_attr, &context->pri_path);
+ to_ib_ah_attr(dev, &qp_attr->alt_ah_attr, &context->alt_path);
+ qp_attr->alt_pkey_index = context->alt_path.pkey_index & 0x7f;
+ qp_attr->alt_port_num = qp_attr->alt_ah_attr.port_num;
+ }
+
+ qp_attr->pkey_index = context->pri_path.pkey_index & 0x7f;
+ qp_attr->port_num = context->pri_path.port;
+
+ /* qp_attr->en_sqd_async_notify is only applicable in modify qp */
+ qp_attr->sq_draining = mlx5_state == MLX5_QP_STATE_SQ_DRAINING;
+
+ qp_attr->max_rd_atomic = 1 << ((be32_to_cpu(context->params1) >> 21) & 0x7);
+
+ qp_attr->max_dest_rd_atomic =
+ 1 << ((be32_to_cpu(context->params2) >> 21) & 0x7);
+ qp_attr->min_rnr_timer =
+ (be32_to_cpu(context->rnr_nextrecvpsn) >> 24) & 0x1f;
+ qp_attr->timeout = context->pri_path.ackto_lt >> 3;
+ qp_attr->retry_cnt = (be32_to_cpu(context->params1) >> 16) & 0x7;
+ qp_attr->rnr_retry = (be32_to_cpu(context->params1) >> 13) & 0x7;
+ qp_attr->alt_timeout = context->alt_path.ackto_lt >> 3;
+ qp_attr->cur_qp_state = qp_attr->qp_state;
+ qp_attr->cap.max_recv_wr = qp->rq.wqe_cnt;
+ qp_attr->cap.max_recv_sge = qp->rq.max_gs;
+
+ if (!ibqp->uobject) {
+ qp_attr->cap.max_send_wr = qp->sq.wqe_cnt;
+ qp_attr->cap.max_send_sge = qp->sq.max_gs;
+ } else {
+ qp_attr->cap.max_send_wr = 0;
+ qp_attr->cap.max_send_sge = 0;
+ }
+
+ /* We don't support inline sends for kernel QPs (yet), and we
+ * don't know what userspace's value should be.
+ */
+ qp_attr->cap.max_inline_data = 0;
+
+ qp_init_attr->cap = qp_attr->cap;
+
+ qp_init_attr->create_flags = 0;
+ if (qp->flags & MLX5_IB_QP_BLOCK_MULTICAST_LOOPBACK)
+ qp_init_attr->create_flags |= IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK;
+
+ qp_init_attr->sq_sig_type = qp->sq_signal_bits & MLX5_WQE_CTRL_CQ_UPDATE ?
+ IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR;
+
+out_free:
+ kfree(outb);
+
+out:
+ mutex_unlock(&qp->mutex);
+ return err;
+}
+
+struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibdev);
+ struct mlx5_ib_xrcd *xrcd;
+ int err;
+
+ if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_XRC))
+ return ERR_PTR(-ENOSYS);
+
+ xrcd = kmalloc(sizeof(*xrcd), GFP_KERNEL);
+ if (!xrcd)
+ return ERR_PTR(-ENOMEM);
+
+ err = mlx5_core_xrcd_alloc(&dev->mdev, &xrcd->xrcdn);
+ if (err) {
+ kfree(xrcd);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return &xrcd->ibxrcd;
+}
+
+int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd)
+{
+ struct mlx5_ib_dev *dev = to_mdev(xrcd->device);
+ u32 xrcdn = to_mxrcd(xrcd)->xrcdn;
+ int err;
+
+ err = mlx5_core_xrcd_dealloc(&dev->mdev, xrcdn);
+ if (err) {
+ mlx5_ib_warn(dev, "failed to dealloc xrcdn 0x%x\n", xrcdn);
+ return err;
+ }
+
+ kfree(xrcd);
+
+ return 0;
+}
diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c
new file mode 100644
index 0000000..84d297a
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/srq.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/module.h>
+#include <linux/mlx5/qp.h>
+#include <linux/mlx5/srq.h>
+#include <linux/slab.h>
+#include <rdma/ib_umem.h>
+
+#include "mlx5_ib.h"
+#include "user.h"
+
+/* not supported currently */
+static int srq_signature;
+
+static void *get_wqe(struct mlx5_ib_srq *srq, int n)
+{
+ return mlx5_buf_offset(&srq->buf, n << srq->msrq.wqe_shift);
+}
+
+static void mlx5_ib_srq_event(struct mlx5_core_srq *srq, enum mlx5_event type)
+{
+ struct ib_event event;
+ struct ib_srq *ibsrq = &to_mibsrq(srq)->ibsrq;
+
+ if (ibsrq->event_handler) {
+ event.device = ibsrq->device;
+ event.element.srq = ibsrq;
+ switch (type) {
+ case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
+ event.event = IB_EVENT_SRQ_LIMIT_REACHED;
+ break;
+ case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
+ event.event = IB_EVENT_SRQ_ERR;
+ break;
+ default:
+ pr_warn("mlx5_ib: Unexpected event type %d on SRQ %06x\n",
+ type, srq->srqn);
+ return;
+ }
+
+ ibsrq->event_handler(&event, ibsrq->srq_context);
+ }
+}
+
+static int create_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq,
+ struct mlx5_create_srq_mbox_in **in,
+ struct ib_udata *udata, int buf_size, int *inlen)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_ib_create_srq ucmd;
+ int err;
+ int npages;
+ int page_shift;
+ int ncont;
+ u32 offset;
+
+ if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) {
+ mlx5_ib_dbg(dev, "failed copy udata\n");
+ return -EFAULT;
+ }
+ srq->wq_sig = !!(ucmd.flags & MLX5_SRQ_FLAG_SIGNATURE);
+
+ srq->umem = ib_umem_get(pd->uobject->context, ucmd.buf_addr, buf_size,
+ 0, 0);
+ if (IS_ERR(srq->umem)) {
+ mlx5_ib_dbg(dev, "failed umem get, size %d\n", buf_size);
+ err = PTR_ERR(srq->umem);
+ return err;
+ }
+
+ mlx5_ib_cont_pages(srq->umem, ucmd.buf_addr, &npages,
+ &page_shift, &ncont, NULL);
+ err = mlx5_ib_get_buf_offset(ucmd.buf_addr, page_shift,
+ &offset);
+ if (err) {
+ mlx5_ib_warn(dev, "bad offset\n");
+ goto err_umem;
+ }
+
+ *inlen = sizeof(**in) + sizeof(*(*in)->pas) * ncont;
+ *in = mlx5_vzalloc(*inlen);
+ if (!(*in)) {
+ err = -ENOMEM;
+ goto err_umem;
+ }
+
+ mlx5_ib_populate_pas(dev, srq->umem, page_shift, (*in)->pas, 0);
+
+ err = mlx5_ib_db_map_user(to_mucontext(pd->uobject->context),
+ ucmd.db_addr, &srq->db);
+ if (err) {
+ mlx5_ib_dbg(dev, "map doorbell failed\n");
+ goto err_in;
+ }
+
+ (*in)->ctx.log_pg_sz = page_shift - PAGE_SHIFT;
+ (*in)->ctx.pgoff_cqn = cpu_to_be32(offset << 26);
+
+ return 0;
+
+err_in:
+ mlx5_vfree(*in);
+
+err_umem:
+ ib_umem_release(srq->umem);
+
+ return err;
+}
+
+static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq,
+ struct mlx5_create_srq_mbox_in **in, int buf_size,
+ int *inlen)
+{
+ int err;
+ int i;
+ struct mlx5_wqe_srq_next_seg *next;
+ int page_shift;
+ int npages;
+
+ err = mlx5_db_alloc(&dev->mdev, &srq->db);
+ if (err) {
+ mlx5_ib_warn(dev, "alloc dbell rec failed\n");
+ return err;
+ }
+
+ *srq->db.db = 0;
+
+ if (mlx5_buf_alloc(&dev->mdev, buf_size, PAGE_SIZE * 2, &srq->buf)) {
+ mlx5_ib_dbg(dev, "buf alloc failed\n");
+ err = -ENOMEM;
+ goto err_db;
+ }
+ page_shift = srq->buf.page_shift;
+
+ srq->head = 0;
+ srq->tail = srq->msrq.max - 1;
+ srq->wqe_ctr = 0;
+
+ for (i = 0; i < srq->msrq.max; i++) {
+ next = get_wqe(srq, i);
+ next->next_wqe_index =
+ cpu_to_be16((i + 1) & (srq->msrq.max - 1));
+ }
+
+ npages = DIV_ROUND_UP(srq->buf.npages, 1 << (page_shift - PAGE_SHIFT));
+ mlx5_ib_dbg(dev, "buf_size %d, page_shift %d, npages %d, calc npages %d\n",
+ buf_size, page_shift, srq->buf.npages, npages);
+ *inlen = sizeof(**in) + sizeof(*(*in)->pas) * npages;
+ *in = mlx5_vzalloc(*inlen);
+ if (!*in) {
+ err = -ENOMEM;
+ goto err_buf;
+ }
+ mlx5_fill_page_array(&srq->buf, (*in)->pas);
+
+ srq->wrid = kmalloc(srq->msrq.max * sizeof(u64), GFP_KERNEL);
+ if (!srq->wrid) {
+ mlx5_ib_dbg(dev, "kmalloc failed %lu\n",
+ (unsigned long)(srq->msrq.max * sizeof(u64)));
+ err = -ENOMEM;
+ goto err_in;
+ }
+ srq->wq_sig = !!srq_signature;
+
+ (*in)->ctx.log_pg_sz = page_shift - PAGE_SHIFT;
+
+ return 0;
+
+err_in:
+ mlx5_vfree(*in);
+
+err_buf:
+ mlx5_buf_free(&dev->mdev, &srq->buf);
+
+err_db:
+ mlx5_db_free(&dev->mdev, &srq->db);
+ return err;
+}
+
+static void destroy_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq)
+{
+ mlx5_ib_db_unmap_user(to_mucontext(pd->uobject->context), &srq->db);
+ ib_umem_release(srq->umem);
+}
+
+
+static void destroy_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq)
+{
+ kfree(srq->wrid);
+ mlx5_buf_free(&dev->mdev, &srq->buf);
+ mlx5_db_free(&dev->mdev, &srq->db);
+}
+
+struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
+ struct ib_srq_init_attr *init_attr,
+ struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev = to_mdev(pd->device);
+ struct mlx5_ib_srq *srq;
+ int desc_size;
+ int buf_size;
+ int err;
+ struct mlx5_create_srq_mbox_in *uninitialized_var(in);
+ int uninitialized_var(inlen);
+ int is_xrc;
+ u32 flgs, xrcdn;
+
+ /* Sanity check SRQ size before proceeding */
+ if (init_attr->attr.max_wr >= dev->mdev.caps.max_srq_wqes) {
+ mlx5_ib_dbg(dev, "max_wr %d, cap %d\n",
+ init_attr->attr.max_wr,
+ dev->mdev.caps.max_srq_wqes);
+ return ERR_PTR(-EINVAL);
+ }
+
+ srq = kmalloc(sizeof(*srq), GFP_KERNEL);
+ if (!srq)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&srq->mutex);
+ spin_lock_init(&srq->lock);
+ srq->msrq.max = roundup_pow_of_two(init_attr->attr.max_wr + 1);
+ srq->msrq.max_gs = init_attr->attr.max_sge;
+
+ desc_size = sizeof(struct mlx5_wqe_srq_next_seg) +
+ srq->msrq.max_gs * sizeof(struct mlx5_wqe_data_seg);
+ desc_size = roundup_pow_of_two(desc_size);
+ desc_size = max_t(int, 32, desc_size);
+ srq->msrq.max_avail_gather = (desc_size - sizeof(struct mlx5_wqe_srq_next_seg)) /
+ sizeof(struct mlx5_wqe_data_seg);
+ srq->msrq.wqe_shift = ilog2(desc_size);
+ buf_size = srq->msrq.max * desc_size;
+ mlx5_ib_dbg(dev, "desc_size 0x%x, req wr 0x%x, srq size 0x%x, max_gs 0x%x, max_avail_gather 0x%x\n",
+ desc_size, init_attr->attr.max_wr, srq->msrq.max, srq->msrq.max_gs,
+ srq->msrq.max_avail_gather);
+
+ if (pd->uobject)
+ err = create_srq_user(pd, srq, &in, udata, buf_size, &inlen);
+ else
+ err = create_srq_kernel(dev, srq, &in, buf_size, &inlen);
+
+ if (err) {
+ mlx5_ib_warn(dev, "create srq %s failed, err %d\n",
+ pd->uobject ? "user" : "kernel", err);
+ goto err_srq;
+ }
+
+ is_xrc = (init_attr->srq_type == IB_SRQT_XRC);
+ in->ctx.state_log_sz = ilog2(srq->msrq.max);
+ flgs = ((srq->msrq.wqe_shift - 4) | (is_xrc << 5) | (srq->wq_sig << 7)) << 24;
+ xrcdn = 0;
+ if (is_xrc) {
+ xrcdn = to_mxrcd(init_attr->ext.xrc.xrcd)->xrcdn;
+ in->ctx.pgoff_cqn |= cpu_to_be32(to_mcq(init_attr->ext.xrc.cq)->mcq.cqn);
+ } else if (init_attr->srq_type == IB_SRQT_BASIC) {
+ xrcdn = to_mxrcd(dev->devr.x0)->xrcdn;
+ in->ctx.pgoff_cqn |= cpu_to_be32(to_mcq(dev->devr.c0)->mcq.cqn);
+ }
+
+ in->ctx.flags_xrcd = cpu_to_be32((flgs & 0xFF000000) | (xrcdn & 0xFFFFFF));
+
+ in->ctx.pd = cpu_to_be32(to_mpd(pd)->pdn);
+ in->ctx.db_record = cpu_to_be64(srq->db.dma);
+ err = mlx5_core_create_srq(&dev->mdev, &srq->msrq, in, inlen);
+ mlx5_vfree(in);
+ if (err) {
+ mlx5_ib_dbg(dev, "create SRQ failed, err %d\n", err);
+ goto err_srq;
+ }
+
+ mlx5_ib_dbg(dev, "create SRQ with srqn 0x%x\n", srq->msrq.srqn);
+
+ srq->msrq.event = mlx5_ib_srq_event;
+ srq->ibsrq.ext.xrc.srq_num = srq->msrq.srqn;
+
+ if (pd->uobject)
+ if (ib_copy_to_udata(udata, &srq->msrq.srqn, sizeof(__u32))) {
+ mlx5_ib_dbg(dev, "copy to user failed\n");
+ err = -EFAULT;
+ goto err_core;
+ }
+
+ init_attr->attr.max_wr = srq->msrq.max - 1;
+
+ return &srq->ibsrq;
+
+err_core:
+ mlx5_core_destroy_srq(&dev->mdev, &srq->msrq);
+ if (pd->uobject)
+ destroy_srq_user(pd, srq);
+ else
+ destroy_srq_kernel(dev, srq);
+
+err_srq:
+ kfree(srq);
+
+ return ERR_PTR(err);
+}
+
+int mlx5_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
+ enum ib_srq_attr_mask attr_mask, struct ib_udata *udata)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
+ struct mlx5_ib_srq *srq = to_msrq(ibsrq);
+ int ret;
+
+ /* We don't support resizing SRQs yet */
+ if (attr_mask & IB_SRQ_MAX_WR)
+ return -EINVAL;
+
+ if (attr_mask & IB_SRQ_LIMIT) {
+ if (attr->srq_limit >= srq->msrq.max)
+ return -EINVAL;
+
+ mutex_lock(&srq->mutex);
+ ret = mlx5_core_arm_srq(&dev->mdev, &srq->msrq, attr->srq_limit, 1);
+ mutex_unlock(&srq->mutex);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int mlx5_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
+{
+ struct mlx5_ib_dev *dev = to_mdev(ibsrq->device);
+ struct mlx5_ib_srq *srq = to_msrq(ibsrq);
+ int ret;
+ struct mlx5_query_srq_mbox_out *out;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ ret = mlx5_core_query_srq(&dev->mdev, &srq->msrq, out);
+ if (ret)
+ goto out_box;
+
+ srq_attr->srq_limit = be16_to_cpu(out->ctx.lwm);
+ srq_attr->max_wr = srq->msrq.max - 1;
+ srq_attr->max_sge = srq->msrq.max_gs;
+
+out_box:
+ kfree(out);
+ return ret;
+}
+
+int mlx5_ib_destroy_srq(struct ib_srq *srq)
+{
+ struct mlx5_ib_dev *dev = to_mdev(srq->device);
+ struct mlx5_ib_srq *msrq = to_msrq(srq);
+
+ mlx5_core_destroy_srq(&dev->mdev, &msrq->msrq);
+
+ if (srq->uobject) {
+ mlx5_ib_db_unmap_user(to_mucontext(srq->uobject->context), &msrq->db);
+ ib_umem_release(msrq->umem);
+ } else {
+ kfree(msrq->wrid);
+ mlx5_buf_free(&dev->mdev, &msrq->buf);
+ mlx5_db_free(&dev->mdev, &msrq->db);
+ }
+
+ kfree(srq);
+ return 0;
+}
+
+void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index)
+{
+ struct mlx5_wqe_srq_next_seg *next;
+
+ /* always called with interrupts disabled. */
+ spin_lock(&srq->lock);
+
+ next = get_wqe(srq, srq->tail);
+ next->next_wqe_index = cpu_to_be16(wqe_index);
+ srq->tail = wqe_index;
+
+ spin_unlock(&srq->lock);
+}
+
+int mlx5_ib_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
+ struct ib_recv_wr **bad_wr)
+{
+ struct mlx5_ib_srq *srq = to_msrq(ibsrq);
+ struct mlx5_wqe_srq_next_seg *next;
+ struct mlx5_wqe_data_seg *scat;
+ unsigned long flags;
+ int err = 0;
+ int nreq;
+ int i;
+
+ spin_lock_irqsave(&srq->lock, flags);
+
+ for (nreq = 0; wr; nreq++, wr = wr->next) {
+ if (unlikely(wr->num_sge > srq->msrq.max_gs)) {
+ err = -EINVAL;
+ *bad_wr = wr;
+ break;
+ }
+
+ if (unlikely(srq->head == srq->tail)) {
+ err = -ENOMEM;
+ *bad_wr = wr;
+ break;
+ }
+
+ srq->wrid[srq->head] = wr->wr_id;
+
+ next = get_wqe(srq, srq->head);
+ srq->head = be16_to_cpu(next->next_wqe_index);
+ scat = (struct mlx5_wqe_data_seg *)(next + 1);
+
+ for (i = 0; i < wr->num_sge; i++) {
+ scat[i].byte_count = cpu_to_be32(wr->sg_list[i].length);
+ scat[i].lkey = cpu_to_be32(wr->sg_list[i].lkey);
+ scat[i].addr = cpu_to_be64(wr->sg_list[i].addr);
+ }
+
+ if (i < srq->msrq.max_avail_gather) {
+ scat[i].byte_count = 0;
+ scat[i].lkey = cpu_to_be32(MLX5_INVALID_LKEY);
+ scat[i].addr = 0;
+ }
+ }
+
+ if (likely(nreq)) {
+ srq->wqe_ctr += nreq;
+
+ /* Make sure that descriptors are written before
+ * doorbell record.
+ */
+ wmb();
+
+ *srq->db.db = cpu_to_be32(srq->wqe_ctr);
+ }
+
+ spin_unlock_irqrestore(&srq->lock, flags);
+
+ return err;
+}
diff --git a/drivers/infiniband/hw/mlx5/user.h b/drivers/infiniband/hw/mlx5/user.h
new file mode 100644
index 0000000..a886de3
--- /dev/null
+++ b/drivers/infiniband/hw/mlx5/user.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_IB_USER_H
+#define MLX5_IB_USER_H
+
+#include <linux/types.h>
+
+enum {
+ MLX5_QP_FLAG_SIGNATURE = 1 << 0,
+ MLX5_QP_FLAG_SCATTER_CQE = 1 << 1,
+};
+
+enum {
+ MLX5_SRQ_FLAG_SIGNATURE = 1 << 0,
+};
+
+
+/* Increment this value if any changes that break userspace ABI
+ * compatibility are made.
+ */
+#define MLX5_IB_UVERBS_ABI_VERSION 1
+
+/* Make sure that all structs defined in this file remain laid out so
+ * that they pack the same way on 32-bit and 64-bit architectures (to
+ * avoid incompatibility between 32-bit userspace and 64-bit kernels).
+ * In particular do not use pointer types -- pass pointers in __u64
+ * instead.
+ */
+
+struct mlx5_ib_alloc_ucontext_req {
+ __u32 total_num_uuars;
+ __u32 num_low_latency_uuars;
+};
+
+struct mlx5_ib_alloc_ucontext_resp {
+ __u32 qp_tab_size;
+ __u32 bf_reg_size;
+ __u32 tot_uuars;
+ __u32 cache_line_size;
+ __u16 max_sq_desc_sz;
+ __u16 max_rq_desc_sz;
+ __u32 max_send_wqebb;
+ __u32 max_recv_wr;
+ __u32 max_srq_recv_wr;
+ __u16 num_ports;
+ __u16 reserved;
+};
+
+struct mlx5_ib_alloc_pd_resp {
+ __u32 pdn;
+};
+
+struct mlx5_ib_create_cq {
+ __u64 buf_addr;
+ __u64 db_addr;
+ __u32 cqe_size;
+};
+
+struct mlx5_ib_create_cq_resp {
+ __u32 cqn;
+ __u32 reserved;
+};
+
+struct mlx5_ib_resize_cq {
+ __u64 buf_addr;
+};
+
+struct mlx5_ib_create_srq {
+ __u64 buf_addr;
+ __u64 db_addr;
+ __u32 flags;
+};
+
+struct mlx5_ib_create_srq_resp {
+ __u32 srqn;
+ __u32 reserved;
+};
+
+struct mlx5_ib_create_qp {
+ __u64 buf_addr;
+ __u64 db_addr;
+ __u32 sq_wqe_count;
+ __u32 rq_wqe_count;
+ __u32 rq_wqe_shift;
+ __u32 flags;
+};
+
+struct mlx5_ib_create_qp_resp {
+ __u32 uuar_index;
+};
+#endif /* MLX5_IB_USER_H */
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma.h b/drivers/infiniband/hw/ocrdma/ocrdma.h
index 48970af..d540180 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma.h
@@ -42,8 +42,6 @@
#define OCRDMA_ROCE_DEV_VERSION "1.0.0"
#define OCRDMA_NODE_DESC "Emulex OneConnect RoCE HCA"
-#define ocrdma_err(format, arg...) printk(KERN_ERR format, ##arg)
-
#define OCRDMA_MAX_AH 512
#define OCRDMA_UVERBS(CMD_NAME) (1ull << IB_USER_VERBS_CMD_##CMD_NAME)
@@ -97,7 +95,6 @@
u16 id; /* qid, where to ring the doorbell. */
u16 head, tail;
bool created;
- atomic_t used; /* Number of valid elements in the queue */
};
struct ocrdma_eq {
@@ -198,7 +195,6 @@
struct ocrdma_ucontext *ucontext;
dma_addr_t pa;
u32 len;
- atomic_t use_cnt;
/* head of all qp's sq and rq for which cqes need to be flushed
* by the software.
@@ -210,7 +206,6 @@
struct ib_pd ibpd;
struct ocrdma_dev *dev;
struct ocrdma_ucontext *uctx;
- atomic_t use_cnt;
u32 id;
int num_dpp_qp;
u32 dpp_page;
@@ -241,16 +236,16 @@
struct ib_srq ibsrq;
struct ocrdma_dev *dev;
u8 __iomem *db;
- /* provide synchronization to multiple context(s) posting rqe */
- spinlock_t q_lock ____cacheline_aligned;
-
struct ocrdma_qp_hwq_info rq;
- struct ocrdma_pd *pd;
- atomic_t use_cnt;
- u32 id;
u64 *rqe_wr_id_tbl;
u32 *idx_bit_fields;
u32 bit_fields_len;
+
+ /* provide synchronization to multiple context(s) posting rqe */
+ spinlock_t q_lock ____cacheline_aligned;
+
+ struct ocrdma_pd *pd;
+ u32 id;
};
struct ocrdma_qp {
@@ -258,8 +253,6 @@
struct ocrdma_dev *dev;
u8 __iomem *sq_db;
- /* provide synchronization to multiple context(s) posting wqe, rqe */
- spinlock_t q_lock ____cacheline_aligned;
struct ocrdma_qp_hwq_info sq;
struct {
uint64_t wrid;
@@ -269,6 +262,9 @@
uint8_t rsvd[3];
} *wqe_wr_id_tbl;
u32 max_inline_data;
+
+ /* provide synchronization to multiple context(s) posting wqe, rqe */
+ spinlock_t q_lock ____cacheline_aligned;
struct ocrdma_cq *sq_cq;
/* list maintained per CQ to flush SQ errors */
struct list_head sq_entry;
@@ -296,10 +292,6 @@
u8 *ird_q_va;
};
-#define OCRDMA_GET_NUM_POSTED_SHIFT_VAL(qp) \
- (((qp->dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) && \
- (qp->id < 64)) ? 24 : 16)
-
struct ocrdma_hw_mr {
struct ocrdma_dev *dev;
u32 lkey;
@@ -390,4 +382,43 @@
return container_of(ibsrq, struct ocrdma_srq, ibsrq);
}
+
+static inline int ocrdma_get_num_posted_shift(struct ocrdma_qp *qp)
+{
+ return ((qp->dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY &&
+ qp->id < 64) ? 24 : 16);
+}
+
+static inline int is_cqe_valid(struct ocrdma_cq *cq, struct ocrdma_cqe *cqe)
+{
+ int cqe_valid;
+ cqe_valid = le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_VALID;
+ return ((cqe_valid == cq->phase) ? 1 : 0);
+}
+
+static inline int is_cqe_for_sq(struct ocrdma_cqe *cqe)
+{
+ return (le32_to_cpu(cqe->flags_status_srcqpn) &
+ OCRDMA_CQE_QTYPE) ? 0 : 1;
+}
+
+static inline int is_cqe_invalidated(struct ocrdma_cqe *cqe)
+{
+ return (le32_to_cpu(cqe->flags_status_srcqpn) &
+ OCRDMA_CQE_INVALIDATE) ? 1 : 0;
+}
+
+static inline int is_cqe_imm(struct ocrdma_cqe *cqe)
+{
+ return (le32_to_cpu(cqe->flags_status_srcqpn) &
+ OCRDMA_CQE_IMM) ? 1 : 0;
+}
+
+static inline int is_cqe_wr_imm(struct ocrdma_cqe *cqe)
+{
+ return (le32_to_cpu(cqe->flags_status_srcqpn) &
+ OCRDMA_CQE_WRITE_IMM) ? 1 : 0;
+}
+
+
#endif
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
index 71942af..0965278 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
@@ -128,7 +128,6 @@
static inline void ocrdma_mq_inc_head(struct ocrdma_dev *dev)
{
dev->mq.sq.head = (dev->mq.sq.head + 1) & (OCRDMA_MQ_LEN - 1);
- atomic_inc(&dev->mq.sq.used);
}
static inline void *ocrdma_get_mqe_rsp(struct ocrdma_dev *dev)
@@ -564,32 +563,19 @@
memset(cmd, 0, sizeof(*cmd));
num_pages = PAGES_4K_SPANNED(mq->va, mq->size);
- if (dev->nic_info.dev_family == OCRDMA_GEN2_FAMILY) {
- ocrdma_init_mch(&cmd->req, OCRDMA_CMD_CREATE_MQ,
- OCRDMA_SUBSYS_COMMON, sizeof(*cmd));
- cmd->v0.pages = num_pages;
- cmd->v0.async_cqid_valid = OCRDMA_CREATE_MQ_ASYNC_CQ_VALID;
- cmd->v0.async_cqid_valid = (cq->id << 1);
- cmd->v0.cqid_ringsize |= (ocrdma_encoded_q_len(mq->len) <<
- OCRDMA_CREATE_MQ_RING_SIZE_SHIFT);
- cmd->v0.cqid_ringsize |=
- (cq->id << OCRDMA_CREATE_MQ_V0_CQ_ID_SHIFT);
- cmd->v0.valid = OCRDMA_CREATE_MQ_VALID;
- pa = &cmd->v0.pa[0];
- } else {
- ocrdma_init_mch(&cmd->req, OCRDMA_CMD_CREATE_MQ_EXT,
- OCRDMA_SUBSYS_COMMON, sizeof(*cmd));
- cmd->req.rsvd_version = 1;
- cmd->v1.cqid_pages = num_pages;
- cmd->v1.cqid_pages |= (cq->id << OCRDMA_CREATE_MQ_CQ_ID_SHIFT);
- cmd->v1.async_cqid_valid = OCRDMA_CREATE_MQ_ASYNC_CQ_VALID;
- cmd->v1.async_event_bitmap = Bit(20);
- cmd->v1.async_cqid_ringsize = cq->id;
- cmd->v1.async_cqid_ringsize |= (ocrdma_encoded_q_len(mq->len) <<
- OCRDMA_CREATE_MQ_RING_SIZE_SHIFT);
- cmd->v1.valid = OCRDMA_CREATE_MQ_VALID;
- pa = &cmd->v1.pa[0];
- }
+ ocrdma_init_mch(&cmd->req, OCRDMA_CMD_CREATE_MQ_EXT,
+ OCRDMA_SUBSYS_COMMON, sizeof(*cmd));
+ cmd->req.rsvd_version = 1;
+ cmd->cqid_pages = num_pages;
+ cmd->cqid_pages |= (cq->id << OCRDMA_CREATE_MQ_CQ_ID_SHIFT);
+ cmd->async_cqid_valid = OCRDMA_CREATE_MQ_ASYNC_CQ_VALID;
+ cmd->async_event_bitmap = Bit(20);
+ cmd->async_cqid_ringsize = cq->id;
+ cmd->async_cqid_ringsize |= (ocrdma_encoded_q_len(mq->len) <<
+ OCRDMA_CREATE_MQ_RING_SIZE_SHIFT);
+ cmd->valid = OCRDMA_CREATE_MQ_VALID;
+ pa = &cmd->pa[0];
+
ocrdma_build_q_pages(pa, num_pages, mq->dma, PAGE_SIZE_4K);
status = be_roce_mcc_cmd(dev->nic_info.netdev,
cmd, sizeof(*cmd), NULL, NULL);
@@ -745,7 +731,7 @@
qp_event = 0;
srq_event = 0;
dev_event = 0;
- ocrdma_err("%s() unknown type=0x%x\n", __func__, type);
+ pr_err("%s() unknown type=0x%x\n", __func__, type);
break;
}
@@ -775,8 +761,8 @@
if (evt_code == OCRDMA_ASYNC_EVE_CODE)
ocrdma_dispatch_ibevent(dev, cqe);
else
- ocrdma_err("%s(%d) invalid evt code=0x%x\n",
- __func__, dev->id, evt_code);
+ pr_err("%s(%d) invalid evt code=0x%x\n", __func__,
+ dev->id, evt_code);
}
static void ocrdma_process_mcqe(struct ocrdma_dev *dev, struct ocrdma_mcqe *cqe)
@@ -790,8 +776,8 @@
dev->mqe_ctx.cmd_done = true;
wake_up(&dev->mqe_ctx.cmd_wait);
} else
- ocrdma_err("%s() cqe for invalid tag0x%x.expected=0x%x\n",
- __func__, cqe->tag_lo, dev->mqe_ctx.tag);
+ pr_err("%s() cqe for invalid tag0x%x.expected=0x%x\n",
+ __func__, cqe->tag_lo, dev->mqe_ctx.tag);
}
static int ocrdma_mq_cq_handler(struct ocrdma_dev *dev, u16 cq_id)
@@ -810,7 +796,7 @@
else if (cqe->valid_ae_cmpl_cons & OCRDMA_MCQE_CMPL_MASK)
ocrdma_process_mcqe(dev, cqe);
else
- ocrdma_err("%s() cqe->compl is not set.\n", __func__);
+ pr_err("%s() cqe->compl is not set.\n", __func__);
memset(cqe, 0, sizeof(struct ocrdma_mcqe));
ocrdma_mcq_inc_tail(dev);
}
@@ -869,7 +855,7 @@
cq = dev->cq_tbl[cq_idx];
if (cq == NULL) {
- ocrdma_err("%s%d invalid id=0x%x\n", __func__, dev->id, cq_idx);
+ pr_err("%s%d invalid id=0x%x\n", __func__, dev->id, cq_idx);
return;
}
spin_lock_irqsave(&cq->cq_lock, flags);
@@ -971,7 +957,7 @@
rsp = ocrdma_get_mqe_rsp(dev);
ocrdma_copy_le32_to_cpu(mqe, rsp, (sizeof(*mqe)));
if (cqe_status || ext_status) {
- ocrdma_err
+ pr_err
("%s() opcode=0x%x, cqe_status=0x%x, ext_status=0x%x\n",
__func__,
(rsp->u.rsp.subsys_op & OCRDMA_MBX_RSP_OPCODE_MASK) >>
@@ -1353,8 +1339,8 @@
if (dpp_cq)
return -EINVAL;
if (entries > dev->attr.max_cqe) {
- ocrdma_err("%s(%d) max_cqe=0x%x, requester_cqe=0x%x\n",
- __func__, dev->id, dev->attr.max_cqe, entries);
+ pr_err("%s(%d) max_cqe=0x%x, requester_cqe=0x%x\n",
+ __func__, dev->id, dev->attr.max_cqe, entries);
return -EINVAL;
}
if (dpp_cq && (dev->nic_info.dev_family != OCRDMA_GEN2_FAMILY))
@@ -1621,7 +1607,7 @@
status = ocrdma_mbx_reg_mr(dev, hwmr, pdid,
cur_pbl_cnt, hwmr->pbe_size, last);
if (status) {
- ocrdma_err("%s() status=%d\n", __func__, status);
+ pr_err("%s() status=%d\n", __func__, status);
return status;
}
/* if there is no more pbls to register then exit. */
@@ -1644,7 +1630,7 @@
break;
}
if (status)
- ocrdma_err("%s() err. status=%d\n", __func__, status);
+ pr_err("%s() err. status=%d\n", __func__, status);
return status;
}
@@ -1841,8 +1827,8 @@
status = ocrdma_build_q_conf(&max_wqe_allocated,
dev->attr.wqe_size, &hw_pages, &hw_page_size);
if (status) {
- ocrdma_err("%s() req. max_send_wr=0x%x\n", __func__,
- max_wqe_allocated);
+ pr_err("%s() req. max_send_wr=0x%x\n", __func__,
+ max_wqe_allocated);
return -EINVAL;
}
qp->sq.max_cnt = max_wqe_allocated;
@@ -1891,8 +1877,8 @@
status = ocrdma_build_q_conf(&max_rqe_allocated, dev->attr.rqe_size,
&hw_pages, &hw_page_size);
if (status) {
- ocrdma_err("%s() req. max_recv_wr=0x%x\n", __func__,
- attrs->cap.max_recv_wr + 1);
+ pr_err("%s() req. max_recv_wr=0x%x\n", __func__,
+ attrs->cap.max_recv_wr + 1);
return status;
}
qp->rq.max_cnt = max_rqe_allocated;
@@ -1900,7 +1886,7 @@
qp->rq.va = dma_alloc_coherent(&pdev->dev, len, &pa, GFP_KERNEL);
if (!qp->rq.va)
- return status;
+ return -ENOMEM;
memset(qp->rq.va, 0, len);
qp->rq.pa = pa;
qp->rq.len = len;
@@ -2087,10 +2073,10 @@
if (qp->rq.va)
dma_free_coherent(&pdev->dev, qp->rq.len, qp->rq.va, qp->rq.pa);
rq_err:
- ocrdma_err("%s(%d) rq_err\n", __func__, dev->id);
+ pr_err("%s(%d) rq_err\n", __func__, dev->id);
dma_free_coherent(&pdev->dev, qp->sq.len, qp->sq.va, qp->sq.pa);
sq_err:
- ocrdma_err("%s(%d) sq_err\n", __func__, dev->id);
+ pr_err("%s(%d) sq_err\n", __func__, dev->id);
kfree(cmd);
return status;
}
@@ -2127,7 +2113,7 @@
else if (rdma_link_local_addr(&in6))
rdma_get_ll_mac(&in6, mac_addr);
else {
- ocrdma_err("%s() fail to resolve mac_addr.\n", __func__);
+ pr_err("%s() fail to resolve mac_addr.\n", __func__);
return -EINVAL;
}
return 0;
@@ -2362,8 +2348,8 @@
dev->attr.rqe_size,
&hw_pages, &hw_page_size);
if (status) {
- ocrdma_err("%s() req. max_wr=0x%x\n", __func__,
- srq_attr->attr.max_wr);
+ pr_err("%s() req. max_wr=0x%x\n", __func__,
+ srq_attr->attr.max_wr);
status = -EINVAL;
goto ret;
}
@@ -2614,7 +2600,7 @@
ocrdma_destroy_qp_eqs(dev);
qpeq_err:
ocrdma_destroy_eq(dev, &dev->meq);
- ocrdma_err("%s() status=%d\n", __func__, status);
+ pr_err("%s() status=%d\n", __func__, status);
return status;
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 48928c8..ded416f1 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -378,7 +378,7 @@
spin_lock_init(&dev->flush_q_lock);
return 0;
alloc_err:
- ocrdma_err("%s(%d) error.\n", __func__, dev->id);
+ pr_err("%s(%d) error.\n", __func__, dev->id);
return -ENOMEM;
}
@@ -396,7 +396,7 @@
dev = (struct ocrdma_dev *)ib_alloc_device(sizeof(struct ocrdma_dev));
if (!dev) {
- ocrdma_err("Unable to allocate ib device\n");
+ pr_err("Unable to allocate ib device\n");
return NULL;
}
dev->mbx_cmd = kzalloc(sizeof(struct ocrdma_mqe_emb_cmd), GFP_KERNEL);
@@ -437,7 +437,7 @@
idr_err:
kfree(dev->mbx_cmd);
ib_dealloc_device(&dev->ibdev);
- ocrdma_err("%s() leaving. ret=%d\n", __func__, status);
+ pr_err("%s() leaving. ret=%d\n", __func__, status);
return NULL;
}
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
index c75cbdf..36b062d 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
@@ -608,16 +608,8 @@
OCRDMA_CREATE_MQ_ASYNC_CQ_VALID = Bit(0)
};
-struct ocrdma_create_mq_v0 {
- u32 pages;
- u32 cqid_ringsize;
- u32 valid;
- u32 async_cqid_valid;
- u32 rsvd;
- struct ocrdma_pa pa[8];
-} __packed;
-
-struct ocrdma_create_mq_v1 {
+struct ocrdma_create_mq_req {
+ struct ocrdma_mbx_hdr req;
u32 cqid_pages;
u32 async_event_bitmap;
u32 async_cqid_ringsize;
@@ -627,14 +619,6 @@
struct ocrdma_pa pa[8];
} __packed;
-struct ocrdma_create_mq_req {
- struct ocrdma_mbx_hdr req;
- union {
- struct ocrdma_create_mq_v0 v0;
- struct ocrdma_create_mq_v1 v1;
- };
-} __packed;
-
struct ocrdma_create_mq_rsp {
struct ocrdma_mbx_rsp rsp;
u32 id;
@@ -1550,21 +1534,6 @@
u32 flags_status_srcqpn; /* w3 */
} __packed;
-#define is_cqe_valid(cq, cqe) \
- (((le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_VALID)\
- == cq->phase) ? 1 : 0)
-#define is_cqe_for_sq(cqe) \
- ((le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_QTYPE) ? 0 : 1)
-#define is_cqe_for_rq(cqe) \
- ((le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_QTYPE) ? 1 : 0)
-#define is_cqe_invalidated(cqe) \
- ((le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_INVALIDATE) ? \
- 1 : 0)
-#define is_cqe_imm(cqe) \
- ((le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_IMM) ? 1 : 0)
-#define is_cqe_wr_imm(cqe) \
- ((le32_to_cpu(cqe->flags_status_srcqpn) & OCRDMA_CQE_WRITE_IMM) ? 1 : 0)
-
struct ocrdma_sge {
u32 addr_hi;
u32 addr_lo;
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index b29a424..dcfbab1 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -114,8 +114,8 @@
dev = get_ocrdma_dev(ibdev);
if (port > 1) {
- ocrdma_err("%s(%d) invalid_port=0x%x\n", __func__,
- dev->id, port);
+ pr_err("%s(%d) invalid_port=0x%x\n", __func__,
+ dev->id, port);
return -EINVAL;
}
netdev = dev->nic_info.netdev;
@@ -155,8 +155,7 @@
dev = get_ocrdma_dev(ibdev);
if (port > 1) {
- ocrdma_err("%s(%d) invalid_port=0x%x\n", __func__,
- dev->id, port);
+ pr_err("%s(%d) invalid_port=0x%x\n", __func__, dev->id, port);
return -EINVAL;
}
return 0;
@@ -398,7 +397,6 @@
kfree(pd);
return ERR_PTR(status);
}
- atomic_set(&pd->use_cnt, 0);
if (udata && context) {
status = ocrdma_copy_pd_uresp(pd, context, udata);
@@ -419,12 +417,6 @@
int status;
u64 usr_db;
- if (atomic_read(&pd->use_cnt)) {
- ocrdma_err("%s(%d) pd=0x%x is in use.\n",
- __func__, dev->id, pd->id);
- status = -EFAULT;
- goto dealloc_err;
- }
status = ocrdma_mbx_dealloc_pd(dev, pd);
if (pd->uctx) {
u64 dpp_db = dev->nic_info.dpp_unmapped_addr +
@@ -436,7 +428,6 @@
ocrdma_del_mmap(pd->uctx, usr_db, dev->nic_info.db_page_size);
}
kfree(pd);
-dealloc_err:
return status;
}
@@ -450,8 +441,8 @@
struct ocrdma_dev *dev = pd->dev;
if (acc & IB_ACCESS_REMOTE_WRITE && !(acc & IB_ACCESS_LOCAL_WRITE)) {
- ocrdma_err("%s(%d) leaving err, invalid access rights\n",
- __func__, dev->id);
+ pr_err("%s(%d) leaving err, invalid access rights\n",
+ __func__, dev->id);
return ERR_PTR(-EINVAL);
}
@@ -474,7 +465,6 @@
return ERR_PTR(-ENOMEM);
}
mr->pd = pd;
- atomic_inc(&pd->use_cnt);
mr->ibmr.lkey = mr->hwmr.lkey;
if (mr->hwmr.remote_wr || mr->hwmr.remote_rd)
mr->ibmr.rkey = mr->hwmr.lkey;
@@ -664,7 +654,6 @@
if (status)
goto mbx_err;
mr->pd = pd;
- atomic_inc(&pd->use_cnt);
mr->ibmr.lkey = mr->hwmr.lkey;
if (mr->hwmr.remote_wr || mr->hwmr.remote_rd)
mr->ibmr.rkey = mr->hwmr.lkey;
@@ -689,7 +678,6 @@
if (mr->hwmr.fr_mr == 0)
ocrdma_free_mr_pbl_tbl(dev, &mr->hwmr);
- atomic_dec(&mr->pd->use_cnt);
/* it could be user registered memory. */
if (mr->umem)
ib_umem_release(mr->umem);
@@ -714,8 +702,8 @@
uresp.phase_change = cq->phase_change ? 1 : 0;
status = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
if (status) {
- ocrdma_err("%s(%d) copy error cqid=0x%x.\n",
- __func__, cq->dev->id, cq->id);
+ pr_err("%s(%d) copy error cqid=0x%x.\n",
+ __func__, cq->dev->id, cq->id);
goto err;
}
uctx = get_ocrdma_ucontext(ib_ctx);
@@ -752,7 +740,6 @@
spin_lock_init(&cq->cq_lock);
spin_lock_init(&cq->comp_handler_lock);
- atomic_set(&cq->use_cnt, 0);
INIT_LIST_HEAD(&cq->sq_head);
INIT_LIST_HEAD(&cq->rq_head);
cq->dev = dev;
@@ -799,9 +786,6 @@
struct ocrdma_cq *cq = get_ocrdma_cq(ibcq);
struct ocrdma_dev *dev = cq->dev;
- if (atomic_read(&cq->use_cnt))
- return -EINVAL;
-
status = ocrdma_mbx_destroy_cq(dev, cq);
if (cq->ucontext) {
@@ -837,57 +821,56 @@
if (attrs->qp_type != IB_QPT_GSI &&
attrs->qp_type != IB_QPT_RC &&
attrs->qp_type != IB_QPT_UD) {
- ocrdma_err("%s(%d) unsupported qp type=0x%x requested\n",
- __func__, dev->id, attrs->qp_type);
+ pr_err("%s(%d) unsupported qp type=0x%x requested\n",
+ __func__, dev->id, attrs->qp_type);
return -EINVAL;
}
if (attrs->cap.max_send_wr > dev->attr.max_wqe) {
- ocrdma_err("%s(%d) unsupported send_wr=0x%x requested\n",
- __func__, dev->id, attrs->cap.max_send_wr);
- ocrdma_err("%s(%d) supported send_wr=0x%x\n",
- __func__, dev->id, dev->attr.max_wqe);
+ pr_err("%s(%d) unsupported send_wr=0x%x requested\n",
+ __func__, dev->id, attrs->cap.max_send_wr);
+ pr_err("%s(%d) supported send_wr=0x%x\n",
+ __func__, dev->id, dev->attr.max_wqe);
return -EINVAL;
}
if (!attrs->srq && (attrs->cap.max_recv_wr > dev->attr.max_rqe)) {
- ocrdma_err("%s(%d) unsupported recv_wr=0x%x requested\n",
- __func__, dev->id, attrs->cap.max_recv_wr);
- ocrdma_err("%s(%d) supported recv_wr=0x%x\n",
- __func__, dev->id, dev->attr.max_rqe);
+ pr_err("%s(%d) unsupported recv_wr=0x%x requested\n",
+ __func__, dev->id, attrs->cap.max_recv_wr);
+ pr_err("%s(%d) supported recv_wr=0x%x\n",
+ __func__, dev->id, dev->attr.max_rqe);
return -EINVAL;
}
if (attrs->cap.max_inline_data > dev->attr.max_inline_data) {
- ocrdma_err("%s(%d) unsupported inline data size=0x%x"
- " requested\n", __func__, dev->id,
- attrs->cap.max_inline_data);
- ocrdma_err("%s(%d) supported inline data size=0x%x\n",
- __func__, dev->id, dev->attr.max_inline_data);
+ pr_err("%s(%d) unsupported inline data size=0x%x requested\n",
+ __func__, dev->id, attrs->cap.max_inline_data);
+ pr_err("%s(%d) supported inline data size=0x%x\n",
+ __func__, dev->id, dev->attr.max_inline_data);
return -EINVAL;
}
if (attrs->cap.max_send_sge > dev->attr.max_send_sge) {
- ocrdma_err("%s(%d) unsupported send_sge=0x%x requested\n",
- __func__, dev->id, attrs->cap.max_send_sge);
- ocrdma_err("%s(%d) supported send_sge=0x%x\n",
- __func__, dev->id, dev->attr.max_send_sge);
+ pr_err("%s(%d) unsupported send_sge=0x%x requested\n",
+ __func__, dev->id, attrs->cap.max_send_sge);
+ pr_err("%s(%d) supported send_sge=0x%x\n",
+ __func__, dev->id, dev->attr.max_send_sge);
return -EINVAL;
}
if (attrs->cap.max_recv_sge > dev->attr.max_recv_sge) {
- ocrdma_err("%s(%d) unsupported recv_sge=0x%x requested\n",
- __func__, dev->id, attrs->cap.max_recv_sge);
- ocrdma_err("%s(%d) supported recv_sge=0x%x\n",
- __func__, dev->id, dev->attr.max_recv_sge);
+ pr_err("%s(%d) unsupported recv_sge=0x%x requested\n",
+ __func__, dev->id, attrs->cap.max_recv_sge);
+ pr_err("%s(%d) supported recv_sge=0x%x\n",
+ __func__, dev->id, dev->attr.max_recv_sge);
return -EINVAL;
}
/* unprivileged user space cannot create special QP */
if (ibpd->uobject && attrs->qp_type == IB_QPT_GSI) {
- ocrdma_err
+ pr_err
("%s(%d) Userspace can't create special QPs of type=0x%x\n",
__func__, dev->id, attrs->qp_type);
return -EINVAL;
}
/* allow creating only one GSI type of QP */
if (attrs->qp_type == IB_QPT_GSI && dev->gsi_qp_created) {
- ocrdma_err("%s(%d) GSI special QPs already created.\n",
- __func__, dev->id);
+ pr_err("%s(%d) GSI special QPs already created.\n",
+ __func__, dev->id);
return -EINVAL;
}
/* verify consumer QPs are not trying to use GSI QP's CQ */
@@ -896,8 +879,8 @@
(dev->gsi_sqcq == get_ocrdma_cq(attrs->recv_cq)) ||
(dev->gsi_rqcq == get_ocrdma_cq(attrs->send_cq)) ||
(dev->gsi_rqcq == get_ocrdma_cq(attrs->recv_cq))) {
- ocrdma_err("%s(%d) Consumer QP cannot use GSI CQs.\n",
- __func__, dev->id);
+ pr_err("%s(%d) Consumer QP cannot use GSI CQs.\n",
+ __func__, dev->id);
return -EINVAL;
}
}
@@ -949,7 +932,7 @@
}
status = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
if (status) {
- ocrdma_err("%s(%d) user copy error.\n", __func__, dev->id);
+ pr_err("%s(%d) user copy error.\n", __func__, dev->id);
goto err;
}
status = ocrdma_add_mmap(pd->uctx, uresp.sq_page_addr[0],
@@ -1023,15 +1006,6 @@
qp->state = OCRDMA_QPS_RST;
}
-static void ocrdma_set_qp_use_cnt(struct ocrdma_qp *qp, struct ocrdma_pd *pd)
-{
- atomic_inc(&pd->use_cnt);
- atomic_inc(&qp->sq_cq->use_cnt);
- atomic_inc(&qp->rq_cq->use_cnt);
- if (qp->srq)
- atomic_inc(&qp->srq->use_cnt);
- qp->ibqp.qp_num = qp->id;
-}
static void ocrdma_store_gsi_qp_cq(struct ocrdma_dev *dev,
struct ib_qp_init_attr *attrs)
@@ -1099,7 +1073,7 @@
goto cpy_err;
}
ocrdma_store_gsi_qp_cq(dev, attrs);
- ocrdma_set_qp_use_cnt(qp, pd);
+ qp->ibqp.qp_num = qp->id;
mutex_unlock(&dev->dev_lock);
return &qp->ibqp;
@@ -1112,7 +1086,7 @@
kfree(qp->wqe_wr_id_tbl);
kfree(qp->rqe_wr_id_tbl);
kfree(qp);
- ocrdma_err("%s(%d) error=%d\n", __func__, dev->id, status);
+ pr_err("%s(%d) error=%d\n", __func__, dev->id, status);
gen_err:
return ERR_PTR(status);
}
@@ -1162,10 +1136,10 @@
spin_unlock_irqrestore(&qp->q_lock, flags);
if (!ib_modify_qp_is_ok(old_qps, new_qps, ibqp->qp_type, attr_mask)) {
- ocrdma_err("%s(%d) invalid attribute mask=0x%x specified for "
- "qpn=0x%x of type=0x%x old_qps=0x%x, new_qps=0x%x\n",
- __func__, dev->id, attr_mask, qp->id, ibqp->qp_type,
- old_qps, new_qps);
+ pr_err("%s(%d) invalid attribute mask=0x%x specified for\n"
+ "qpn=0x%x of type=0x%x old_qps=0x%x, new_qps=0x%x\n",
+ __func__, dev->id, attr_mask, qp->id, ibqp->qp_type,
+ old_qps, new_qps);
goto param_err;
}
@@ -1475,11 +1449,6 @@
ocrdma_del_flush_qp(qp);
- atomic_dec(&qp->pd->use_cnt);
- atomic_dec(&qp->sq_cq->use_cnt);
- atomic_dec(&qp->rq_cq->use_cnt);
- if (qp->srq)
- atomic_dec(&qp->srq->use_cnt);
kfree(qp->wqe_wr_id_tbl);
kfree(qp->rqe_wr_id_tbl);
kfree(qp);
@@ -1565,14 +1534,12 @@
goto arm_err;
}
- atomic_set(&srq->use_cnt, 0);
if (udata) {
status = ocrdma_copy_srq_uresp(srq, udata);
if (status)
goto arm_err;
}
- atomic_inc(&pd->use_cnt);
return &srq->ibsrq;
arm_err:
@@ -1618,18 +1585,12 @@
srq = get_ocrdma_srq(ibsrq);
dev = srq->dev;
- if (atomic_read(&srq->use_cnt)) {
- ocrdma_err("%s(%d) err, srq=0x%x in use\n",
- __func__, dev->id, srq->id);
- return -EAGAIN;
- }
status = ocrdma_mbx_destroy_srq(dev, srq);
if (srq->pd->uctx)
ocrdma_del_mmap(srq->pd->uctx, (u64) srq->rq.pa, srq->rq.len);
- atomic_dec(&srq->pd->use_cnt);
kfree(srq->idx_bit_fields);
kfree(srq->rqe_wr_id_tbl);
kfree(srq);
@@ -1677,9 +1638,9 @@
{
if (wr->send_flags & IB_SEND_INLINE) {
if (wr->sg_list[0].length > qp->max_inline_data) {
- ocrdma_err("%s() supported_len=0x%x,"
- " unspported len req=0x%x\n", __func__,
- qp->max_inline_data, wr->sg_list[0].length);
+ pr_err("%s() supported_len=0x%x,\n"
+ " unspported len req=0x%x\n", __func__,
+ qp->max_inline_data, wr->sg_list[0].length);
return -EINVAL;
}
memcpy(sge,
@@ -1773,12 +1734,14 @@
spin_lock_irqsave(&qp->q_lock, flags);
if (qp->state != OCRDMA_QPS_RTS && qp->state != OCRDMA_QPS_SQD) {
spin_unlock_irqrestore(&qp->q_lock, flags);
+ *bad_wr = wr;
return -EINVAL;
}
while (wr) {
if (ocrdma_hwq_free_cnt(&qp->sq) == 0 ||
wr->num_sge > qp->sq.max_sges) {
+ *bad_wr = wr;
status = -ENOMEM;
break;
}
@@ -1856,7 +1819,7 @@
static void ocrdma_ring_rq_db(struct ocrdma_qp *qp)
{
- u32 val = qp->rq.dbid | (1 << OCRDMA_GET_NUM_POSTED_SHIFT_VAL(qp));
+ u32 val = qp->rq.dbid | (1 << ocrdma_get_num_posted_shift(qp));
iowrite32(val, qp->rq_db);
}
@@ -2094,8 +2057,8 @@
break;
default:
ibwc->status = IB_WC_GENERAL_ERR;
- ocrdma_err("%s() invalid opcode received = 0x%x\n",
- __func__, hdr->cw & OCRDMA_WQE_OPCODE_MASK);
+ pr_err("%s() invalid opcode received = 0x%x\n",
+ __func__, hdr->cw & OCRDMA_WQE_OPCODE_MASK);
break;
};
}
diff --git a/drivers/infiniband/hw/qib/Kconfig b/drivers/infiniband/hw/qib/Kconfig
index 1e603a3..d03ca4c 100644
--- a/drivers/infiniband/hw/qib/Kconfig
+++ b/drivers/infiniband/hw/qib/Kconfig
@@ -5,3 +5,11 @@
This is a low-level driver for Intel PCIe QLE InfiniBand host
channel adapters. This driver does not support the Intel
HyperTransport card (model QHT7140).
+
+config INFINIBAND_QIB_DCA
+ bool "QIB DCA support"
+ depends on INFINIBAND_QIB && DCA && SMP && GENERIC_HARDIRQS && !(INFINIBAND_QIB=y && DCA=m)
+ default y
+ ---help---
+ Setting this enables DCA support on some Intel chip sets
+ with the iba7322 HCA.
diff --git a/drivers/infiniband/hw/qib/Makefile b/drivers/infiniband/hw/qib/Makefile
index f12d7bb..57f8103 100644
--- a/drivers/infiniband/hw/qib/Makefile
+++ b/drivers/infiniband/hw/qib/Makefile
@@ -13,3 +13,4 @@
ib_qib-$(CONFIG_X86_64) += qib_wc_x86_64.o
ib_qib-$(CONFIG_PPC64) += qib_wc_ppc64.o
+ib_qib-$(CONFIG_DEBUG_FS) += qib_debugfs.o
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 4d11575..4a9af79 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -1,7 +1,7 @@
#ifndef _QIB_KERNEL_H
#define _QIB_KERNEL_H
/*
- * Copyright (c) 2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2012, 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
@@ -51,6 +51,7 @@
#include <linux/completion.h>
#include <linux/kref.h>
#include <linux/sched.h>
+#include <linux/kthread.h>
#include "qib_common.h"
#include "qib_verbs.h"
@@ -114,6 +115,11 @@
/*
* Below contains all data related to a single context (formerly called port).
*/
+
+#ifdef CONFIG_DEBUG_FS
+struct qib_opcode_stats_perctx;
+#endif
+
struct qib_ctxtdata {
void **rcvegrbuf;
dma_addr_t *rcvegrbuf_phys;
@@ -154,6 +160,8 @@
*/
/* instead of calculating it */
unsigned ctxt;
+ /* local node of context */
+ int node_id;
/* non-zero if ctxt is being shared. */
u16 subctxt_cnt;
/* non-zero if ctxt is being shared. */
@@ -222,12 +230,15 @@
u8 redirect_seq_cnt;
/* ctxt rcvhdrq head offset */
u32 head;
- u32 pkt_count;
/* lookaside fields */
struct qib_qp *lookaside_qp;
u32 lookaside_qpn;
/* QPs waiting for context processing */
struct list_head qp_wait_list;
+#ifdef CONFIG_DEBUG_FS
+ /* verbs stats per CTX */
+ struct qib_opcode_stats_perctx *opstats;
+#endif
};
struct qib_sge_state;
@@ -428,9 +439,19 @@
#define ACTIVITY_TIMER 5
#define MAX_NAME_SIZE 64
+
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+struct qib_irq_notify;
+#endif
+
struct qib_msix_entry {
struct msix_entry msix;
void *arg;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ int dca;
+ int rcv;
+ struct qib_irq_notify *notifier;
+#endif
char name[MAX_NAME_SIZE];
cpumask_var_t mask;
};
@@ -828,6 +849,9 @@
struct qib_ctxtdata *);
void (*f_writescratch)(struct qib_devdata *, u32);
int (*f_tempsense_rd)(struct qib_devdata *, int regnum);
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ int (*f_notify_dca)(struct qib_devdata *, unsigned long event);
+#endif
char *boardname; /* human readable board info */
@@ -1075,6 +1099,10 @@
u16 psxmitwait_check_rate;
/* high volume overflow errors defered to tasklet */
struct tasklet_struct error_tasklet;
+ /* per device cq worker */
+ struct kthread_worker *worker;
+
+ int assigned_node_id; /* NUMA node closest to HCA */
};
/* hol_state values */
@@ -1154,7 +1182,7 @@
int qib_setup_eagerbufs(struct qib_ctxtdata *);
void qib_set_ctxtcnt(struct qib_devdata *);
int qib_create_ctxts(struct qib_devdata *dd);
-struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *, u32);
+struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *, u32, int);
void qib_init_pportdata(struct qib_pportdata *, struct qib_devdata *, u8, u8);
void qib_free_ctxtdata(struct qib_devdata *, struct qib_ctxtdata *);
@@ -1320,7 +1348,7 @@
return ppd->sdma_state.current_state == qib_sdma_state_s99_running;
}
int qib_sdma_running(struct qib_pportdata *);
-
+void dump_sdma_state(struct qib_pportdata *ppd);
void __qib_sdma_process_event(struct qib_pportdata *, enum qib_sdma_events);
void qib_sdma_process_event(struct qib_pportdata *, enum qib_sdma_events);
@@ -1445,6 +1473,7 @@
extern unsigned qib_sdma_fetch_arb;
extern unsigned qib_compat_ddr_negotiate;
extern int qib_special_trigger;
+extern unsigned qib_numa_aware;
extern struct mutex qib_mutex;
@@ -1474,27 +1503,23 @@
* first to avoid possible serial port delays from printk.
*/
#define qib_early_err(dev, fmt, ...) \
- do { \
- dev_err(dev, fmt, ##__VA_ARGS__); \
- } while (0)
+ dev_err(dev, fmt, ##__VA_ARGS__)
#define qib_dev_err(dd, fmt, ...) \
- do { \
- dev_err(&(dd)->pcidev->dev, "%s: " fmt, \
- qib_get_unit_name((dd)->unit), ##__VA_ARGS__); \
- } while (0)
+ dev_err(&(dd)->pcidev->dev, "%s: " fmt, \
+ qib_get_unit_name((dd)->unit), ##__VA_ARGS__)
+
+#define qib_dev_warn(dd, fmt, ...) \
+ dev_warn(&(dd)->pcidev->dev, "%s: " fmt, \
+ qib_get_unit_name((dd)->unit), ##__VA_ARGS__)
#define qib_dev_porterr(dd, port, fmt, ...) \
- do { \
- dev_err(&(dd)->pcidev->dev, "%s: IB%u:%u " fmt, \
- qib_get_unit_name((dd)->unit), (dd)->unit, (port), \
- ##__VA_ARGS__); \
- } while (0)
+ dev_err(&(dd)->pcidev->dev, "%s: IB%u:%u " fmt, \
+ qib_get_unit_name((dd)->unit), (dd)->unit, (port), \
+ ##__VA_ARGS__)
#define qib_devinfo(pcidev, fmt, ...) \
- do { \
- dev_info(&(pcidev)->dev, fmt, ##__VA_ARGS__); \
- } while (0)
+ dev_info(&(pcidev)->dev, fmt, ##__VA_ARGS__)
/*
* this is used for formatting hw error messages...
diff --git a/drivers/infiniband/hw/qib/qib_common.h b/drivers/infiniband/hw/qib/qib_common.h
index d39e018..4f255b7 100644
--- a/drivers/infiniband/hw/qib/qib_common.h
+++ b/drivers/infiniband/hw/qib/qib_common.h
@@ -279,7 +279,7 @@
* may not be implemented; the user code must deal with this if it
* cares, or it must abort after initialization reports the difference.
*/
-#define QIB_USER_SWMINOR 11
+#define QIB_USER_SWMINOR 12
#define QIB_USER_SWVERSION ((QIB_USER_SWMAJOR << 16) | QIB_USER_SWMINOR)
diff --git a/drivers/infiniband/hw/qib/qib_cq.c b/drivers/infiniband/hw/qib/qib_cq.c
index 5246aa4..ab4e11c 100644
--- a/drivers/infiniband/hw/qib/qib_cq.c
+++ b/drivers/infiniband/hw/qib/qib_cq.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006, 2007, 2008, 2010 QLogic Corporation. All rights reserved.
* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
*
@@ -34,8 +35,10 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/kthread.h>
#include "qib_verbs.h"
+#include "qib.h"
/**
* qib_cq_enter - add a new entry to the completion queue
@@ -102,13 +105,18 @@
if (cq->notify == IB_CQ_NEXT_COMP ||
(cq->notify == IB_CQ_SOLICITED &&
(solicited || entry->status != IB_WC_SUCCESS))) {
- cq->notify = IB_CQ_NONE;
- cq->triggered++;
+ struct kthread_worker *worker;
/*
* This will cause send_complete() to be called in
* another thread.
*/
- queue_work(qib_cq_wq, &cq->comptask);
+ smp_rmb();
+ worker = cq->dd->worker;
+ if (likely(worker)) {
+ cq->notify = IB_CQ_NONE;
+ cq->triggered++;
+ queue_kthread_work(worker, &cq->comptask);
+ }
}
spin_unlock_irqrestore(&cq->lock, flags);
@@ -163,7 +171,7 @@
return npolled;
}
-static void send_complete(struct work_struct *work)
+static void send_complete(struct kthread_work *work)
{
struct qib_cq *cq = container_of(work, struct qib_cq, comptask);
@@ -287,11 +295,12 @@
* The number of entries should be >= the number requested or return
* an error.
*/
+ cq->dd = dd_from_dev(dev);
cq->ibcq.cqe = entries;
cq->notify = IB_CQ_NONE;
cq->triggered = 0;
spin_lock_init(&cq->lock);
- INIT_WORK(&cq->comptask, send_complete);
+ init_kthread_work(&cq->comptask, send_complete);
wc->head = 0;
wc->tail = 0;
cq->queue = wc;
@@ -323,7 +332,7 @@
struct qib_ibdev *dev = to_idev(ibcq->device);
struct qib_cq *cq = to_icq(ibcq);
- flush_work(&cq->comptask);
+ flush_kthread_work(&cq->comptask);
spin_lock(&dev->n_cqs_lock);
dev->n_cqs_allocated--;
spin_unlock(&dev->n_cqs_lock);
@@ -483,3 +492,49 @@
bail:
return ret;
}
+
+int qib_cq_init(struct qib_devdata *dd)
+{
+ int ret = 0;
+ int cpu;
+ struct task_struct *task;
+
+ if (dd->worker)
+ return 0;
+ dd->worker = kzalloc(sizeof(*dd->worker), GFP_KERNEL);
+ if (!dd->worker)
+ return -ENOMEM;
+ init_kthread_worker(dd->worker);
+ task = kthread_create_on_node(
+ kthread_worker_fn,
+ dd->worker,
+ dd->assigned_node_id,
+ "qib_cq%d", dd->unit);
+ if (IS_ERR(task))
+ goto task_fail;
+ cpu = cpumask_first(cpumask_of_node(dd->assigned_node_id));
+ kthread_bind(task, cpu);
+ wake_up_process(task);
+out:
+ return ret;
+task_fail:
+ ret = PTR_ERR(task);
+ kfree(dd->worker);
+ dd->worker = NULL;
+ goto out;
+}
+
+void qib_cq_exit(struct qib_devdata *dd)
+{
+ struct kthread_worker *worker;
+
+ worker = dd->worker;
+ if (!worker)
+ return;
+ /* blocks future queuing from send_complete() */
+ dd->worker = NULL;
+ smp_wmb();
+ flush_kthread_worker(worker);
+ kthread_stop(worker->task);
+ kfree(worker);
+}
diff --git a/drivers/infiniband/hw/qib/qib_debugfs.c b/drivers/infiniband/hw/qib/qib_debugfs.c
new file mode 100644
index 0000000..799a0c3
--- /dev/null
+++ b/drivers/infiniband/hw/qib/qib_debugfs.c
@@ -0,0 +1,283 @@
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Copyright (c) 2013 Intel Corporation. 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.
+ */
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+
+#include "qib.h"
+#include "qib_verbs.h"
+#include "qib_debugfs.h"
+
+static struct dentry *qib_dbg_root;
+
+#define DEBUGFS_FILE(name) \
+static const struct seq_operations _##name##_seq_ops = { \
+ .start = _##name##_seq_start, \
+ .next = _##name##_seq_next, \
+ .stop = _##name##_seq_stop, \
+ .show = _##name##_seq_show \
+}; \
+static int _##name##_open(struct inode *inode, struct file *s) \
+{ \
+ struct seq_file *seq; \
+ int ret; \
+ ret = seq_open(s, &_##name##_seq_ops); \
+ if (ret) \
+ return ret; \
+ seq = s->private_data; \
+ seq->private = inode->i_private; \
+ return 0; \
+} \
+static const struct file_operations _##name##_file_ops = { \
+ .owner = THIS_MODULE, \
+ .open = _##name##_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = seq_release \
+};
+
+#define DEBUGFS_FILE_CREATE(name) \
+do { \
+ struct dentry *ent; \
+ ent = debugfs_create_file(#name , 0400, ibd->qib_ibdev_dbg, \
+ ibd, &_##name##_file_ops); \
+ if (!ent) \
+ pr_warn("create of " #name " failed\n"); \
+} while (0)
+
+static void *_opcode_stats_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct qib_opcode_stats_perctx *opstats;
+
+ if (*pos >= ARRAY_SIZE(opstats->stats))
+ return NULL;
+ return pos;
+}
+
+static void *_opcode_stats_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct qib_opcode_stats_perctx *opstats;
+
+ ++*pos;
+ if (*pos >= ARRAY_SIZE(opstats->stats))
+ return NULL;
+ return pos;
+}
+
+
+static void _opcode_stats_seq_stop(struct seq_file *s, void *v)
+{
+ /* nothing allocated */
+}
+
+static int _opcode_stats_seq_show(struct seq_file *s, void *v)
+{
+ loff_t *spos = v;
+ loff_t i = *spos, j;
+ u64 n_packets = 0, n_bytes = 0;
+ struct qib_ibdev *ibd = (struct qib_ibdev *)s->private;
+ struct qib_devdata *dd = dd_from_dev(ibd);
+
+ for (j = 0; j < dd->first_user_ctxt; j++) {
+ if (!dd->rcd[j])
+ continue;
+ n_packets += dd->rcd[j]->opstats->stats[i].n_packets;
+ n_bytes += dd->rcd[j]->opstats->stats[i].n_bytes;
+ }
+ if (!n_packets && !n_bytes)
+ return SEQ_SKIP;
+ seq_printf(s, "%02llx %llu/%llu\n", i,
+ (unsigned long long) n_packets,
+ (unsigned long long) n_bytes);
+
+ return 0;
+}
+
+DEBUGFS_FILE(opcode_stats)
+
+static void *_ctx_stats_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct qib_ibdev *ibd = (struct qib_ibdev *)s->private;
+ struct qib_devdata *dd = dd_from_dev(ibd);
+
+ if (!*pos)
+ return SEQ_START_TOKEN;
+ if (*pos >= dd->first_user_ctxt)
+ return NULL;
+ return pos;
+}
+
+static void *_ctx_stats_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct qib_ibdev *ibd = (struct qib_ibdev *)s->private;
+ struct qib_devdata *dd = dd_from_dev(ibd);
+
+ if (v == SEQ_START_TOKEN)
+ return pos;
+
+ ++*pos;
+ if (*pos >= dd->first_user_ctxt)
+ return NULL;
+ return pos;
+}
+
+static void _ctx_stats_seq_stop(struct seq_file *s, void *v)
+{
+ /* nothing allocated */
+}
+
+static int _ctx_stats_seq_show(struct seq_file *s, void *v)
+{
+ loff_t *spos;
+ loff_t i, j;
+ u64 n_packets = 0;
+ struct qib_ibdev *ibd = (struct qib_ibdev *)s->private;
+ struct qib_devdata *dd = dd_from_dev(ibd);
+
+ if (v == SEQ_START_TOKEN) {
+ seq_puts(s, "Ctx:npkts\n");
+ return 0;
+ }
+
+ spos = v;
+ i = *spos;
+
+ if (!dd->rcd[i])
+ return SEQ_SKIP;
+
+ for (j = 0; j < ARRAY_SIZE(dd->rcd[i]->opstats->stats); j++)
+ n_packets += dd->rcd[i]->opstats->stats[j].n_packets;
+
+ if (!n_packets)
+ return SEQ_SKIP;
+
+ seq_printf(s, " %llu:%llu\n", i, n_packets);
+ return 0;
+}
+
+DEBUGFS_FILE(ctx_stats)
+
+static void *_qp_stats_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct qib_qp_iter *iter;
+ loff_t n = *pos;
+
+ iter = qib_qp_iter_init(s->private);
+ if (!iter)
+ return NULL;
+
+ while (n--) {
+ if (qib_qp_iter_next(iter)) {
+ kfree(iter);
+ return NULL;
+ }
+ }
+
+ return iter;
+}
+
+static void *_qp_stats_seq_next(struct seq_file *s, void *iter_ptr,
+ loff_t *pos)
+{
+ struct qib_qp_iter *iter = iter_ptr;
+
+ (*pos)++;
+
+ if (qib_qp_iter_next(iter)) {
+ kfree(iter);
+ return NULL;
+ }
+
+ return iter;
+}
+
+static void _qp_stats_seq_stop(struct seq_file *s, void *iter_ptr)
+{
+ /* nothing for now */
+}
+
+static int _qp_stats_seq_show(struct seq_file *s, void *iter_ptr)
+{
+ struct qib_qp_iter *iter = iter_ptr;
+
+ if (!iter)
+ return 0;
+
+ qib_qp_iter_print(s, iter);
+
+ return 0;
+}
+
+DEBUGFS_FILE(qp_stats)
+
+void qib_dbg_ibdev_init(struct qib_ibdev *ibd)
+{
+ char name[10];
+
+ snprintf(name, sizeof(name), "qib%d", dd_from_dev(ibd)->unit);
+ ibd->qib_ibdev_dbg = debugfs_create_dir(name, qib_dbg_root);
+ if (!ibd->qib_ibdev_dbg) {
+ pr_warn("create of %s failed\n", name);
+ return;
+ }
+ DEBUGFS_FILE_CREATE(opcode_stats);
+ DEBUGFS_FILE_CREATE(ctx_stats);
+ DEBUGFS_FILE_CREATE(qp_stats);
+ return;
+}
+
+void qib_dbg_ibdev_exit(struct qib_ibdev *ibd)
+{
+ if (!qib_dbg_root)
+ goto out;
+ debugfs_remove_recursive(ibd->qib_ibdev_dbg);
+out:
+ ibd->qib_ibdev_dbg = NULL;
+}
+
+void qib_dbg_init(void)
+{
+ qib_dbg_root = debugfs_create_dir(QIB_DRV_NAME, NULL);
+ if (!qib_dbg_root)
+ pr_warn("init of debugfs failed\n");
+}
+
+void qib_dbg_exit(void)
+{
+ debugfs_remove_recursive(qib_dbg_root);
+ qib_dbg_root = NULL;
+}
+
+#endif
+
diff --git a/drivers/infiniband/hw/qib/qib_debugfs.h b/drivers/infiniband/hw/qib/qib_debugfs.h
new file mode 100644
index 0000000..7ae983a
--- /dev/null
+++ b/drivers/infiniband/hw/qib/qib_debugfs.h
@@ -0,0 +1,45 @@
+#ifndef _QIB_DEBUGFS_H
+#define _QIB_DEBUGFS_H
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * Copyright (c) 2013 Intel Corporation. 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.
+ */
+
+struct qib_ibdev;
+void qib_dbg_ibdev_init(struct qib_ibdev *ibd);
+void qib_dbg_ibdev_exit(struct qib_ibdev *ibd);
+void qib_dbg_init(void);
+void qib_dbg_exit(void);
+
+#endif
+
+#endif /* _QIB_DEBUGFS_H */
diff --git a/drivers/infiniband/hw/qib/qib_driver.c b/drivers/infiniband/hw/qib/qib_driver.c
index 2160924..5bee08f 100644
--- a/drivers/infiniband/hw/qib/qib_driver.c
+++ b/drivers/infiniband/hw/qib/qib_driver.c
@@ -558,7 +558,6 @@
}
rcd->head = l;
- rcd->pkt_count += i;
/*
* Iterate over all QPs waiting to respond.
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index 9dd0bc8..b51a514 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2012, 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
*
@@ -1155,6 +1155,49 @@
return pollflag;
}
+static void assign_ctxt_affinity(struct file *fp, struct qib_devdata *dd)
+{
+ struct qib_filedata *fd = fp->private_data;
+ const unsigned int weight = cpumask_weight(¤t->cpus_allowed);
+ const struct cpumask *local_mask = cpumask_of_pcibus(dd->pcidev->bus);
+ int local_cpu;
+
+ /*
+ * If process has NOT already set it's affinity, select and
+ * reserve a processor for it on the local NUMA node.
+ */
+ if ((weight >= qib_cpulist_count) &&
+ (cpumask_weight(local_mask) <= qib_cpulist_count)) {
+ for_each_cpu(local_cpu, local_mask)
+ if (!test_and_set_bit(local_cpu, qib_cpulist)) {
+ fd->rec_cpu_num = local_cpu;
+ return;
+ }
+ }
+
+ /*
+ * If process has NOT already set it's affinity, select and
+ * reserve a processor for it, as a rendevous for all
+ * users of the driver. If they don't actually later
+ * set affinity to this cpu, or set it to some other cpu,
+ * it just means that sooner or later we don't recommend
+ * a cpu, and let the scheduler do it's best.
+ */
+ if (weight >= qib_cpulist_count) {
+ int cpu;
+ cpu = find_first_zero_bit(qib_cpulist,
+ qib_cpulist_count);
+ if (cpu == qib_cpulist_count)
+ qib_dev_err(dd,
+ "no cpus avail for affinity PID %u\n",
+ current->pid);
+ else {
+ __set_bit(cpu, qib_cpulist);
+ fd->rec_cpu_num = cpu;
+ }
+ }
+}
+
/*
* Check that userland and driver are compatible for subcontexts.
*/
@@ -1259,12 +1302,20 @@
static int setup_ctxt(struct qib_pportdata *ppd, int ctxt,
struct file *fp, const struct qib_user_info *uinfo)
{
+ struct qib_filedata *fd = fp->private_data;
struct qib_devdata *dd = ppd->dd;
struct qib_ctxtdata *rcd;
void *ptmp = NULL;
int ret;
+ int numa_id;
- rcd = qib_create_ctxtdata(ppd, ctxt);
+ assign_ctxt_affinity(fp, dd);
+
+ numa_id = qib_numa_aware ? ((fd->rec_cpu_num != -1) ?
+ cpu_to_node(fd->rec_cpu_num) :
+ numa_node_id()) : dd->assigned_node_id;
+
+ rcd = qib_create_ctxtdata(ppd, ctxt, numa_id);
/*
* Allocate memory for use in qib_tid_update() at open to
@@ -1296,6 +1347,9 @@
goto bail;
bailerr:
+ if (fd->rec_cpu_num != -1)
+ __clear_bit(fd->rec_cpu_num, qib_cpulist);
+
dd->rcd[ctxt] = NULL;
kfree(rcd);
kfree(ptmp);
@@ -1485,6 +1539,57 @@
return fp->private_data ? 0 : -ENOMEM;
}
+static int find_hca(unsigned int cpu, int *unit)
+{
+ int ret = 0, devmax, npresent, nup, ndev;
+
+ *unit = -1;
+
+ devmax = qib_count_units(&npresent, &nup);
+ if (!npresent) {
+ ret = -ENXIO;
+ goto done;
+ }
+ if (!nup) {
+ ret = -ENETDOWN;
+ goto done;
+ }
+ for (ndev = 0; ndev < devmax; ndev++) {
+ struct qib_devdata *dd = qib_lookup(ndev);
+ if (dd) {
+ if (pcibus_to_node(dd->pcidev->bus) < 0) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (cpu_to_node(cpu) ==
+ pcibus_to_node(dd->pcidev->bus)) {
+ *unit = ndev;
+ goto done;
+ }
+ }
+ }
+done:
+ return ret;
+}
+
+static int do_qib_user_sdma_queue_create(struct file *fp)
+{
+ struct qib_filedata *fd = fp->private_data;
+ struct qib_ctxtdata *rcd = fd->rcd;
+ struct qib_devdata *dd = rcd->dd;
+
+ if (dd->flags & QIB_HAS_SEND_DMA)
+
+ fd->pq = qib_user_sdma_queue_create(&dd->pcidev->dev,
+ dd->unit,
+ rcd->ctxt,
+ fd->subctxt);
+ if (!fd->pq)
+ return -ENOMEM;
+
+ return 0;
+}
+
/*
* Get ctxt early, so can set affinity prior to memory allocation.
*/
@@ -1517,61 +1622,36 @@
if (qib_compatible_subctxts(swmajor, swminor) &&
uinfo->spu_subctxt_cnt) {
ret = find_shared_ctxt(fp, uinfo);
- if (ret) {
- if (ret > 0)
- ret = 0;
- goto done_chk_sdma;
+ if (ret > 0) {
+ ret = do_qib_user_sdma_queue_create(fp);
+ if (!ret)
+ assign_ctxt_affinity(fp, (ctxt_fp(fp))->dd);
+ goto done_ok;
}
}
i_minor = iminor(file_inode(fp)) - QIB_USER_MINOR_BASE;
if (i_minor)
ret = find_free_ctxt(i_minor - 1, fp, uinfo);
- else
+ else {
+ int unit;
+ const unsigned int cpu = cpumask_first(¤t->cpus_allowed);
+ const unsigned int weight =
+ cpumask_weight(¤t->cpus_allowed);
+
+ if (weight == 1 && !test_bit(cpu, qib_cpulist))
+ if (!find_hca(cpu, &unit) && unit >= 0)
+ if (!find_free_ctxt(unit, fp, uinfo)) {
+ ret = 0;
+ goto done_chk_sdma;
+ }
ret = get_a_ctxt(fp, uinfo, alg);
-
-done_chk_sdma:
- if (!ret) {
- struct qib_filedata *fd = fp->private_data;
- const struct qib_ctxtdata *rcd = fd->rcd;
- const struct qib_devdata *dd = rcd->dd;
- unsigned int weight;
-
- if (dd->flags & QIB_HAS_SEND_DMA) {
- fd->pq = qib_user_sdma_queue_create(&dd->pcidev->dev,
- dd->unit,
- rcd->ctxt,
- fd->subctxt);
- if (!fd->pq)
- ret = -ENOMEM;
- }
-
- /*
- * If process has NOT already set it's affinity, select and
- * reserve a processor for it, as a rendezvous for all
- * users of the driver. If they don't actually later
- * set affinity to this cpu, or set it to some other cpu,
- * it just means that sooner or later we don't recommend
- * a cpu, and let the scheduler do it's best.
- */
- weight = cpumask_weight(tsk_cpus_allowed(current));
- if (!ret && weight >= qib_cpulist_count) {
- int cpu;
- cpu = find_first_zero_bit(qib_cpulist,
- qib_cpulist_count);
- if (cpu != qib_cpulist_count) {
- __set_bit(cpu, qib_cpulist);
- fd->rec_cpu_num = cpu;
- }
- } else if (weight == 1 &&
- test_bit(cpumask_first(tsk_cpus_allowed(current)),
- qib_cpulist))
- qib_devinfo(dd->pcidev,
- "%s PID %u affinity set to cpu %d; already allocated\n",
- current->comm, current->pid,
- cpumask_first(tsk_cpus_allowed(current)));
}
+done_chk_sdma:
+ if (!ret)
+ ret = do_qib_user_sdma_queue_create(fp);
+done_ok:
mutex_unlock(&qib_mutex);
done:
diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c
index 0232ae5..84e593d 100644
--- a/drivers/infiniband/hw/qib/qib_iba6120.c
+++ b/drivers/infiniband/hw/qib/qib_iba6120.c
@@ -3464,6 +3464,13 @@
return -ENXIO;
}
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+static int qib_6120_notify_dca(struct qib_devdata *dd, unsigned long event)
+{
+ return 0;
+}
+#endif
+
/* Dummy function, as 6120 boards never disable EEPROM Write */
static int qib_6120_eeprom_wen(struct qib_devdata *dd, int wen)
{
@@ -3539,6 +3546,9 @@
dd->f_xgxs_reset = qib_6120_xgxs_reset;
dd->f_writescratch = writescratch;
dd->f_tempsense_rd = qib_6120_tempsense_rd;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dd->f_notify_dca = qib_6120_notify_dca;
+#endif
/*
* Do remaining pcie setup and save pcie values in dd.
* Any error printing is already done by the init code.
diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c
index 64d0ecb..454c2e7 100644
--- a/drivers/infiniband/hw/qib/qib_iba7220.c
+++ b/drivers/infiniband/hw/qib/qib_iba7220.c
@@ -4513,6 +4513,13 @@
return ret;
}
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+static int qib_7220_notify_dca(struct qib_devdata *dd, unsigned long event)
+{
+ return 0;
+}
+#endif
+
/* Dummy function, as 7220 boards never disable EEPROM Write */
static int qib_7220_eeprom_wen(struct qib_devdata *dd, int wen)
{
@@ -4587,6 +4594,9 @@
dd->f_xgxs_reset = qib_7220_xgxs_reset;
dd->f_writescratch = writescratch;
dd->f_tempsense_rd = qib_7220_tempsense_rd;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dd->f_notify_dca = qib_7220_notify_dca;
+#endif
/*
* Do remaining pcie setup and save pcie values in dd.
* Any error printing is already done by the init code.
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 3f6b21e..21e8b09 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -44,6 +44,9 @@
#include <linux/module.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_smi.h>
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+#include <linux/dca.h>
+#endif
#include "qib.h"
#include "qib_7322_regs.h"
@@ -80,6 +83,7 @@
static void serdes_7322_los_enable(struct qib_pportdata *, int);
static int serdes_7322_init_old(struct qib_pportdata *);
static int serdes_7322_init_new(struct qib_pportdata *);
+static void dump_sdma_7322_state(struct qib_pportdata *);
#define BMASK(msb, lsb) (((1 << ((msb) + 1 - (lsb))) - 1) << (lsb))
@@ -519,6 +523,14 @@
[0x17] = IB_PHYSPORTSTATE_CFG_TRAIN
};
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+struct qib_irq_notify {
+ int rcv;
+ void *arg;
+ struct irq_affinity_notify notify;
+};
+#endif
+
struct qib_chip_specific {
u64 __iomem *cregbase;
u64 *cntrs;
@@ -546,6 +558,12 @@
u32 lastbuf_for_pio;
u32 stay_in_freeze;
u32 recovery_ports_initted;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ u32 dca_ctrl;
+ int rhdr_cpu[18];
+ int sdma_cpu[2];
+ u64 dca_rcvhdr_ctrl[5]; /* B, C, D, E, F */
+#endif
struct qib_msix_entry *msix_entries;
unsigned long *sendchkenable;
unsigned long *sendgrhchk;
@@ -573,7 +591,7 @@
static void write_tx_serdes_param(struct qib_pportdata *, struct txdds_ent *);
#define TXDDS_TABLE_SZ 16 /* number of entries per speed in onchip table */
-#define TXDDS_EXTRA_SZ 13 /* number of extra tx settings entries */
+#define TXDDS_EXTRA_SZ 18 /* number of extra tx settings entries */
#define TXDDS_MFG_SZ 2 /* number of mfg tx settings entries */
#define SERDES_CHANS 4 /* yes, it's obvious, but one less magic number */
@@ -635,6 +653,7 @@
u8 ibmalfusesnap;
struct qib_qsfp_data qsfp_data;
char epmsgbuf[192]; /* for port error interrupt msg buffer */
+ char sdmamsgbuf[192]; /* for per-port sdma error messages */
};
static struct {
@@ -642,28 +661,76 @@
irq_handler_t handler;
int lsb;
int port; /* 0 if not port-specific, else port # */
+ int dca;
} irq_table[] = {
- { "", qib_7322intr, -1, 0 },
+ { "", qib_7322intr, -1, 0, 0 },
{ " (buf avail)", qib_7322bufavail,
- SYM_LSB(IntStatus, SendBufAvail), 0 },
+ SYM_LSB(IntStatus, SendBufAvail), 0, 0},
{ " (sdma 0)", sdma_intr,
- SYM_LSB(IntStatus, SDmaInt_0), 1 },
+ SYM_LSB(IntStatus, SDmaInt_0), 1, 1 },
{ " (sdma 1)", sdma_intr,
- SYM_LSB(IntStatus, SDmaInt_1), 2 },
+ SYM_LSB(IntStatus, SDmaInt_1), 2, 1 },
{ " (sdmaI 0)", sdma_idle_intr,
- SYM_LSB(IntStatus, SDmaIdleInt_0), 1 },
+ SYM_LSB(IntStatus, SDmaIdleInt_0), 1, 1},
{ " (sdmaI 1)", sdma_idle_intr,
- SYM_LSB(IntStatus, SDmaIdleInt_1), 2 },
+ SYM_LSB(IntStatus, SDmaIdleInt_1), 2, 1},
{ " (sdmaP 0)", sdma_progress_intr,
- SYM_LSB(IntStatus, SDmaProgressInt_0), 1 },
+ SYM_LSB(IntStatus, SDmaProgressInt_0), 1, 1 },
{ " (sdmaP 1)", sdma_progress_intr,
- SYM_LSB(IntStatus, SDmaProgressInt_1), 2 },
+ SYM_LSB(IntStatus, SDmaProgressInt_1), 2, 1 },
{ " (sdmaC 0)", sdma_cleanup_intr,
- SYM_LSB(IntStatus, SDmaCleanupDone_0), 1 },
+ SYM_LSB(IntStatus, SDmaCleanupDone_0), 1, 0 },
{ " (sdmaC 1)", sdma_cleanup_intr,
- SYM_LSB(IntStatus, SDmaCleanupDone_1), 2 },
+ SYM_LSB(IntStatus, SDmaCleanupDone_1), 2 , 0},
};
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+
+static const struct dca_reg_map {
+ int shadow_inx;
+ int lsb;
+ u64 mask;
+ u16 regno;
+} dca_rcvhdr_reg_map[] = {
+ { 0, SYM_LSB(DCACtrlB, RcvHdrq0DCAOPH),
+ ~SYM_MASK(DCACtrlB, RcvHdrq0DCAOPH) , KREG_IDX(DCACtrlB) },
+ { 0, SYM_LSB(DCACtrlB, RcvHdrq1DCAOPH),
+ ~SYM_MASK(DCACtrlB, RcvHdrq1DCAOPH) , KREG_IDX(DCACtrlB) },
+ { 0, SYM_LSB(DCACtrlB, RcvHdrq2DCAOPH),
+ ~SYM_MASK(DCACtrlB, RcvHdrq2DCAOPH) , KREG_IDX(DCACtrlB) },
+ { 0, SYM_LSB(DCACtrlB, RcvHdrq3DCAOPH),
+ ~SYM_MASK(DCACtrlB, RcvHdrq3DCAOPH) , KREG_IDX(DCACtrlB) },
+ { 1, SYM_LSB(DCACtrlC, RcvHdrq4DCAOPH),
+ ~SYM_MASK(DCACtrlC, RcvHdrq4DCAOPH) , KREG_IDX(DCACtrlC) },
+ { 1, SYM_LSB(DCACtrlC, RcvHdrq5DCAOPH),
+ ~SYM_MASK(DCACtrlC, RcvHdrq5DCAOPH) , KREG_IDX(DCACtrlC) },
+ { 1, SYM_LSB(DCACtrlC, RcvHdrq6DCAOPH),
+ ~SYM_MASK(DCACtrlC, RcvHdrq6DCAOPH) , KREG_IDX(DCACtrlC) },
+ { 1, SYM_LSB(DCACtrlC, RcvHdrq7DCAOPH),
+ ~SYM_MASK(DCACtrlC, RcvHdrq7DCAOPH) , KREG_IDX(DCACtrlC) },
+ { 2, SYM_LSB(DCACtrlD, RcvHdrq8DCAOPH),
+ ~SYM_MASK(DCACtrlD, RcvHdrq8DCAOPH) , KREG_IDX(DCACtrlD) },
+ { 2, SYM_LSB(DCACtrlD, RcvHdrq9DCAOPH),
+ ~SYM_MASK(DCACtrlD, RcvHdrq9DCAOPH) , KREG_IDX(DCACtrlD) },
+ { 2, SYM_LSB(DCACtrlD, RcvHdrq10DCAOPH),
+ ~SYM_MASK(DCACtrlD, RcvHdrq10DCAOPH) , KREG_IDX(DCACtrlD) },
+ { 2, SYM_LSB(DCACtrlD, RcvHdrq11DCAOPH),
+ ~SYM_MASK(DCACtrlD, RcvHdrq11DCAOPH) , KREG_IDX(DCACtrlD) },
+ { 3, SYM_LSB(DCACtrlE, RcvHdrq12DCAOPH),
+ ~SYM_MASK(DCACtrlE, RcvHdrq12DCAOPH) , KREG_IDX(DCACtrlE) },
+ { 3, SYM_LSB(DCACtrlE, RcvHdrq13DCAOPH),
+ ~SYM_MASK(DCACtrlE, RcvHdrq13DCAOPH) , KREG_IDX(DCACtrlE) },
+ { 3, SYM_LSB(DCACtrlE, RcvHdrq14DCAOPH),
+ ~SYM_MASK(DCACtrlE, RcvHdrq14DCAOPH) , KREG_IDX(DCACtrlE) },
+ { 3, SYM_LSB(DCACtrlE, RcvHdrq15DCAOPH),
+ ~SYM_MASK(DCACtrlE, RcvHdrq15DCAOPH) , KREG_IDX(DCACtrlE) },
+ { 4, SYM_LSB(DCACtrlF, RcvHdrq16DCAOPH),
+ ~SYM_MASK(DCACtrlF, RcvHdrq16DCAOPH) , KREG_IDX(DCACtrlF) },
+ { 4, SYM_LSB(DCACtrlF, RcvHdrq17DCAOPH),
+ ~SYM_MASK(DCACtrlF, RcvHdrq17DCAOPH) , KREG_IDX(DCACtrlF) },
+};
+#endif
+
/* ibcctrl bits */
#define QLOGIC_IB_IBCC_LINKINITCMD_DISABLE 1
/* cycle through TS1/TS2 till OK */
@@ -686,6 +753,13 @@
static void setup_7322_link_recovery(struct qib_pportdata *, u32);
static void check_7322_rxe_status(struct qib_pportdata *);
static u32 __iomem *qib_7322_getsendbuf(struct qib_pportdata *, u64, u32 *);
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+static void qib_setup_dca(struct qib_devdata *dd);
+static void setup_dca_notifier(struct qib_devdata *dd,
+ struct qib_msix_entry *m);
+static void reset_dca_notifier(struct qib_devdata *dd,
+ struct qib_msix_entry *m);
+#endif
/**
* qib_read_ureg32 - read 32-bit virtualized per-context register
@@ -1529,6 +1603,15 @@
spin_lock_irqsave(&ppd->sdma_lock, flags);
+ if (errs != QIB_E_P_SDMAHALT) {
+ /* SDMA errors have QIB_E_P_SDMAHALT and another bit set */
+ qib_dev_porterr(dd, ppd->port,
+ "SDMA %s 0x%016llx %s\n",
+ qib_sdma_state_names[ppd->sdma_state.current_state],
+ errs, ppd->cpspec->sdmamsgbuf);
+ dump_sdma_7322_state(ppd);
+ }
+
switch (ppd->sdma_state.current_state) {
case qib_sdma_state_s00_hw_down:
break;
@@ -2084,6 +2167,29 @@
qib_dev_err(dd, "%s hardware error\n", msg);
+ if (hwerrs &
+ (SYM_MASK(HwErrMask, SDmaMemReadErrMask_0) |
+ SYM_MASK(HwErrMask, SDmaMemReadErrMask_1))) {
+ int pidx = 0;
+ int err;
+ unsigned long flags;
+ struct qib_pportdata *ppd = dd->pport;
+ for (; pidx < dd->num_pports; ++pidx, ppd++) {
+ err = 0;
+ if (pidx == 0 && (hwerrs &
+ SYM_MASK(HwErrMask, SDmaMemReadErrMask_0)))
+ err++;
+ if (pidx == 1 && (hwerrs &
+ SYM_MASK(HwErrMask, SDmaMemReadErrMask_1)))
+ err++;
+ if (err) {
+ spin_lock_irqsave(&ppd->sdma_lock, flags);
+ dump_sdma_7322_state(ppd);
+ spin_unlock_irqrestore(&ppd->sdma_lock, flags);
+ }
+ }
+ }
+
if (isfatal && !dd->diag_client) {
qib_dev_err(dd,
"Fatal Hardware Error, no longer usable, SN %.16s\n",
@@ -2558,6 +2664,162 @@
qib_write_kreg_port(ppd, krp_rcvpktledcnt, ledblink);
}
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+
+static int qib_7322_notify_dca(struct qib_devdata *dd, unsigned long event)
+{
+ switch (event) {
+ case DCA_PROVIDER_ADD:
+ if (dd->flags & QIB_DCA_ENABLED)
+ break;
+ if (!dca_add_requester(&dd->pcidev->dev)) {
+ qib_devinfo(dd->pcidev, "DCA enabled\n");
+ dd->flags |= QIB_DCA_ENABLED;
+ qib_setup_dca(dd);
+ }
+ break;
+ case DCA_PROVIDER_REMOVE:
+ if (dd->flags & QIB_DCA_ENABLED) {
+ dca_remove_requester(&dd->pcidev->dev);
+ dd->flags &= ~QIB_DCA_ENABLED;
+ dd->cspec->dca_ctrl = 0;
+ qib_write_kreg(dd, KREG_IDX(DCACtrlA),
+ dd->cspec->dca_ctrl);
+ }
+ break;
+ }
+ return 0;
+}
+
+static void qib_update_rhdrq_dca(struct qib_ctxtdata *rcd, int cpu)
+{
+ struct qib_devdata *dd = rcd->dd;
+ struct qib_chip_specific *cspec = dd->cspec;
+
+ if (!(dd->flags & QIB_DCA_ENABLED))
+ return;
+ if (cspec->rhdr_cpu[rcd->ctxt] != cpu) {
+ const struct dca_reg_map *rmp;
+
+ cspec->rhdr_cpu[rcd->ctxt] = cpu;
+ rmp = &dca_rcvhdr_reg_map[rcd->ctxt];
+ cspec->dca_rcvhdr_ctrl[rmp->shadow_inx] &= rmp->mask;
+ cspec->dca_rcvhdr_ctrl[rmp->shadow_inx] |=
+ (u64) dca3_get_tag(&dd->pcidev->dev, cpu) << rmp->lsb;
+ qib_devinfo(dd->pcidev,
+ "Ctxt %d cpu %d dca %llx\n", rcd->ctxt, cpu,
+ (long long) cspec->dca_rcvhdr_ctrl[rmp->shadow_inx]);
+ qib_write_kreg(dd, rmp->regno,
+ cspec->dca_rcvhdr_ctrl[rmp->shadow_inx]);
+ cspec->dca_ctrl |= SYM_MASK(DCACtrlA, RcvHdrqDCAEnable);
+ qib_write_kreg(dd, KREG_IDX(DCACtrlA), cspec->dca_ctrl);
+ }
+}
+
+static void qib_update_sdma_dca(struct qib_pportdata *ppd, int cpu)
+{
+ struct qib_devdata *dd = ppd->dd;
+ struct qib_chip_specific *cspec = dd->cspec;
+ unsigned pidx = ppd->port - 1;
+
+ if (!(dd->flags & QIB_DCA_ENABLED))
+ return;
+ if (cspec->sdma_cpu[pidx] != cpu) {
+ cspec->sdma_cpu[pidx] = cpu;
+ cspec->dca_rcvhdr_ctrl[4] &= ~(ppd->hw_pidx ?
+ SYM_MASK(DCACtrlF, SendDma1DCAOPH) :
+ SYM_MASK(DCACtrlF, SendDma0DCAOPH));
+ cspec->dca_rcvhdr_ctrl[4] |=
+ (u64) dca3_get_tag(&dd->pcidev->dev, cpu) <<
+ (ppd->hw_pidx ?
+ SYM_LSB(DCACtrlF, SendDma1DCAOPH) :
+ SYM_LSB(DCACtrlF, SendDma0DCAOPH));
+ qib_devinfo(dd->pcidev,
+ "sdma %d cpu %d dca %llx\n", ppd->hw_pidx, cpu,
+ (long long) cspec->dca_rcvhdr_ctrl[4]);
+ qib_write_kreg(dd, KREG_IDX(DCACtrlF),
+ cspec->dca_rcvhdr_ctrl[4]);
+ cspec->dca_ctrl |= ppd->hw_pidx ?
+ SYM_MASK(DCACtrlA, SendDMAHead1DCAEnable) :
+ SYM_MASK(DCACtrlA, SendDMAHead0DCAEnable);
+ qib_write_kreg(dd, KREG_IDX(DCACtrlA), cspec->dca_ctrl);
+ }
+}
+
+static void qib_setup_dca(struct qib_devdata *dd)
+{
+ struct qib_chip_specific *cspec = dd->cspec;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cspec->rhdr_cpu); i++)
+ cspec->rhdr_cpu[i] = -1;
+ for (i = 0; i < ARRAY_SIZE(cspec->sdma_cpu); i++)
+ cspec->sdma_cpu[i] = -1;
+ cspec->dca_rcvhdr_ctrl[0] =
+ (1ULL << SYM_LSB(DCACtrlB, RcvHdrq0DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlB, RcvHdrq1DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlB, RcvHdrq2DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlB, RcvHdrq3DCAXfrCnt));
+ cspec->dca_rcvhdr_ctrl[1] =
+ (1ULL << SYM_LSB(DCACtrlC, RcvHdrq4DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlC, RcvHdrq5DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlC, RcvHdrq6DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlC, RcvHdrq7DCAXfrCnt));
+ cspec->dca_rcvhdr_ctrl[2] =
+ (1ULL << SYM_LSB(DCACtrlD, RcvHdrq8DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlD, RcvHdrq9DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlD, RcvHdrq10DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlD, RcvHdrq11DCAXfrCnt));
+ cspec->dca_rcvhdr_ctrl[3] =
+ (1ULL << SYM_LSB(DCACtrlE, RcvHdrq12DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlE, RcvHdrq13DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlE, RcvHdrq14DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlE, RcvHdrq15DCAXfrCnt));
+ cspec->dca_rcvhdr_ctrl[4] =
+ (1ULL << SYM_LSB(DCACtrlF, RcvHdrq16DCAXfrCnt)) |
+ (1ULL << SYM_LSB(DCACtrlF, RcvHdrq17DCAXfrCnt));
+ for (i = 0; i < ARRAY_SIZE(cspec->sdma_cpu); i++)
+ qib_write_kreg(dd, KREG_IDX(DCACtrlB) + i,
+ cspec->dca_rcvhdr_ctrl[i]);
+ for (i = 0; i < cspec->num_msix_entries; i++)
+ setup_dca_notifier(dd, &cspec->msix_entries[i]);
+}
+
+static void qib_irq_notifier_notify(struct irq_affinity_notify *notify,
+ const cpumask_t *mask)
+{
+ struct qib_irq_notify *n =
+ container_of(notify, struct qib_irq_notify, notify);
+ int cpu = cpumask_first(mask);
+
+ if (n->rcv) {
+ struct qib_ctxtdata *rcd = (struct qib_ctxtdata *)n->arg;
+ qib_update_rhdrq_dca(rcd, cpu);
+ } else {
+ struct qib_pportdata *ppd = (struct qib_pportdata *)n->arg;
+ qib_update_sdma_dca(ppd, cpu);
+ }
+}
+
+static void qib_irq_notifier_release(struct kref *ref)
+{
+ struct qib_irq_notify *n =
+ container_of(ref, struct qib_irq_notify, notify.kref);
+ struct qib_devdata *dd;
+
+ if (n->rcv) {
+ struct qib_ctxtdata *rcd = (struct qib_ctxtdata *)n->arg;
+ dd = rcd->dd;
+ } else {
+ struct qib_pportdata *ppd = (struct qib_pportdata *)n->arg;
+ dd = ppd->dd;
+ }
+ qib_devinfo(dd->pcidev,
+ "release on HCA notify 0x%p n 0x%p\n", ref, n);
+ kfree(n);
+}
+#endif
+
/*
* Disable MSIx interrupt if enabled, call generic MSIx code
* to cleanup, and clear pending MSIx interrupts.
@@ -2575,6 +2837,9 @@
dd->cspec->num_msix_entries = 0;
for (i = 0; i < n; i++) {
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ reset_dca_notifier(dd, &dd->cspec->msix_entries[i]);
+#endif
irq_set_affinity_hint(
dd->cspec->msix_entries[i].msix.vector, NULL);
free_cpumask_var(dd->cspec->msix_entries[i].mask);
@@ -2602,6 +2867,15 @@
{
int i;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ if (dd->flags & QIB_DCA_ENABLED) {
+ dca_remove_requester(&dd->pcidev->dev);
+ dd->flags &= ~QIB_DCA_ENABLED;
+ dd->cspec->dca_ctrl = 0;
+ qib_write_kreg(dd, KREG_IDX(DCACtrlA), dd->cspec->dca_ctrl);
+ }
+#endif
+
qib_7322_free_irq(dd);
kfree(dd->cspec->cntrs);
kfree(dd->cspec->sendchkenable);
@@ -3068,6 +3342,53 @@
return IRQ_HANDLED;
}
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+
+static void reset_dca_notifier(struct qib_devdata *dd, struct qib_msix_entry *m)
+{
+ if (!m->dca)
+ return;
+ qib_devinfo(dd->pcidev,
+ "Disabling notifier on HCA %d irq %d\n",
+ dd->unit,
+ m->msix.vector);
+ irq_set_affinity_notifier(
+ m->msix.vector,
+ NULL);
+ m->notifier = NULL;
+}
+
+static void setup_dca_notifier(struct qib_devdata *dd, struct qib_msix_entry *m)
+{
+ struct qib_irq_notify *n;
+
+ if (!m->dca)
+ return;
+ n = kzalloc(sizeof(*n), GFP_KERNEL);
+ if (n) {
+ int ret;
+
+ m->notifier = n;
+ n->notify.irq = m->msix.vector;
+ n->notify.notify = qib_irq_notifier_notify;
+ n->notify.release = qib_irq_notifier_release;
+ n->arg = m->arg;
+ n->rcv = m->rcv;
+ qib_devinfo(dd->pcidev,
+ "set notifier irq %d rcv %d notify %p\n",
+ n->notify.irq, n->rcv, &n->notify);
+ ret = irq_set_affinity_notifier(
+ n->notify.irq,
+ &n->notify);
+ if (ret) {
+ m->notifier = NULL;
+ kfree(n);
+ }
+ }
+}
+
+#endif
+
/*
* Set up our chip-specific interrupt handler.
* The interrupt type has already been setup, so
@@ -3149,6 +3470,9 @@
void *arg;
u64 val;
int lsb, reg, sh;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ int dca = 0;
+#endif
dd->cspec->msix_entries[msixnum].
name[sizeof(dd->cspec->msix_entries[msixnum].name) - 1]
@@ -3161,6 +3485,9 @@
arg = dd->pport + irq_table[i].port - 1;
} else
arg = dd;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dca = irq_table[i].dca;
+#endif
lsb = irq_table[i].lsb;
handler = irq_table[i].handler;
snprintf(dd->cspec->msix_entries[msixnum].name,
@@ -3178,6 +3505,9 @@
continue;
if (qib_krcvq01_no_msi && ctxt < 2)
continue;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dca = 1;
+#endif
lsb = QIB_I_RCVAVAIL_LSB + ctxt;
handler = qib_7322pintr;
snprintf(dd->cspec->msix_entries[msixnum].name,
@@ -3203,6 +3533,11 @@
goto try_intx;
}
dd->cspec->msix_entries[msixnum].arg = arg;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dd->cspec->msix_entries[msixnum].dca = dca;
+ dd->cspec->msix_entries[msixnum].rcv =
+ handler == qib_7322pintr;
+#endif
if (lsb >= 0) {
reg = lsb / IBA7322_REDIRECT_VEC_PER_REG;
sh = (lsb % IBA7322_REDIRECT_VEC_PER_REG) *
@@ -6452,6 +6787,86 @@
qib_write_kreg_port(ppd, krp_senddmadesccnt, cnt);
}
+/*
+ * sdma_lock should be acquired before calling this routine
+ */
+static void dump_sdma_7322_state(struct qib_pportdata *ppd)
+{
+ u64 reg, reg1, reg2;
+
+ reg = qib_read_kreg_port(ppd, krp_senddmastatus);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmastatus: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_sendctrl);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA sendctrl: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmabase);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmabase: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmabufmask0);
+ reg1 = qib_read_kreg_port(ppd, krp_senddmabufmask1);
+ reg2 = qib_read_kreg_port(ppd, krp_senddmabufmask2);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmabufmask 0:%llx 1:%llx 2:%llx\n",
+ reg, reg1, reg2);
+
+ /* get bufuse bits, clear them, and print them again if non-zero */
+ reg = qib_read_kreg_port(ppd, krp_senddmabuf_use0);
+ qib_write_kreg_port(ppd, krp_senddmabuf_use0, reg);
+ reg1 = qib_read_kreg_port(ppd, krp_senddmabuf_use1);
+ qib_write_kreg_port(ppd, krp_senddmabuf_use0, reg1);
+ reg2 = qib_read_kreg_port(ppd, krp_senddmabuf_use2);
+ qib_write_kreg_port(ppd, krp_senddmabuf_use0, reg2);
+ /* 0 and 1 should always be zero, so print as short form */
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA current senddmabuf_use 0:%llx 1:%llx 2:%llx\n",
+ reg, reg1, reg2);
+ reg = qib_read_kreg_port(ppd, krp_senddmabuf_use0);
+ reg1 = qib_read_kreg_port(ppd, krp_senddmabuf_use1);
+ reg2 = qib_read_kreg_port(ppd, krp_senddmabuf_use2);
+ /* 0 and 1 should always be zero, so print as short form */
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA cleared senddmabuf_use 0:%llx 1:%llx 2:%llx\n",
+ reg, reg1, reg2);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmatail);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmatail: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmahead);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmahead: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmaheadaddr);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmaheadaddr: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmalengen);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmalengen: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmadesccnt);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmadesccnt: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmaidlecnt);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmaidlecnt: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmaprioritythld);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmapriorityhld: 0x%016llx\n", reg);
+
+ reg = qib_read_kreg_port(ppd, krp_senddmareloadcnt);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA senddmareloadcnt: 0x%016llx\n", reg);
+
+ dump_sdma_state(ppd);
+}
+
static struct sdma_set_state_action sdma_7322_action_table[] = {
[qib_sdma_state_s00_hw_down] = {
.go_s99_running_tofalse = 1,
@@ -6885,6 +7300,9 @@
dd->f_sdma_init_early = qib_7322_sdma_init_early;
dd->f_writescratch = writescratch;
dd->f_tempsense_rd = qib_7322_tempsense_rd;
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dd->f_notify_dca = qib_7322_notify_dca;
+#endif
/*
* Do remaining PCIe setup and save PCIe values in dd.
* Any error printing is already done by the init code.
@@ -6921,7 +7339,7 @@
actual_cnt -= dd->num_pports;
tabsize = actual_cnt;
- dd->cspec->msix_entries = kmalloc(tabsize *
+ dd->cspec->msix_entries = kzalloc(tabsize *
sizeof(struct qib_msix_entry), GFP_KERNEL);
if (!dd->cspec->msix_entries) {
qib_dev_err(dd, "No memory for MSIx table\n");
@@ -6941,7 +7359,13 @@
/* clear diagctrl register, in case diags were running and crashed */
qib_write_kreg(dd, kr_hwdiagctrl, 0);
-
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ if (!dca_add_requester(&pdev->dev)) {
+ qib_devinfo(dd->pcidev, "DCA enabled\n");
+ dd->flags |= QIB_DCA_ENABLED;
+ qib_setup_dca(dd);
+ }
+#endif
goto bail;
bail_cleanup:
@@ -7156,15 +7580,20 @@
{ 0, 0, 0, 1 }, /* QMH7342 backplane settings */
{ 0, 0, 0, 2 }, /* QMH7342 backplane settings */
{ 0, 0, 0, 2 }, /* QMH7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
- { 0, 0, 0, 11 }, /* QME7342 backplane settings */
{ 0, 0, 0, 3 }, /* QMH7342 backplane settings */
{ 0, 0, 0, 4 }, /* QMH7342 backplane settings */
+ { 0, 1, 4, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 3, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 12 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 11 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 9 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 14 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 2, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 11 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 7 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 9 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 6 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 8 }, /* QME7342 backplane settings 1.1 */
};
static const struct txdds_ent txdds_extra_ddr[TXDDS_EXTRA_SZ] = {
@@ -7173,15 +7602,20 @@
{ 0, 0, 0, 7 }, /* QMH7342 backplane settings */
{ 0, 0, 0, 8 }, /* QMH7342 backplane settings */
{ 0, 0, 0, 8 }, /* QMH7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
- { 0, 0, 0, 13 }, /* QME7342 backplane settings */
{ 0, 0, 0, 9 }, /* QMH7342 backplane settings */
{ 0, 0, 0, 10 }, /* QMH7342 backplane settings */
+ { 0, 1, 4, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 3, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 12 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 11 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 9 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 14 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 2, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 11 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 7 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 9 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 6 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 8 }, /* QME7342 backplane settings 1.1 */
};
static const struct txdds_ent txdds_extra_qdr[TXDDS_EXTRA_SZ] = {
@@ -7190,15 +7624,20 @@
{ 0, 1, 0, 5 }, /* QMH7342 backplane settings */
{ 0, 1, 0, 6 }, /* QMH7342 backplane settings */
{ 0, 1, 0, 8 }, /* QMH7342 backplane settings */
- { 0, 1, 12, 10 }, /* QME7342 backplane setting */
- { 0, 1, 12, 11 }, /* QME7342 backplane setting */
- { 0, 1, 12, 12 }, /* QME7342 backplane setting */
- { 0, 1, 12, 14 }, /* QME7342 backplane setting */
- { 0, 1, 12, 6 }, /* QME7342 backplane setting */
- { 0, 1, 12, 7 }, /* QME7342 backplane setting */
- { 0, 1, 12, 8 }, /* QME7342 backplane setting */
{ 0, 1, 0, 10 }, /* QMH7342 backplane settings */
{ 0, 1, 0, 12 }, /* QMH7342 backplane settings */
+ { 0, 1, 4, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 3, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 12 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 11 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 9 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 14 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 2, 15 }, /* QME7342 backplane settings 1.0 */
+ { 0, 1, 0, 11 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 7 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 9 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 6 }, /* QME7342 backplane settings 1.1 */
+ { 0, 1, 0, 8 }, /* QME7342 backplane settings 1.1 */
};
static const struct txdds_ent txdds_extra_mfg[TXDDS_MFG_SZ] = {
diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c
index 173f805..36e048e 100644
--- a/drivers/infiniband/hw/qib/qib_init.c
+++ b/drivers/infiniband/hw/qib/qib_init.c
@@ -39,10 +39,17 @@
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/printk.h>
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+#include <linux/dca.h>
+#endif
#include "qib.h"
#include "qib_common.h"
#include "qib_mad.h"
+#ifdef CONFIG_DEBUG_FS
+#include "qib_debugfs.h"
+#include "qib_verbs.h"
+#endif
#undef pr_fmt
#define pr_fmt(fmt) QIB_DRV_NAME ": " fmt
@@ -64,6 +71,11 @@
module_param_named(cfgctxts, qib_cfgctxts, ushort, S_IRUGO);
MODULE_PARM_DESC(cfgctxts, "Set max number of contexts to use");
+unsigned qib_numa_aware;
+module_param_named(numa_aware, qib_numa_aware, uint, S_IRUGO);
+MODULE_PARM_DESC(numa_aware,
+ "0 -> PSM allocation close to HCA, 1 -> PSM allocation local to process");
+
/*
* If set, do not write to any regs if avoidable, hack to allow
* check for deranged default register values.
@@ -89,8 +101,6 @@
module_param_named(wc_pat, qib_wc_pat, uint, S_IRUGO);
MODULE_PARM_DESC(wc_pat, "enable write-combining via PAT mechanism");
-struct workqueue_struct *qib_cq_wq;
-
static void verify_interrupt(unsigned long);
static struct idr qib_unit_table;
@@ -121,6 +131,11 @@
{
unsigned i;
int ret;
+ int local_node_id = pcibus_to_node(dd->pcidev->bus);
+
+ if (local_node_id < 0)
+ local_node_id = numa_node_id();
+ dd->assigned_node_id = local_node_id;
/*
* Allocate full ctxtcnt array, rather than just cfgctxts, because
@@ -143,7 +158,8 @@
continue;
ppd = dd->pport + (i % dd->num_pports);
- rcd = qib_create_ctxtdata(ppd, i);
+
+ rcd = qib_create_ctxtdata(ppd, i, dd->assigned_node_id);
if (!rcd) {
qib_dev_err(dd,
"Unable to allocate ctxtdata for Kernel ctxt, failing\n");
@@ -161,20 +177,33 @@
/*
* Common code for user and kernel context setup.
*/
-struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *ppd, u32 ctxt)
+struct qib_ctxtdata *qib_create_ctxtdata(struct qib_pportdata *ppd, u32 ctxt,
+ int node_id)
{
struct qib_devdata *dd = ppd->dd;
struct qib_ctxtdata *rcd;
- rcd = kzalloc(sizeof(*rcd), GFP_KERNEL);
+ rcd = kzalloc_node(sizeof(*rcd), GFP_KERNEL, node_id);
if (rcd) {
INIT_LIST_HEAD(&rcd->qp_wait_list);
+ rcd->node_id = node_id;
rcd->ppd = ppd;
rcd->dd = dd;
rcd->cnt = 1;
rcd->ctxt = ctxt;
dd->rcd[ctxt] = rcd;
-
+#ifdef CONFIG_DEBUG_FS
+ if (ctxt < dd->first_user_ctxt) { /* N/A for PSM contexts */
+ rcd->opstats = kzalloc_node(sizeof(*rcd->opstats),
+ GFP_KERNEL, node_id);
+ if (!rcd->opstats) {
+ kfree(rcd);
+ qib_dev_err(dd,
+ "Unable to allocate per ctxt stats buffer\n");
+ return NULL;
+ }
+ }
+#endif
dd->f_init_ctxt(rcd);
/*
@@ -429,6 +458,7 @@
dd->intrchk_timer.function = verify_interrupt;
dd->intrchk_timer.data = (unsigned long) dd;
+ ret = qib_cq_init(dd);
done:
return ret;
}
@@ -944,6 +974,10 @@
vfree(rcd->subctxt_uregbase);
vfree(rcd->subctxt_rcvegrbuf);
vfree(rcd->subctxt_rcvhdr_base);
+#ifdef CONFIG_DEBUG_FS
+ kfree(rcd->opstats);
+ rcd->opstats = NULL;
+#endif
kfree(rcd);
}
@@ -1033,7 +1067,6 @@
dd->f_set_armlaunch(dd, 1);
}
-
void qib_free_devdata(struct qib_devdata *dd)
{
unsigned long flags;
@@ -1043,6 +1076,9 @@
list_del(&dd->list);
spin_unlock_irqrestore(&qib_devs_lock, flags);
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_ibdev_exit(&dd->verbs_dev);
+#endif
ib_dealloc_device(&dd->verbs_dev.ibdev);
}
@@ -1066,6 +1102,10 @@
goto bail;
}
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_ibdev_init(&dd->verbs_dev);
+#endif
+
idr_preload(GFP_KERNEL);
spin_lock_irqsave(&qib_devs_lock, flags);
@@ -1081,6 +1121,9 @@
if (ret < 0) {
qib_early_err(&pdev->dev,
"Could not allocate unit ID: error %d\n", -ret);
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_ibdev_exit(&dd->verbs_dev);
+#endif
ib_dealloc_device(&dd->verbs_dev.ibdev);
dd = ERR_PTR(ret);
goto bail;
@@ -1158,6 +1201,35 @@
.err_handler = &qib_pci_err_handler,
};
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+
+static int qib_notify_dca(struct notifier_block *, unsigned long, void *);
+static struct notifier_block dca_notifier = {
+ .notifier_call = qib_notify_dca,
+ .next = NULL,
+ .priority = 0
+};
+
+static int qib_notify_dca_device(struct device *device, void *data)
+{
+ struct qib_devdata *dd = dev_get_drvdata(device);
+ unsigned long event = *(unsigned long *)data;
+
+ return dd->f_notify_dca(dd, event);
+}
+
+static int qib_notify_dca(struct notifier_block *nb, unsigned long event,
+ void *p)
+{
+ int rval;
+
+ rval = driver_for_each_device(&qib_driver.driver, NULL,
+ &event, qib_notify_dca_device);
+ return rval ? NOTIFY_BAD : NOTIFY_DONE;
+}
+
+#endif
+
/*
* Do all the generic driver unit- and chip-independent memory
* allocation and initialization.
@@ -1170,22 +1242,22 @@
if (ret)
goto bail;
- qib_cq_wq = create_singlethread_workqueue("qib_cq");
- if (!qib_cq_wq) {
- ret = -ENOMEM;
- goto bail_dev;
- }
-
/*
* These must be called before the driver is registered with
* the PCI subsystem.
*/
idr_init(&qib_unit_table);
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dca_register_notify(&dca_notifier);
+#endif
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_init();
+#endif
ret = pci_register_driver(&qib_driver);
if (ret < 0) {
pr_err("Unable to register driver: error %d\n", -ret);
- goto bail_unit;
+ goto bail_dev;
}
/* not fatal if it doesn't work */
@@ -1193,10 +1265,14 @@
pr_err("Unable to register ipathfs\n");
goto bail; /* all OK */
-bail_unit:
- idr_destroy(&qib_unit_table);
- destroy_workqueue(qib_cq_wq);
bail_dev:
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dca_unregister_notify(&dca_notifier);
+#endif
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_exit();
+#endif
+ idr_destroy(&qib_unit_table);
qib_dev_cleanup();
bail:
return ret;
@@ -1217,9 +1293,13 @@
"Unable to cleanup counter filesystem: error %d\n",
-ret);
+#ifdef CONFIG_INFINIBAND_QIB_DCA
+ dca_unregister_notify(&dca_notifier);
+#endif
pci_unregister_driver(&qib_driver);
-
- destroy_workqueue(qib_cq_wq);
+#ifdef CONFIG_DEBUG_FS
+ qib_dbg_exit();
+#endif
qib_cpulist_count = 0;
kfree(qib_cpulist);
@@ -1270,7 +1350,7 @@
if (dd->pageshadow) {
struct page **tmpp = dd->pageshadow;
dma_addr_t *tmpd = dd->physshadow;
- int i, cnt = 0;
+ int i;
for (ctxt = 0; ctxt < dd->cfgctxts; ctxt++) {
int ctxt_tidbase = ctxt * dd->rcvtidcnt;
@@ -1283,13 +1363,13 @@
PAGE_SIZE, PCI_DMA_FROMDEVICE);
qib_release_user_pages(&tmpp[i], 1);
tmpp[i] = NULL;
- cnt++;
}
}
- tmpp = dd->pageshadow;
dd->pageshadow = NULL;
vfree(tmpp);
+ dd->physshadow = NULL;
+ vfree(tmpd);
}
/*
@@ -1311,6 +1391,7 @@
}
kfree(tmp);
kfree(dd->boardname);
+ qib_cq_exit(dd);
}
/*
@@ -1483,6 +1564,7 @@
int qib_create_rcvhdrq(struct qib_devdata *dd, struct qib_ctxtdata *rcd)
{
unsigned amt;
+ int old_node_id;
if (!rcd->rcvhdrq) {
dma_addr_t phys_hdrqtail;
@@ -1492,9 +1574,13 @@
sizeof(u32), PAGE_SIZE);
gfp_flags = (rcd->ctxt >= dd->first_user_ctxt) ?
GFP_USER : GFP_KERNEL;
+
+ old_node_id = dev_to_node(&dd->pcidev->dev);
+ set_dev_node(&dd->pcidev->dev, rcd->node_id);
rcd->rcvhdrq = dma_alloc_coherent(
&dd->pcidev->dev, amt, &rcd->rcvhdrq_phys,
gfp_flags | __GFP_COMP);
+ set_dev_node(&dd->pcidev->dev, old_node_id);
if (!rcd->rcvhdrq) {
qib_dev_err(dd,
@@ -1510,9 +1596,11 @@
}
if (!(dd->flags & QIB_NODMA_RTAIL)) {
+ set_dev_node(&dd->pcidev->dev, rcd->node_id);
rcd->rcvhdrtail_kvaddr = dma_alloc_coherent(
&dd->pcidev->dev, PAGE_SIZE, &phys_hdrqtail,
gfp_flags);
+ set_dev_node(&dd->pcidev->dev, old_node_id);
if (!rcd->rcvhdrtail_kvaddr)
goto bail_free;
rcd->rcvhdrqtailaddr_phys = phys_hdrqtail;
@@ -1556,6 +1644,7 @@
unsigned e, egrcnt, egrperchunk, chunk, egrsize, egroff;
size_t size;
gfp_t gfp_flags;
+ int old_node_id;
/*
* GFP_USER, but without GFP_FS, so buffer cache can be
@@ -1574,25 +1663,29 @@
size = rcd->rcvegrbuf_size;
if (!rcd->rcvegrbuf) {
rcd->rcvegrbuf =
- kzalloc(chunk * sizeof(rcd->rcvegrbuf[0]),
- GFP_KERNEL);
+ kzalloc_node(chunk * sizeof(rcd->rcvegrbuf[0]),
+ GFP_KERNEL, rcd->node_id);
if (!rcd->rcvegrbuf)
goto bail;
}
if (!rcd->rcvegrbuf_phys) {
rcd->rcvegrbuf_phys =
- kmalloc(chunk * sizeof(rcd->rcvegrbuf_phys[0]),
- GFP_KERNEL);
+ kmalloc_node(chunk * sizeof(rcd->rcvegrbuf_phys[0]),
+ GFP_KERNEL, rcd->node_id);
if (!rcd->rcvegrbuf_phys)
goto bail_rcvegrbuf;
}
for (e = 0; e < rcd->rcvegrbuf_chunks; e++) {
if (rcd->rcvegrbuf[e])
continue;
+
+ old_node_id = dev_to_node(&dd->pcidev->dev);
+ set_dev_node(&dd->pcidev->dev, rcd->node_id);
rcd->rcvegrbuf[e] =
dma_alloc_coherent(&dd->pcidev->dev, size,
&rcd->rcvegrbuf_phys[e],
gfp_flags);
+ set_dev_node(&dd->pcidev->dev, old_node_id);
if (!rcd->rcvegrbuf[e])
goto bail_rcvegrbuf_phys;
}
diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c
index a6a2cc2..3cca55b 100644
--- a/drivers/infiniband/hw/qib/qib_qp.c
+++ b/drivers/infiniband/hw/qib/qib_qp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2012, 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006 - 2012 QLogic Corporation. * All rights reserved.
* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
*
@@ -35,6 +35,9 @@
#include <linux/err.h>
#include <linux/vmalloc.h>
#include <linux/jhash.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+#endif
#include "qib.h"
@@ -222,8 +225,8 @@
unsigned long flags;
unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
- spin_lock_irqsave(&dev->qpt_lock, flags);
atomic_inc(&qp->refcount);
+ spin_lock_irqsave(&dev->qpt_lock, flags);
if (qp->ibqp.qp_num == 0)
rcu_assign_pointer(ibp->qp0, qp);
@@ -235,7 +238,6 @@
}
spin_unlock_irqrestore(&dev->qpt_lock, flags);
- synchronize_rcu();
}
/*
@@ -247,36 +249,39 @@
struct qib_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
unsigned n = qpn_hash(dev, qp->ibqp.qp_num);
unsigned long flags;
+ int removed = 1;
spin_lock_irqsave(&dev->qpt_lock, flags);
if (rcu_dereference_protected(ibp->qp0,
lockdep_is_held(&dev->qpt_lock)) == qp) {
- atomic_dec(&qp->refcount);
rcu_assign_pointer(ibp->qp0, NULL);
} else if (rcu_dereference_protected(ibp->qp1,
lockdep_is_held(&dev->qpt_lock)) == qp) {
- atomic_dec(&qp->refcount);
rcu_assign_pointer(ibp->qp1, NULL);
} else {
struct qib_qp *q;
struct qib_qp __rcu **qpp;
+ removed = 0;
qpp = &dev->qp_table[n];
for (; (q = rcu_dereference_protected(*qpp,
lockdep_is_held(&dev->qpt_lock))) != NULL;
qpp = &q->next)
if (q == qp) {
- atomic_dec(&qp->refcount);
rcu_assign_pointer(*qpp,
rcu_dereference_protected(qp->next,
lockdep_is_held(&dev->qpt_lock)));
+ removed = 1;
break;
}
}
spin_unlock_irqrestore(&dev->qpt_lock, flags);
- synchronize_rcu();
+ if (removed) {
+ synchronize_rcu();
+ atomic_dec(&qp->refcount);
+ }
}
/**
@@ -334,26 +339,25 @@
{
struct qib_qp *qp = NULL;
+ rcu_read_lock();
if (unlikely(qpn <= 1)) {
- rcu_read_lock();
if (qpn == 0)
qp = rcu_dereference(ibp->qp0);
else
qp = rcu_dereference(ibp->qp1);
+ if (qp)
+ atomic_inc(&qp->refcount);
} else {
struct qib_ibdev *dev = &ppd_from_ibp(ibp)->dd->verbs_dev;
unsigned n = qpn_hash(dev, qpn);
- rcu_read_lock();
for (qp = rcu_dereference(dev->qp_table[n]); qp;
qp = rcu_dereference(qp->next))
- if (qp->ibqp.qp_num == qpn)
+ if (qp->ibqp.qp_num == qpn) {
+ atomic_inc(&qp->refcount);
break;
+ }
}
- if (qp)
- if (unlikely(!atomic_inc_not_zero(&qp->refcount)))
- qp = NULL;
-
rcu_read_unlock();
return qp;
}
@@ -1286,3 +1290,94 @@
}
}
}
+
+#ifdef CONFIG_DEBUG_FS
+
+struct qib_qp_iter {
+ struct qib_ibdev *dev;
+ struct qib_qp *qp;
+ int n;
+};
+
+struct qib_qp_iter *qib_qp_iter_init(struct qib_ibdev *dev)
+{
+ struct qib_qp_iter *iter;
+
+ iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+ if (!iter)
+ return NULL;
+
+ iter->dev = dev;
+ if (qib_qp_iter_next(iter)) {
+ kfree(iter);
+ return NULL;
+ }
+
+ return iter;
+}
+
+int qib_qp_iter_next(struct qib_qp_iter *iter)
+{
+ struct qib_ibdev *dev = iter->dev;
+ int n = iter->n;
+ int ret = 1;
+ struct qib_qp *pqp = iter->qp;
+ struct qib_qp *qp;
+
+ rcu_read_lock();
+ for (; n < dev->qp_table_size; n++) {
+ if (pqp)
+ qp = rcu_dereference(pqp->next);
+ else
+ qp = rcu_dereference(dev->qp_table[n]);
+ pqp = qp;
+ if (qp) {
+ if (iter->qp)
+ atomic_dec(&iter->qp->refcount);
+ atomic_inc(&qp->refcount);
+ rcu_read_unlock();
+ iter->qp = qp;
+ iter->n = n;
+ return 0;
+ }
+ }
+ rcu_read_unlock();
+ if (iter->qp)
+ atomic_dec(&iter->qp->refcount);
+ return ret;
+}
+
+static const char * const qp_type_str[] = {
+ "SMI", "GSI", "RC", "UC", "UD",
+};
+
+void qib_qp_iter_print(struct seq_file *s, struct qib_qp_iter *iter)
+{
+ struct qib_swqe *wqe;
+ struct qib_qp *qp = iter->qp;
+
+ wqe = get_swqe_ptr(qp, qp->s_last);
+ seq_printf(s,
+ "N %d QP%u %s %u %u %u f=%x %u %u %u %u %u PSN %x %x %x %x %x (%u %u %u %u %u %u) QP%u LID %x\n",
+ iter->n,
+ qp->ibqp.qp_num,
+ qp_type_str[qp->ibqp.qp_type],
+ qp->state,
+ wqe->wr.opcode,
+ qp->s_hdrwords,
+ qp->s_flags,
+ atomic_read(&qp->s_dma_busy),
+ !list_empty(&qp->iowait),
+ qp->timeout,
+ wqe->ssn,
+ qp->s_lsn,
+ qp->s_last_psn,
+ qp->s_psn, qp->s_next_psn,
+ qp->s_sending_psn, qp->s_sending_hpsn,
+ qp->s_last, qp->s_acked, qp->s_cur,
+ qp->s_tail, qp->s_head, qp->s_size,
+ qp->remote_qpn,
+ qp->remote_ah_attr.dlid);
+}
+
+#endif
diff --git a/drivers/infiniband/hw/qib/qib_sdma.c b/drivers/infiniband/hw/qib/qib_sdma.c
index 3fc5144..32162d3 100644
--- a/drivers/infiniband/hw/qib/qib_sdma.c
+++ b/drivers/infiniband/hw/qib/qib_sdma.c
@@ -708,6 +708,62 @@
return ret;
}
+/*
+ * sdma_lock should be acquired before calling this routine
+ */
+void dump_sdma_state(struct qib_pportdata *ppd)
+{
+ struct qib_sdma_desc *descq;
+ struct qib_sdma_txreq *txp, *txpnext;
+ __le64 *descqp;
+ u64 desc[2];
+ dma_addr_t addr;
+ u16 gen, dwlen, dwoffset;
+ u16 head, tail, cnt;
+
+ head = ppd->sdma_descq_head;
+ tail = ppd->sdma_descq_tail;
+ cnt = qib_sdma_descq_freecnt(ppd);
+ descq = ppd->sdma_descq;
+
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA ppd->sdma_descq_head: %u\n", head);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA ppd->sdma_descq_tail: %u\n", tail);
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA sdma_descq_freecnt: %u\n", cnt);
+
+ /* print info for each entry in the descriptor queue */
+ while (head != tail) {
+ char flags[6] = { 'x', 'x', 'x', 'x', 'x', 0 };
+
+ descqp = &descq[head].qw[0];
+ desc[0] = le64_to_cpu(descqp[0]);
+ desc[1] = le64_to_cpu(descqp[1]);
+ flags[0] = (desc[0] & 1<<15) ? 'I' : '-';
+ flags[1] = (desc[0] & 1<<14) ? 'L' : 'S';
+ flags[2] = (desc[0] & 1<<13) ? 'H' : '-';
+ flags[3] = (desc[0] & 1<<12) ? 'F' : '-';
+ flags[4] = (desc[0] & 1<<11) ? 'L' : '-';
+ addr = (desc[1] << 32) | ((desc[0] >> 32) & 0xfffffffcULL);
+ gen = (desc[0] >> 30) & 3ULL;
+ dwlen = (desc[0] >> 14) & (0x7ffULL << 2);
+ dwoffset = (desc[0] & 0x7ffULL) << 2;
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA sdmadesc[%u]: flags:%s addr:0x%016llx gen:%u len:%u bytes offset:%u bytes\n",
+ head, flags, addr, gen, dwlen, dwoffset);
+ if (++head == ppd->sdma_descq_cnt)
+ head = 0;
+ }
+
+ /* print dma descriptor indices from the TX requests */
+ list_for_each_entry_safe(txp, txpnext, &ppd->sdma_activelist,
+ list)
+ qib_dev_porterr(ppd->dd, ppd->port,
+ "SDMA txp->start_idx: %u txp->next_descq_idx: %u\n",
+ txp->start_idx, txp->next_descq_idx);
+}
+
void qib_sdma_process_event(struct qib_pportdata *ppd,
enum qib_sdma_events event)
{
diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c
index 904c384..092b0bb 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.c
+++ b/drivers/infiniband/hw/qib/qib_verbs.c
@@ -645,9 +645,11 @@
} else
goto drop;
- opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
- ibp->opstats[opcode & 0x7f].n_bytes += tlen;
- ibp->opstats[opcode & 0x7f].n_packets++;
+ opcode = (be32_to_cpu(ohdr->bth[0]) >> 24) & 0x7f;
+#ifdef CONFIG_DEBUG_FS
+ rcd->opstats->stats[opcode].n_bytes += tlen;
+ rcd->opstats->stats[opcode].n_packets++;
+#endif
/* Get the destination QP number. */
qp_num = be32_to_cpu(ohdr->bth[1]) & QIB_QPN_MASK;
diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h
index aff8b2c..012e2c7 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.h
+++ b/drivers/infiniband/hw/qib/qib_verbs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2012, 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
*
@@ -41,6 +41,7 @@
#include <linux/interrupt.h>
#include <linux/kref.h>
#include <linux/workqueue.h>
+#include <linux/kthread.h>
#include <linux/completion.h>
#include <rdma/ib_pack.h>
#include <rdma/ib_user_verbs.h>
@@ -267,7 +268,8 @@
*/
struct qib_cq {
struct ib_cq ibcq;
- struct work_struct comptask;
+ struct kthread_work comptask;
+ struct qib_devdata *dd;
spinlock_t lock; /* protect changes in this struct */
u8 notify;
u8 triggered;
@@ -658,6 +660,10 @@
u64 n_bytes; /* total number of bytes */
};
+struct qib_opcode_stats_perctx {
+ struct qib_opcode_stats stats[128];
+};
+
struct qib_ibport {
struct qib_qp __rcu *qp0;
struct qib_qp __rcu *qp1;
@@ -724,7 +730,6 @@
u8 vl_high_limit;
u8 sl_to_vl[16];
- struct qib_opcode_stats opstats[128];
};
@@ -768,6 +773,10 @@
spinlock_t n_srqs_lock;
u32 n_mcast_grps_allocated; /* number of mcast groups allocated */
spinlock_t n_mcast_grps_lock;
+#ifdef CONFIG_DEBUG_FS
+ /* per HCA debugfs */
+ struct dentry *qib_ibdev_dbg;
+#endif
};
struct qib_verbs_counters {
@@ -832,8 +841,6 @@
!(qp->s_flags & QIB_S_ANY_WAIT_SEND));
}
-extern struct workqueue_struct *qib_cq_wq;
-
/*
* This must be called with s_lock held.
*/
@@ -910,6 +917,18 @@
void qib_free_qpn_table(struct qib_qpn_table *qpt);
+#ifdef CONFIG_DEBUG_FS
+
+struct qib_qp_iter;
+
+struct qib_qp_iter *qib_qp_iter_init(struct qib_ibdev *dev);
+
+int qib_qp_iter_next(struct qib_qp_iter *iter);
+
+void qib_qp_iter_print(struct seq_file *s, struct qib_qp_iter *iter);
+
+#endif
+
void qib_get_credit(struct qib_qp *qp, u32 aeth);
unsigned qib_pkt_delay(u32 plen, u8 snd_mult, u8 rcv_mult);
@@ -972,6 +991,10 @@
int qib_destroy_srq(struct ib_srq *ibsrq);
+int qib_cq_init(struct qib_devdata *dd);
+
+void qib_cq_exit(struct qib_devdata *dd);
+
void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int sig);
int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 2693129..3f62041 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -388,6 +388,7 @@
init_waitqueue_head(&isert_conn->conn_wait_comp_err);
kref_init(&isert_conn->conn_kref);
kref_get(&isert_conn->conn_kref);
+ mutex_init(&isert_conn->conn_mutex);
cma_id->context = isert_conn;
isert_conn->conn_cm_id = cma_id;
@@ -540,15 +541,32 @@
struct isert_conn, conn_logout_work);
pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-
+ mutex_lock(&isert_conn->conn_mutex);
isert_conn->state = ISER_CONN_DOWN;
if (isert_conn->post_recv_buf_count == 0 &&
atomic_read(&isert_conn->post_send_buf_count) == 0) {
pr_debug("Calling wake_up(&isert_conn->conn_wait);\n");
- wake_up(&isert_conn->conn_wait);
+ mutex_unlock(&isert_conn->conn_mutex);
+ goto wake_up;
}
+ if (!isert_conn->conn_cm_id) {
+ mutex_unlock(&isert_conn->conn_mutex);
+ isert_put_conn(isert_conn);
+ return;
+ }
+ if (!isert_conn->logout_posted) {
+ pr_debug("Calling rdma_disconnect for !logout_posted from"
+ " isert_disconnect_work\n");
+ rdma_disconnect(isert_conn->conn_cm_id);
+ mutex_unlock(&isert_conn->conn_mutex);
+ iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
+ goto wake_up;
+ }
+ mutex_unlock(&isert_conn->conn_mutex);
+wake_up:
+ wake_up(&isert_conn->conn_wait);
isert_put_conn(isert_conn);
}
@@ -934,16 +952,11 @@
}
sequence_cmd:
- rc = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+ rc = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
if (!rc && dump_payload == false && unsol_data)
iscsit_set_unsoliticed_dataout(cmd);
- if (rc == CMDSN_ERROR_CANNOT_RECOVER)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, (unsigned char *)hdr, cmd);
-
return 0;
}
@@ -1001,17 +1014,71 @@
}
static int
+isert_handle_nop_out(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
+ struct iser_rx_desc *rx_desc, unsigned char *buf)
+{
+ struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd;
+ struct iscsi_conn *conn = isert_conn->conn;
+ struct iscsi_nopout *hdr = (struct iscsi_nopout *)buf;
+ int rc;
+
+ rc = iscsit_setup_nop_out(conn, cmd, hdr);
+ if (rc < 0)
+ return rc;
+ /*
+ * FIXME: Add support for NOPOUT payload using unsolicited RDMA payload
+ */
+
+ return iscsit_process_nop_out(conn, cmd, hdr);
+}
+
+static int
+isert_handle_text_cmd(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
+ struct iser_rx_desc *rx_desc, struct iscsi_text *hdr)
+{
+ struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd;
+ struct iscsi_conn *conn = isert_conn->conn;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int rc;
+ unsigned char *text_in;
+
+ rc = iscsit_setup_text_cmd(conn, cmd, hdr);
+ if (rc < 0)
+ return rc;
+
+ text_in = kzalloc(payload_length, GFP_KERNEL);
+ if (!text_in) {
+ pr_err("Unable to allocate text_in of payload_length: %u\n",
+ payload_length);
+ return -ENOMEM;
+ }
+ cmd->text_in_ptr = text_in;
+
+ memcpy(cmd->text_in_ptr, &rx_desc->data[0], payload_length);
+
+ return iscsit_process_text_cmd(conn, cmd, hdr);
+}
+
+static int
isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc,
uint32_t read_stag, uint64_t read_va,
uint32_t write_stag, uint64_t write_va)
{
struct iscsi_hdr *hdr = &rx_desc->iscsi_header;
struct iscsi_conn *conn = isert_conn->conn;
+ struct iscsi_session *sess = conn->sess;
struct iscsi_cmd *cmd;
struct isert_cmd *isert_cmd;
int ret = -EINVAL;
u8 opcode = (hdr->opcode & ISCSI_OPCODE_MASK);
+ if (sess->sess_ops->SessionType &&
+ (!(opcode & ISCSI_OP_TEXT) || !(opcode & ISCSI_OP_LOGOUT))) {
+ pr_err("Got illegal opcode: 0x%02x in SessionType=Discovery,"
+ " ignoring\n", opcode);
+ return 0;
+ }
+
switch (opcode) {
case ISCSI_OP_SCSI_CMD:
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
@@ -1032,7 +1099,9 @@
if (!cmd)
break;
- ret = iscsit_handle_nop_out(conn, cmd, (unsigned char *)hdr);
+ isert_cmd = container_of(cmd, struct isert_cmd, iscsi_cmd);
+ ret = isert_handle_nop_out(isert_conn, isert_cmd,
+ rx_desc, (unsigned char *)hdr);
break;
case ISCSI_OP_SCSI_DATA_OUT:
ret = isert_handle_iscsi_dataout(isert_conn, rx_desc,
@@ -1057,6 +1126,15 @@
SECONDS_FOR_LOGOUT_COMP *
HZ);
break;
+ case ISCSI_OP_TEXT:
+ cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+ if (!cmd)
+ break;
+
+ isert_cmd = container_of(cmd, struct isert_cmd, iscsi_cmd);
+ ret = isert_handle_text_cmd(isert_conn, isert_cmd,
+ rx_desc, (struct iscsi_text *)hdr);
+ break;
default:
pr_err("Got unknown iSCSI OpCode: 0x%02x\n", opcode);
dump_stack();
@@ -1184,14 +1262,12 @@
{
struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd;
struct isert_conn *isert_conn = isert_cmd->conn;
- struct iscsi_conn *conn;
+ struct iscsi_conn *conn = isert_conn->conn;
pr_debug("Entering isert_put_cmd: %p\n", isert_cmd);
switch (cmd->iscsi_opcode) {
case ISCSI_OP_SCSI_CMD:
- conn = isert_conn->conn;
-
spin_lock_bh(&conn->cmd_lock);
if (!list_empty(&cmd->i_conn_node))
list_del(&cmd->i_conn_node);
@@ -1201,16 +1277,19 @@
iscsit_stop_dataout_timer(cmd);
isert_unmap_cmd(isert_cmd, isert_conn);
- /*
- * Fall-through
- */
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+ break;
case ISCSI_OP_SCSI_TMFUNC:
+ spin_lock_bh(&conn->cmd_lock);
+ if (!list_empty(&cmd->i_conn_node))
+ list_del(&cmd->i_conn_node);
+ spin_unlock_bh(&conn->cmd_lock);
+
transport_generic_free_cmd(&cmd->se_cmd, 0);
break;
case ISCSI_OP_REJECT:
case ISCSI_OP_NOOP_OUT:
- conn = isert_conn->conn;
-
+ case ISCSI_OP_TEXT:
spin_lock_bh(&conn->cmd_lock);
if (!list_empty(&cmd->i_conn_node))
list_del(&cmd->i_conn_node);
@@ -1222,6 +1301,9 @@
* associated cmd->se_cmd needs to be released.
*/
if (cmd->se_cmd.se_tfo != NULL) {
+ pr_debug("Calling transport_generic_free_cmd from"
+ " isert_put_cmd for 0x%02x\n",
+ cmd->iscsi_opcode);
transport_generic_free_cmd(&cmd->se_cmd, 0);
break;
}
@@ -1249,11 +1331,11 @@
isert_completion_put(struct iser_tx_desc *tx_desc, struct isert_cmd *isert_cmd,
struct ib_device *ib_dev)
{
- if (isert_cmd->sense_buf_dma != 0) {
- pr_debug("Calling ib_dma_unmap_single for isert_cmd->sense_buf_dma\n");
- ib_dma_unmap_single(ib_dev, isert_cmd->sense_buf_dma,
- isert_cmd->sense_buf_len, DMA_TO_DEVICE);
- isert_cmd->sense_buf_dma = 0;
+ if (isert_cmd->pdu_buf_dma != 0) {
+ pr_debug("Calling ib_dma_unmap_single for isert_cmd->pdu_buf_dma\n");
+ ib_dma_unmap_single(ib_dev, isert_cmd->pdu_buf_dma,
+ isert_cmd->pdu_buf_len, DMA_TO_DEVICE);
+ isert_cmd->pdu_buf_dma = 0;
}
isert_unmap_tx_desc(tx_desc, ib_dev);
@@ -1318,8 +1400,8 @@
atomic_dec(&isert_conn->post_send_buf_count);
cmd->i_state = ISTATE_SENT_STATUS;
- complete(&cmd->reject_comp);
isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev);
+ break;
case ISTATE_SEND_LOGOUTRSP:
pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n");
/*
@@ -1329,6 +1411,11 @@
isert_conn->logout_posted = true;
iscsit_logout_post_handler(cmd, cmd->conn);
break;
+ case ISTATE_SEND_TEXTRSP:
+ atomic_dec(&isert_conn->post_send_buf_count);
+ cmd->i_state = ISTATE_SENT_STATUS;
+ isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev);
+ break;
default:
pr_err("Unknown do_control_comp i_state %d\n", cmd->i_state);
dump_stack();
@@ -1345,7 +1432,9 @@
struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd;
if (cmd->i_state == ISTATE_SEND_TASKMGTRSP ||
- cmd->i_state == ISTATE_SEND_LOGOUTRSP) {
+ cmd->i_state == ISTATE_SEND_LOGOUTRSP ||
+ cmd->i_state == ISTATE_SEND_REJECT ||
+ cmd->i_state == ISTATE_SEND_TEXTRSP) {
isert_unmap_tx_desc(tx_desc, ib_dev);
INIT_WORK(&isert_cmd->comp_work, isert_do_control_comp);
@@ -1419,7 +1508,11 @@
pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
pr_debug("Calling wake_up from isert_cq_comp_err\n");
- isert_conn->state = ISER_CONN_TERMINATING;
+ mutex_lock(&isert_conn->conn_mutex);
+ if (isert_conn->state != ISER_CONN_DOWN)
+ isert_conn->state = ISER_CONN_TERMINATING;
+ mutex_unlock(&isert_conn->conn_mutex);
+
wake_up(&isert_conn->conn_wait_comp_err);
}
}
@@ -1445,6 +1538,7 @@
} else {
pr_debug("TX wc.status != IB_WC_SUCCESS >>>>>>>>>>>>>>\n");
pr_debug("TX wc.status: 0x%08x\n", wc.status);
+ pr_debug("TX wc.vendor_err: 0x%08x\n", wc.vendor_err);
atomic_dec(&isert_conn->post_send_buf_count);
isert_cq_comp_err(tx_desc, isert_conn);
}
@@ -1484,9 +1578,11 @@
isert_rx_completion(rx_desc, isert_conn, xfer_len);
} else {
pr_debug("RX wc.status != IB_WC_SUCCESS >>>>>>>>>>>>>>\n");
- if (wc.status != IB_WC_WR_FLUSH_ERR)
+ if (wc.status != IB_WC_WR_FLUSH_ERR) {
pr_debug("RX wc.status: 0x%08x\n", wc.status);
-
+ pr_debug("RX wc.vendor_err: 0x%08x\n",
+ wc.vendor_err);
+ }
isert_conn->post_recv_buf_count--;
isert_cq_comp_err(NULL, isert_conn);
}
@@ -1543,7 +1639,7 @@
(cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
struct ib_sge *tx_dsg = &isert_cmd->tx_desc.tx_sg[1];
- u32 padding, sense_len;
+ u32 padding, pdu_len;
put_unaligned_be16(cmd->se_cmd.scsi_sense_length,
cmd->sense_buffer);
@@ -1551,15 +1647,15 @@
padding = -(cmd->se_cmd.scsi_sense_length) & 3;
hton24(hdr->dlength, (u32)cmd->se_cmd.scsi_sense_length);
- sense_len = cmd->se_cmd.scsi_sense_length + padding;
+ pdu_len = cmd->se_cmd.scsi_sense_length + padding;
- isert_cmd->sense_buf_dma = ib_dma_map_single(ib_dev,
- (void *)cmd->sense_buffer, sense_len,
+ isert_cmd->pdu_buf_dma = ib_dma_map_single(ib_dev,
+ (void *)cmd->sense_buffer, pdu_len,
DMA_TO_DEVICE);
- isert_cmd->sense_buf_len = sense_len;
- tx_dsg->addr = isert_cmd->sense_buf_dma;
- tx_dsg->length = sense_len;
+ isert_cmd->pdu_buf_len = pdu_len;
+ tx_dsg->addr = isert_cmd->pdu_buf_dma;
+ tx_dsg->length = pdu_len;
tx_dsg->lkey = isert_conn->conn_mr->lkey;
isert_cmd->tx_desc.num_sge = 2;
}
@@ -1637,11 +1733,25 @@
struct isert_cmd, iscsi_cmd);
struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
struct ib_send_wr *send_wr = &isert_cmd->tx_desc.send_wr;
+ struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+ struct ib_sge *tx_dsg = &isert_cmd->tx_desc.tx_sg[1];
+ struct iscsi_reject *hdr =
+ (struct iscsi_reject *)&isert_cmd->tx_desc.iscsi_header;
isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc);
- iscsit_build_reject(cmd, conn, (struct iscsi_reject *)
- &isert_cmd->tx_desc.iscsi_header);
+ iscsit_build_reject(cmd, conn, hdr);
isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
+
+ hton24(hdr->dlength, ISCSI_HDR_LEN);
+ isert_cmd->pdu_buf_dma = ib_dma_map_single(ib_dev,
+ (void *)cmd->buf_ptr, ISCSI_HDR_LEN,
+ DMA_TO_DEVICE);
+ isert_cmd->pdu_buf_len = ISCSI_HDR_LEN;
+ tx_dsg->addr = isert_cmd->pdu_buf_dma;
+ tx_dsg->length = ISCSI_HDR_LEN;
+ tx_dsg->lkey = isert_conn->conn_mr->lkey;
+ isert_cmd->tx_desc.num_sge = 2;
+
isert_init_send_wr(isert_cmd, send_wr);
pr_debug("Posting Reject IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
@@ -1650,6 +1760,47 @@
}
static int
+isert_put_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
+{
+ struct isert_cmd *isert_cmd = container_of(cmd,
+ struct isert_cmd, iscsi_cmd);
+ struct isert_conn *isert_conn = (struct isert_conn *)conn->context;
+ struct ib_send_wr *send_wr = &isert_cmd->tx_desc.send_wr;
+ struct iscsi_text_rsp *hdr =
+ (struct iscsi_text_rsp *)&isert_cmd->tx_desc.iscsi_header;
+ u32 txt_rsp_len;
+ int rc;
+
+ isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc);
+ rc = iscsit_build_text_rsp(cmd, conn, hdr);
+ if (rc < 0)
+ return rc;
+
+ txt_rsp_len = rc;
+ isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc);
+
+ if (txt_rsp_len) {
+ struct ib_device *ib_dev = isert_conn->conn_cm_id->device;
+ struct ib_sge *tx_dsg = &isert_cmd->tx_desc.tx_sg[1];
+ void *txt_rsp_buf = cmd->buf_ptr;
+
+ isert_cmd->pdu_buf_dma = ib_dma_map_single(ib_dev,
+ txt_rsp_buf, txt_rsp_len, DMA_TO_DEVICE);
+
+ isert_cmd->pdu_buf_len = txt_rsp_len;
+ tx_dsg->addr = isert_cmd->pdu_buf_dma;
+ tx_dsg->length = txt_rsp_len;
+ tx_dsg->lkey = isert_conn->conn_mr->lkey;
+ isert_cmd->tx_desc.num_sge = 2;
+ }
+ isert_init_send_wr(isert_cmd, send_wr);
+
+ pr_debug("Posting Text Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n");
+
+ return isert_post_response(isert_conn, isert_cmd);
+}
+
+static int
isert_build_rdma_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
struct ib_sge *ib_sge, struct ib_send_wr *send_wr,
u32 data_left, u32 offset)
@@ -1947,6 +2098,9 @@
case ISTATE_SEND_REJECT:
ret = isert_put_reject(cmd, conn);
break;
+ case ISTATE_SEND_TEXTRSP:
+ ret = isert_put_text_rsp(cmd, conn);
+ break;
case ISTATE_SEND_STATUS:
/*
* Special case for sending non GOOD SCSI status from TX thread
@@ -2175,6 +2329,17 @@
kfree(isert_np);
}
+static int isert_check_state(struct isert_conn *isert_conn, int state)
+{
+ int ret;
+
+ mutex_lock(&isert_conn->conn_mutex);
+ ret = (isert_conn->state == state);
+ mutex_unlock(&isert_conn->conn_mutex);
+
+ return ret;
+}
+
static void isert_free_conn(struct iscsi_conn *conn)
{
struct isert_conn *isert_conn = conn->context;
@@ -2184,26 +2349,43 @@
* Decrement post_send_buf_count for special case when called
* from isert_do_control_comp() -> iscsit_logout_post_handler()
*/
+ mutex_lock(&isert_conn->conn_mutex);
if (isert_conn->logout_posted)
atomic_dec(&isert_conn->post_send_buf_count);
- if (isert_conn->conn_cm_id)
+ if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) {
+ pr_debug("Calling rdma_disconnect from isert_free_conn\n");
rdma_disconnect(isert_conn->conn_cm_id);
+ }
/*
* Only wait for conn_wait_comp_err if the isert_conn made it
* into full feature phase..
*/
- if (isert_conn->state > ISER_CONN_INIT) {
+ if (isert_conn->state == ISER_CONN_UP) {
pr_debug("isert_free_conn: Before wait_event comp_err %d\n",
isert_conn->state);
- wait_event(isert_conn->conn_wait_comp_err,
- isert_conn->state == ISER_CONN_TERMINATING);
- pr_debug("isert_free_conn: After wait_event #1 >>>>>>>>>>>>\n");
- }
+ mutex_unlock(&isert_conn->conn_mutex);
- pr_debug("isert_free_conn: wait_event conn_wait %d\n", isert_conn->state);
- wait_event(isert_conn->conn_wait, isert_conn->state == ISER_CONN_DOWN);
- pr_debug("isert_free_conn: After wait_event #2 >>>>>>>>>>>>>>>>>>>>\n");
+ wait_event(isert_conn->conn_wait_comp_err,
+ (isert_check_state(isert_conn, ISER_CONN_TERMINATING)));
+
+ wait_event(isert_conn->conn_wait,
+ (isert_check_state(isert_conn, ISER_CONN_DOWN)));
+
+ isert_put_conn(isert_conn);
+ return;
+ }
+ if (isert_conn->state == ISER_CONN_INIT) {
+ mutex_unlock(&isert_conn->conn_mutex);
+ isert_put_conn(isert_conn);
+ return;
+ }
+ pr_debug("isert_free_conn: wait_event conn_wait %d\n",
+ isert_conn->state);
+ mutex_unlock(&isert_conn->conn_mutex);
+
+ wait_event(isert_conn->conn_wait,
+ (isert_check_state(isert_conn, ISER_CONN_DOWN)));
isert_put_conn(isert_conn);
}
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index b104f4c..191117b 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -61,8 +61,8 @@
uint32_t write_stag;
uint64_t read_va;
uint64_t write_va;
- u64 sense_buf_dma;
- u32 sense_buf_len;
+ u64 pdu_buf_dma;
+ u32 pdu_buf_len;
u32 read_va_off;
u32 write_va_off;
u32 rdma_wr_num;
@@ -102,6 +102,7 @@
struct ib_qp *conn_qp;
struct isert_device *conn_device;
struct work_struct conn_logout_work;
+ struct mutex conn_mutex;
wait_queue_head_t conn_wait;
wait_queue_head_t conn_wait_comp_err;
struct kref conn_kref;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 7ccf328..f93baf8 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -53,8 +53,8 @@
#define DRV_NAME "ib_srp"
#define PFX DRV_NAME ": "
-#define DRV_VERSION "0.2"
-#define DRV_RELDATE "November 1, 2005"
+#define DRV_VERSION "1.0"
+#define DRV_RELDATE "July 1, 2013"
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator "
@@ -231,14 +231,16 @@
return -ENOMEM;
recv_cq = ib_create_cq(target->srp_host->srp_dev->dev,
- srp_recv_completion, NULL, target, SRP_RQ_SIZE, 0);
+ srp_recv_completion, NULL, target, SRP_RQ_SIZE,
+ target->comp_vector);
if (IS_ERR(recv_cq)) {
ret = PTR_ERR(recv_cq);
goto err;
}
send_cq = ib_create_cq(target->srp_host->srp_dev->dev,
- srp_send_completion, NULL, target, SRP_SQ_SIZE, 0);
+ srp_send_completion, NULL, target, SRP_SQ_SIZE,
+ target->comp_vector);
if (IS_ERR(send_cq)) {
ret = PTR_ERR(send_cq);
goto err_recv_cq;
@@ -542,11 +544,11 @@
WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
+ srp_remove_target(target);
+
spin_lock(&target->srp_host->target_lock);
list_del(&target->list);
spin_unlock(&target->srp_host->target_lock);
-
- srp_remove_target(target);
}
static void srp_rport_delete(struct srp_rport *rport)
@@ -1744,18 +1746,24 @@
{
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_request *req = (struct srp_request *) scmnd->host_scribble;
+ int ret;
shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
if (!req || !srp_claim_req(target, req, scmnd))
return FAILED;
- srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
- SRP_TSK_ABORT_TASK);
+ if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
+ SRP_TSK_ABORT_TASK) == 0)
+ ret = SUCCESS;
+ else if (target->transport_offline)
+ ret = FAST_IO_FAIL;
+ else
+ ret = FAILED;
srp_free_req(target, req, scmnd, 0);
scmnd->result = DID_ABORT << 16;
scmnd->scsi_done(scmnd);
- return SUCCESS;
+ return ret;
}
static int srp_reset_device(struct scsi_cmnd *scmnd)
@@ -1891,6 +1899,14 @@
return sprintf(buf, "%s\n", target->srp_host->srp_dev->dev->name);
}
+static ssize_t show_comp_vector(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct srp_target_port *target = host_to_target(class_to_shost(dev));
+
+ return sprintf(buf, "%d\n", target->comp_vector);
+}
+
static ssize_t show_cmd_sg_entries(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1917,6 +1933,7 @@
static DEVICE_ATTR(zero_req_lim, S_IRUGO, show_zero_req_lim, NULL);
static DEVICE_ATTR(local_ib_port, S_IRUGO, show_local_ib_port, NULL);
static DEVICE_ATTR(local_ib_device, S_IRUGO, show_local_ib_device, NULL);
+static DEVICE_ATTR(comp_vector, S_IRUGO, show_comp_vector, NULL);
static DEVICE_ATTR(cmd_sg_entries, S_IRUGO, show_cmd_sg_entries, NULL);
static DEVICE_ATTR(allow_ext_sg, S_IRUGO, show_allow_ext_sg, NULL);
@@ -1931,6 +1948,7 @@
&dev_attr_zero_req_lim,
&dev_attr_local_ib_port,
&dev_attr_local_ib_device,
+ &dev_attr_comp_vector,
&dev_attr_cmd_sg_entries,
&dev_attr_allow_ext_sg,
NULL
@@ -1946,6 +1964,7 @@
.eh_abort_handler = srp_abort,
.eh_device_reset_handler = srp_reset_device,
.eh_host_reset_handler = srp_reset_host,
+ .skip_settle_delay = true,
.sg_tablesize = SRP_DEF_SG_TABLESIZE,
.can_queue = SRP_CMD_SQ_SIZE,
.this_id = -1,
@@ -2001,6 +2020,36 @@
.dev_release = srp_release_dev
};
+/**
+ * srp_conn_unique() - check whether the connection to a target is unique
+ */
+static bool srp_conn_unique(struct srp_host *host,
+ struct srp_target_port *target)
+{
+ struct srp_target_port *t;
+ bool ret = false;
+
+ if (target->state == SRP_TARGET_REMOVED)
+ goto out;
+
+ ret = true;
+
+ spin_lock(&host->target_lock);
+ list_for_each_entry(t, &host->target_list, list) {
+ if (t != target &&
+ target->id_ext == t->id_ext &&
+ target->ioc_guid == t->ioc_guid &&
+ target->initiator_ext == t->initiator_ext) {
+ ret = false;
+ break;
+ }
+ }
+ spin_unlock(&host->target_lock);
+
+out:
+ return ret;
+}
+
/*
* Target ports are added by writing
*
@@ -2023,6 +2072,7 @@
SRP_OPT_CMD_SG_ENTRIES = 1 << 9,
SRP_OPT_ALLOW_EXT_SG = 1 << 10,
SRP_OPT_SG_TABLESIZE = 1 << 11,
+ SRP_OPT_COMP_VECTOR = 1 << 12,
SRP_OPT_ALL = (SRP_OPT_ID_EXT |
SRP_OPT_IOC_GUID |
SRP_OPT_DGID |
@@ -2043,6 +2093,7 @@
{ SRP_OPT_CMD_SG_ENTRIES, "cmd_sg_entries=%u" },
{ SRP_OPT_ALLOW_EXT_SG, "allow_ext_sg=%u" },
{ SRP_OPT_SG_TABLESIZE, "sg_tablesize=%u" },
+ { SRP_OPT_COMP_VECTOR, "comp_vector=%u" },
{ SRP_OPT_ERR, NULL }
};
@@ -2198,6 +2249,14 @@
target->sg_tablesize = token;
break;
+ case SRP_OPT_COMP_VECTOR:
+ if (match_int(args, &token) || token < 0) {
+ pr_warn("bad comp_vector parameter '%s'\n", p);
+ goto out;
+ }
+ target->comp_vector = token;
+ break;
+
default:
pr_warn("unknown parameter or missing value '%s' in target creation request\n",
p);
@@ -2257,6 +2316,16 @@
if (ret)
goto err;
+ if (!srp_conn_unique(target->srp_host, target)) {
+ shost_printk(KERN_INFO, target->scsi_host,
+ PFX "Already connected to target port with id_ext=%016llx;ioc_guid=%016llx;initiator_ext=%016llx\n",
+ be64_to_cpu(target->id_ext),
+ be64_to_cpu(target->ioc_guid),
+ be64_to_cpu(target->initiator_ext));
+ ret = -EEXIST;
+ goto err;
+ }
+
if (!host->srp_dev->fmr_pool && !target->allow_ext_sg &&
target->cmd_sg_cnt < target->sg_tablesize) {
pr_warn("No FMR pool and no external indirect descriptors, limiting sg_tablesize to cmd_sg_cnt\n");
@@ -2507,6 +2576,8 @@
struct srp_target_port *target;
srp_dev = ib_get_client_data(device, &srp_client);
+ if (!srp_dev)
+ return;
list_for_each_entry_safe(host, tmp_host, &srp_dev->dev_list, list) {
device_unregister(&host->dev);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 66fbedd..e641088 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -156,6 +156,7 @@
char target_name[32];
unsigned int scsi_id;
unsigned int sg_tablesize;
+ int comp_vector;
struct ib_sa_path_rec path;
__be16 orig_dgid[8];
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 3f3f041..653ac6b 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -3011,7 +3011,7 @@
* Callback function called by the TCM core. Must not block since it can be
* invoked on the context of the IB completion handler.
*/
-static int srpt_queue_response(struct se_cmd *cmd)
+static void srpt_queue_response(struct se_cmd *cmd)
{
struct srpt_rdma_ch *ch;
struct srpt_send_ioctx *ioctx;
@@ -3022,8 +3022,6 @@
int resp_len;
u8 srp_tm_status;
- ret = 0;
-
ioctx = container_of(cmd, struct srpt_send_ioctx, cmd);
ch = ioctx->ch;
BUG_ON(!ch);
@@ -3049,7 +3047,7 @@
|| WARN_ON_ONCE(state == SRPT_STATE_CMD_RSP_SENT))) {
atomic_inc(&ch->req_lim_delta);
srpt_abort_cmd(ioctx);
- goto out;
+ return;
}
dir = ioctx->cmd.data_direction;
@@ -3061,7 +3059,7 @@
if (ret) {
printk(KERN_ERR "xfer_data failed for tag %llu\n",
ioctx->tag);
- goto out;
+ return;
}
}
@@ -3082,9 +3080,17 @@
srpt_set_cmd_state(ioctx, SRPT_STATE_DONE);
target_put_sess_cmd(ioctx->ch->sess, &ioctx->cmd);
}
+}
-out:
- return ret;
+static int srpt_queue_data_in(struct se_cmd *cmd)
+{
+ srpt_queue_response(cmd);
+ return 0;
+}
+
+static void srpt_queue_tm_rsp(struct se_cmd *cmd)
+{
+ srpt_queue_response(cmd);
}
static int srpt_queue_status(struct se_cmd *cmd)
@@ -3097,7 +3103,8 @@
(SCF_TRANSPORT_TASK_SENSE | SCF_EMULATED_TASK_SENSE))
WARN_ON(cmd->scsi_status != SAM_STAT_CHECK_CONDITION);
ioctx->queue_status_only = true;
- return srpt_queue_response(cmd);
+ srpt_queue_response(cmd);
+ return 0;
}
static void srpt_refresh_port_work(struct work_struct *work)
@@ -3930,9 +3937,9 @@
.set_default_node_attributes = srpt_set_default_node_attrs,
.get_task_tag = srpt_get_task_tag,
.get_cmd_state = srpt_get_tcm_cmd_state,
- .queue_data_in = srpt_queue_response,
+ .queue_data_in = srpt_queue_data_in,
.queue_status = srpt_queue_status,
- .queue_tm_rsp = srpt_queue_response,
+ .queue_tm_rsp = srpt_queue_tm_rsp,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 01730b2..820d85c 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -269,4 +269,17 @@
Enables bits of IOMMU API required by VFIO. The iommu_ops
is not implemented as it is not necessary for VFIO.
+config ARM_SMMU
+ bool "ARM Ltd. System MMU (SMMU) Support"
+ depends on ARM64 || (ARM_LPAE && OF)
+ select IOMMU_API
+ select ARM_DMA_USE_IOMMU if ARM
+ help
+ Support for implementations of the ARM System MMU architecture
+ versions 1 and 2. The driver supports both v7l and v8l table
+ formats with 4k and 64k page sizes.
+
+ Say Y here if your SoC includes an IOMMU device implementing
+ the ARM SMMU architecture.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index ef0e520..bbe7041 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
+obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
obj-$(CONFIG_DMAR_TABLE) += dmar.o
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 21d02b0..6dc6594 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -287,14 +287,27 @@
/*
* If it's a multifunction device that does not support our
- * required ACS flags, add to the same group as function 0.
+ * required ACS flags, add to the same group as lowest numbered
+ * function that also does not suport the required ACS flags.
*/
if (dma_pdev->multifunction &&
- !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
- swap_pci_ref(&dma_pdev,
- pci_get_slot(dma_pdev->bus,
- PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
- 0)));
+ !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
+ u8 i, slot = PCI_SLOT(dma_pdev->devfn);
+
+ for (i = 0; i < 8; i++) {
+ struct pci_dev *tmp;
+
+ tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
+ if (!tmp)
+ continue;
+
+ if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
+ swap_pci_ref(&dma_pdev, tmp);
+ break;
+ }
+ pci_dev_put(tmp);
+ }
+ }
/*
* Devices on the root bus go through the iommu. If that's not us,
@@ -1484,6 +1497,10 @@
/* Large PTE found which maps this address */
unmap_size = PTE_PAGE_SIZE(*pte);
+
+ /* Only unmap from the first pte in the page */
+ if ((unmap_size - 1) & bus_addr)
+ break;
count = PAGE_SIZE_PTE_COUNT(unmap_size);
for (i = 0; i < count; i++)
pte[i] = 0ULL;
@@ -1493,7 +1510,7 @@
unmapped += unmap_size;
}
- BUG_ON(!is_power_of_2(unmapped));
+ BUG_ON(unmapped && !is_power_of_2(unmapped));
return unmapped;
}
@@ -1893,34 +1910,59 @@
write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
}
+#define DEFINE_FREE_PT_FN(LVL, FN) \
+static void free_pt_##LVL (unsigned long __pt) \
+{ \
+ unsigned long p; \
+ u64 *pt; \
+ int i; \
+ \
+ pt = (u64 *)__pt; \
+ \
+ for (i = 0; i < 512; ++i) { \
+ if (!IOMMU_PTE_PRESENT(pt[i])) \
+ continue; \
+ \
+ p = (unsigned long)IOMMU_PTE_PAGE(pt[i]); \
+ FN(p); \
+ } \
+ free_page((unsigned long)pt); \
+}
+
+DEFINE_FREE_PT_FN(l2, free_page)
+DEFINE_FREE_PT_FN(l3, free_pt_l2)
+DEFINE_FREE_PT_FN(l4, free_pt_l3)
+DEFINE_FREE_PT_FN(l5, free_pt_l4)
+DEFINE_FREE_PT_FN(l6, free_pt_l5)
+
static void free_pagetable(struct protection_domain *domain)
{
- int i, j;
- u64 *p1, *p2, *p3;
+ unsigned long root = (unsigned long)domain->pt_root;
- p1 = domain->pt_root;
-
- if (!p1)
- return;
-
- for (i = 0; i < 512; ++i) {
- if (!IOMMU_PTE_PRESENT(p1[i]))
- continue;
-
- p2 = IOMMU_PTE_PAGE(p1[i]);
- for (j = 0; j < 512; ++j) {
- if (!IOMMU_PTE_PRESENT(p2[j]))
- continue;
- p3 = IOMMU_PTE_PAGE(p2[j]);
- free_page((unsigned long)p3);
- }
-
- free_page((unsigned long)p2);
+ switch (domain->mode) {
+ case PAGE_MODE_NONE:
+ break;
+ case PAGE_MODE_1_LEVEL:
+ free_page(root);
+ break;
+ case PAGE_MODE_2_LEVEL:
+ free_pt_l2(root);
+ break;
+ case PAGE_MODE_3_LEVEL:
+ free_pt_l3(root);
+ break;
+ case PAGE_MODE_4_LEVEL:
+ free_pt_l4(root);
+ break;
+ case PAGE_MODE_5_LEVEL:
+ free_pt_l5(root);
+ break;
+ case PAGE_MODE_6_LEVEL:
+ free_pt_l6(root);
+ break;
+ default:
+ BUG();
}
-
- free_page((unsigned long)p1);
-
- domain->pt_root = NULL;
}
static void free_gcr3_tbl_level1(u64 *tbl)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
new file mode 100644
index 0000000..ebd0a4c
--- /dev/null
+++ b/drivers/iommu/arm-smmu.c
@@ -0,0 +1,1969 @@
+/*
+ * IOMMU API for ARM architected SMMU implementations.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2013 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ *
+ * This driver currently supports:
+ * - SMMUv1 and v2 implementations
+ * - Stream-matching and stream-indexing
+ * - v7/v8 long-descriptor format
+ * - Non-secure access to the SMMU
+ * - 4k and 64k pages, with contiguous pte hints.
+ * - Up to 39-bit addressing
+ * - Context fault reporting
+ */
+
+#define pr_fmt(fmt) "arm-smmu: " fmt
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/amba/bus.h>
+
+#include <asm/pgalloc.h>
+
+/* Maximum number of stream IDs assigned to a single device */
+#define MAX_MASTER_STREAMIDS 8
+
+/* Maximum number of context banks per SMMU */
+#define ARM_SMMU_MAX_CBS 128
+
+/* Maximum number of mapping groups per SMMU */
+#define ARM_SMMU_MAX_SMRS 128
+
+/* Number of VMIDs per SMMU */
+#define ARM_SMMU_NUM_VMIDS 256
+
+/* SMMU global address space */
+#define ARM_SMMU_GR0(smmu) ((smmu)->base)
+#define ARM_SMMU_GR1(smmu) ((smmu)->base + (smmu)->pagesize)
+
+/* Page table bits */
+#define ARM_SMMU_PTE_PAGE (((pteval_t)3) << 0)
+#define ARM_SMMU_PTE_CONT (((pteval_t)1) << 52)
+#define ARM_SMMU_PTE_AF (((pteval_t)1) << 10)
+#define ARM_SMMU_PTE_SH_NS (((pteval_t)0) << 8)
+#define ARM_SMMU_PTE_SH_OS (((pteval_t)2) << 8)
+#define ARM_SMMU_PTE_SH_IS (((pteval_t)3) << 8)
+
+#if PAGE_SIZE == SZ_4K
+#define ARM_SMMU_PTE_CONT_ENTRIES 16
+#elif PAGE_SIZE == SZ_64K
+#define ARM_SMMU_PTE_CONT_ENTRIES 32
+#else
+#define ARM_SMMU_PTE_CONT_ENTRIES 1
+#endif
+
+#define ARM_SMMU_PTE_CONT_SIZE (PAGE_SIZE * ARM_SMMU_PTE_CONT_ENTRIES)
+#define ARM_SMMU_PTE_CONT_MASK (~(ARM_SMMU_PTE_CONT_SIZE - 1))
+#define ARM_SMMU_PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(pte_t))
+
+/* Stage-1 PTE */
+#define ARM_SMMU_PTE_AP_UNPRIV (((pteval_t)1) << 6)
+#define ARM_SMMU_PTE_AP_RDONLY (((pteval_t)2) << 6)
+#define ARM_SMMU_PTE_ATTRINDX_SHIFT 2
+
+/* Stage-2 PTE */
+#define ARM_SMMU_PTE_HAP_FAULT (((pteval_t)0) << 6)
+#define ARM_SMMU_PTE_HAP_READ (((pteval_t)1) << 6)
+#define ARM_SMMU_PTE_HAP_WRITE (((pteval_t)2) << 6)
+#define ARM_SMMU_PTE_MEMATTR_OIWB (((pteval_t)0xf) << 2)
+#define ARM_SMMU_PTE_MEMATTR_NC (((pteval_t)0x5) << 2)
+#define ARM_SMMU_PTE_MEMATTR_DEV (((pteval_t)0x1) << 2)
+
+/* Configuration registers */
+#define ARM_SMMU_GR0_sCR0 0x0
+#define sCR0_CLIENTPD (1 << 0)
+#define sCR0_GFRE (1 << 1)
+#define sCR0_GFIE (1 << 2)
+#define sCR0_GCFGFRE (1 << 4)
+#define sCR0_GCFGFIE (1 << 5)
+#define sCR0_USFCFG (1 << 10)
+#define sCR0_VMIDPNE (1 << 11)
+#define sCR0_PTM (1 << 12)
+#define sCR0_FB (1 << 13)
+#define sCR0_BSU_SHIFT 14
+#define sCR0_BSU_MASK 0x3
+
+/* Identification registers */
+#define ARM_SMMU_GR0_ID0 0x20
+#define ARM_SMMU_GR0_ID1 0x24
+#define ARM_SMMU_GR0_ID2 0x28
+#define ARM_SMMU_GR0_ID3 0x2c
+#define ARM_SMMU_GR0_ID4 0x30
+#define ARM_SMMU_GR0_ID5 0x34
+#define ARM_SMMU_GR0_ID6 0x38
+#define ARM_SMMU_GR0_ID7 0x3c
+#define ARM_SMMU_GR0_sGFSR 0x48
+#define ARM_SMMU_GR0_sGFSYNR0 0x50
+#define ARM_SMMU_GR0_sGFSYNR1 0x54
+#define ARM_SMMU_GR0_sGFSYNR2 0x58
+#define ARM_SMMU_GR0_PIDR0 0xfe0
+#define ARM_SMMU_GR0_PIDR1 0xfe4
+#define ARM_SMMU_GR0_PIDR2 0xfe8
+
+#define ID0_S1TS (1 << 30)
+#define ID0_S2TS (1 << 29)
+#define ID0_NTS (1 << 28)
+#define ID0_SMS (1 << 27)
+#define ID0_PTFS_SHIFT 24
+#define ID0_PTFS_MASK 0x2
+#define ID0_PTFS_V8_ONLY 0x2
+#define ID0_CTTW (1 << 14)
+#define ID0_NUMIRPT_SHIFT 16
+#define ID0_NUMIRPT_MASK 0xff
+#define ID0_NUMSMRG_SHIFT 0
+#define ID0_NUMSMRG_MASK 0xff
+
+#define ID1_PAGESIZE (1 << 31)
+#define ID1_NUMPAGENDXB_SHIFT 28
+#define ID1_NUMPAGENDXB_MASK 7
+#define ID1_NUMS2CB_SHIFT 16
+#define ID1_NUMS2CB_MASK 0xff
+#define ID1_NUMCB_SHIFT 0
+#define ID1_NUMCB_MASK 0xff
+
+#define ID2_OAS_SHIFT 4
+#define ID2_OAS_MASK 0xf
+#define ID2_IAS_SHIFT 0
+#define ID2_IAS_MASK 0xf
+#define ID2_UBS_SHIFT 8
+#define ID2_UBS_MASK 0xf
+#define ID2_PTFS_4K (1 << 12)
+#define ID2_PTFS_16K (1 << 13)
+#define ID2_PTFS_64K (1 << 14)
+
+#define PIDR2_ARCH_SHIFT 4
+#define PIDR2_ARCH_MASK 0xf
+
+/* Global TLB invalidation */
+#define ARM_SMMU_GR0_STLBIALL 0x60
+#define ARM_SMMU_GR0_TLBIVMID 0x64
+#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
+#define ARM_SMMU_GR0_TLBIALLH 0x6c
+#define ARM_SMMU_GR0_sTLBGSYNC 0x70
+#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
+#define sTLBGSTATUS_GSACTIVE (1 << 0)
+#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
+
+/* Stream mapping registers */
+#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
+#define SMR_VALID (1 << 31)
+#define SMR_MASK_SHIFT 16
+#define SMR_MASK_MASK 0x7fff
+#define SMR_ID_SHIFT 0
+#define SMR_ID_MASK 0x7fff
+
+#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
+#define S2CR_CBNDX_SHIFT 0
+#define S2CR_CBNDX_MASK 0xff
+#define S2CR_TYPE_SHIFT 16
+#define S2CR_TYPE_MASK 0x3
+#define S2CR_TYPE_TRANS (0 << S2CR_TYPE_SHIFT)
+#define S2CR_TYPE_BYPASS (1 << S2CR_TYPE_SHIFT)
+#define S2CR_TYPE_FAULT (2 << S2CR_TYPE_SHIFT)
+
+/* Context bank attribute registers */
+#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
+#define CBAR_VMID_SHIFT 0
+#define CBAR_VMID_MASK 0xff
+#define CBAR_S1_MEMATTR_SHIFT 12
+#define CBAR_S1_MEMATTR_MASK 0xf
+#define CBAR_S1_MEMATTR_WB 0xf
+#define CBAR_TYPE_SHIFT 16
+#define CBAR_TYPE_MASK 0x3
+#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
+#define CBAR_IRPTNDX_SHIFT 24
+#define CBAR_IRPTNDX_MASK 0xff
+
+#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
+#define CBA2R_RW64_32BIT (0 << 0)
+#define CBA2R_RW64_64BIT (1 << 0)
+
+/* Translation context bank */
+#define ARM_SMMU_CB_BASE(smmu) ((smmu)->base + ((smmu)->size >> 1))
+#define ARM_SMMU_CB(smmu, n) ((n) * (smmu)->pagesize)
+
+#define ARM_SMMU_CB_SCTLR 0x0
+#define ARM_SMMU_CB_RESUME 0x8
+#define ARM_SMMU_CB_TTBCR2 0x10
+#define ARM_SMMU_CB_TTBR0_LO 0x20
+#define ARM_SMMU_CB_TTBR0_HI 0x24
+#define ARM_SMMU_CB_TTBCR 0x30
+#define ARM_SMMU_CB_S1_MAIR0 0x38
+#define ARM_SMMU_CB_FSR 0x58
+#define ARM_SMMU_CB_FAR_LO 0x60
+#define ARM_SMMU_CB_FAR_HI 0x64
+#define ARM_SMMU_CB_FSYNR0 0x68
+
+#define SCTLR_S1_ASIDPNE (1 << 12)
+#define SCTLR_CFCFG (1 << 7)
+#define SCTLR_CFIE (1 << 6)
+#define SCTLR_CFRE (1 << 5)
+#define SCTLR_E (1 << 4)
+#define SCTLR_AFE (1 << 2)
+#define SCTLR_TRE (1 << 1)
+#define SCTLR_M (1 << 0)
+#define SCTLR_EAE_SBOP (SCTLR_AFE | SCTLR_TRE)
+
+#define RESUME_RETRY (0 << 0)
+#define RESUME_TERMINATE (1 << 0)
+
+#define TTBCR_EAE (1 << 31)
+
+#define TTBCR_PASIZE_SHIFT 16
+#define TTBCR_PASIZE_MASK 0x7
+
+#define TTBCR_TG0_4K (0 << 14)
+#define TTBCR_TG0_64K (1 << 14)
+
+#define TTBCR_SH0_SHIFT 12
+#define TTBCR_SH0_MASK 0x3
+#define TTBCR_SH_NS 0
+#define TTBCR_SH_OS 2
+#define TTBCR_SH_IS 3
+
+#define TTBCR_ORGN0_SHIFT 10
+#define TTBCR_IRGN0_SHIFT 8
+#define TTBCR_RGN_MASK 0x3
+#define TTBCR_RGN_NC 0
+#define TTBCR_RGN_WBWA 1
+#define TTBCR_RGN_WT 2
+#define TTBCR_RGN_WB 3
+
+#define TTBCR_SL0_SHIFT 6
+#define TTBCR_SL0_MASK 0x3
+#define TTBCR_SL0_LVL_2 0
+#define TTBCR_SL0_LVL_1 1
+
+#define TTBCR_T1SZ_SHIFT 16
+#define TTBCR_T0SZ_SHIFT 0
+#define TTBCR_SZ_MASK 0xf
+
+#define TTBCR2_SEP_SHIFT 15
+#define TTBCR2_SEP_MASK 0x7
+
+#define TTBCR2_PASIZE_SHIFT 0
+#define TTBCR2_PASIZE_MASK 0x7
+
+/* Common definitions for PASize and SEP fields */
+#define TTBCR2_ADDR_32 0
+#define TTBCR2_ADDR_36 1
+#define TTBCR2_ADDR_40 2
+#define TTBCR2_ADDR_42 3
+#define TTBCR2_ADDR_44 4
+#define TTBCR2_ADDR_48 5
+
+#define MAIR_ATTR_SHIFT(n) ((n) << 3)
+#define MAIR_ATTR_MASK 0xff
+#define MAIR_ATTR_DEVICE 0x04
+#define MAIR_ATTR_NC 0x44
+#define MAIR_ATTR_WBRWA 0xff
+#define MAIR_ATTR_IDX_NC 0
+#define MAIR_ATTR_IDX_CACHE 1
+#define MAIR_ATTR_IDX_DEV 2
+
+#define FSR_MULTI (1 << 31)
+#define FSR_SS (1 << 30)
+#define FSR_UUT (1 << 8)
+#define FSR_ASF (1 << 7)
+#define FSR_TLBLKF (1 << 6)
+#define FSR_TLBMCF (1 << 5)
+#define FSR_EF (1 << 4)
+#define FSR_PF (1 << 3)
+#define FSR_AFF (1 << 2)
+#define FSR_TF (1 << 1)
+
+#define FSR_IGN (FSR_AFF | FSR_ASF | FSR_TLBMCF | \
+ FSR_TLBLKF)
+#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
+ FSR_EF | FSR_PF | FSR_TF)
+
+#define FSYNR0_WNR (1 << 4)
+
+struct arm_smmu_smr {
+ u8 idx;
+ u16 mask;
+ u16 id;
+};
+
+struct arm_smmu_master {
+ struct device_node *of_node;
+
+ /*
+ * The following is specific to the master's position in the
+ * SMMU chain.
+ */
+ struct rb_node node;
+ int num_streamids;
+ u16 streamids[MAX_MASTER_STREAMIDS];
+
+ /*
+ * We only need to allocate these on the root SMMU, as we
+ * configure unmatched streams to bypass translation.
+ */
+ struct arm_smmu_smr *smrs;
+};
+
+struct arm_smmu_device {
+ struct device *dev;
+ struct device_node *parent_of_node;
+
+ void __iomem *base;
+ unsigned long size;
+ unsigned long pagesize;
+
+#define ARM_SMMU_FEAT_COHERENT_WALK (1 << 0)
+#define ARM_SMMU_FEAT_STREAM_MATCH (1 << 1)
+#define ARM_SMMU_FEAT_TRANS_S1 (1 << 2)
+#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3)
+#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4)
+ u32 features;
+ int version;
+
+ u32 num_context_banks;
+ u32 num_s2_context_banks;
+ DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
+ atomic_t irptndx;
+
+ u32 num_mapping_groups;
+ DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+
+ unsigned long input_size;
+ unsigned long s1_output_size;
+ unsigned long s2_output_size;
+
+ u32 num_global_irqs;
+ u32 num_context_irqs;
+ unsigned int *irqs;
+
+ DECLARE_BITMAP(vmid_map, ARM_SMMU_NUM_VMIDS);
+
+ struct list_head list;
+ struct rb_root masters;
+};
+
+struct arm_smmu_cfg {
+ struct arm_smmu_device *smmu;
+ u8 vmid;
+ u8 cbndx;
+ u8 irptndx;
+ u32 cbar;
+ pgd_t *pgd;
+};
+
+struct arm_smmu_domain {
+ /*
+ * A domain can span across multiple, chained SMMUs and requires
+ * all devices within the domain to follow the same translation
+ * path.
+ */
+ struct arm_smmu_device *leaf_smmu;
+ struct arm_smmu_cfg root_cfg;
+ phys_addr_t output_mask;
+
+ spinlock_t lock;
+};
+
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+
+static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
+ struct device_node *dev_node)
+{
+ struct rb_node *node = smmu->masters.rb_node;
+
+ while (node) {
+ struct arm_smmu_master *master;
+ master = container_of(node, struct arm_smmu_master, node);
+
+ if (dev_node < master->of_node)
+ node = node->rb_left;
+ else if (dev_node > master->of_node)
+ node = node->rb_right;
+ else
+ return master;
+ }
+
+ return NULL;
+}
+
+static int insert_smmu_master(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ struct rb_node **new, *parent;
+
+ new = &smmu->masters.rb_node;
+ parent = NULL;
+ while (*new) {
+ struct arm_smmu_master *this;
+ this = container_of(*new, struct arm_smmu_master, node);
+
+ parent = *new;
+ if (master->of_node < this->of_node)
+ new = &((*new)->rb_left);
+ else if (master->of_node > this->of_node)
+ new = &((*new)->rb_right);
+ else
+ return -EEXIST;
+ }
+
+ rb_link_node(&master->node, parent, new);
+ rb_insert_color(&master->node, &smmu->masters);
+ return 0;
+}
+
+static int register_smmu_master(struct arm_smmu_device *smmu,
+ struct device *dev,
+ struct of_phandle_args *masterspec)
+{
+ int i;
+ struct arm_smmu_master *master;
+
+ master = find_smmu_master(smmu, masterspec->np);
+ if (master) {
+ dev_err(dev,
+ "rejecting multiple registrations for master device %s\n",
+ masterspec->np->name);
+ return -EBUSY;
+ }
+
+ if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
+ dev_err(dev,
+ "reached maximum number (%d) of stream IDs for master device %s\n",
+ MAX_MASTER_STREAMIDS, masterspec->np->name);
+ return -ENOSPC;
+ }
+
+ master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->of_node = masterspec->np;
+ master->num_streamids = masterspec->args_count;
+
+ for (i = 0; i < master->num_streamids; ++i)
+ master->streamids[i] = masterspec->args[i];
+
+ return insert_smmu_master(smmu, master);
+}
+
+static struct arm_smmu_device *find_parent_smmu(struct arm_smmu_device *smmu)
+{
+ struct arm_smmu_device *parent;
+
+ if (!smmu->parent_of_node)
+ return NULL;
+
+ spin_lock(&arm_smmu_devices_lock);
+ list_for_each_entry(parent, &arm_smmu_devices, list)
+ if (parent->dev->of_node == smmu->parent_of_node)
+ goto out_unlock;
+
+ parent = NULL;
+ dev_warn(smmu->dev,
+ "Failed to find SMMU parent despite parent in DT\n");
+out_unlock:
+ spin_unlock(&arm_smmu_devices_lock);
+ return parent;
+}
+
+static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
+{
+ int idx;
+
+ do {
+ idx = find_next_zero_bit(map, end, start);
+ if (idx == end)
+ return -ENOSPC;
+ } while (test_and_set_bit(idx, map));
+
+ return idx;
+}
+
+static void __arm_smmu_free_bitmap(unsigned long *map, int idx)
+{
+ clear_bit(idx, map);
+}
+
+/* Wait for any pending TLB invalidations to complete */
+static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
+{
+ int count = 0;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ writel_relaxed(0, gr0_base + ARM_SMMU_GR0_sTLBGSYNC);
+ while (readl_relaxed(gr0_base + ARM_SMMU_GR0_sTLBGSTATUS)
+ & sTLBGSTATUS_GSACTIVE) {
+ cpu_relax();
+ if (++count == TLB_LOOP_TIMEOUT) {
+ dev_err_ratelimited(smmu->dev,
+ "TLB sync timed out -- SMMU may be deadlocked\n");
+ return;
+ }
+ udelay(1);
+ }
+}
+
+static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
+{
+ int flags, ret;
+ u32 fsr, far, fsynr, resume;
+ unsigned long iova;
+ struct iommu_domain *domain = dev;
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ struct arm_smmu_device *smmu = root_cfg->smmu;
+ void __iomem *cb_base;
+
+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, root_cfg->cbndx);
+ fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
+
+ if (!(fsr & FSR_FAULT))
+ return IRQ_NONE;
+
+ if (fsr & FSR_IGN)
+ dev_err_ratelimited(smmu->dev,
+ "Unexpected context fault (fsr 0x%u)\n",
+ fsr);
+
+ fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
+ flags = fsynr & FSYNR0_WNR ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
+
+ far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_LO);
+ iova = far;
+#ifdef CONFIG_64BIT
+ far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_HI);
+ iova |= ((unsigned long)far << 32);
+#endif
+
+ if (!report_iommu_fault(domain, smmu->dev, iova, flags)) {
+ ret = IRQ_HANDLED;
+ resume = RESUME_RETRY;
+ } else {
+ ret = IRQ_NONE;
+ resume = RESUME_TERMINATE;
+ }
+
+ /* Clear the faulting FSR */
+ writel(fsr, cb_base + ARM_SMMU_CB_FSR);
+
+ /* Retry or terminate any stalled transactions */
+ if (fsr & FSR_SS)
+ writel_relaxed(resume, cb_base + ARM_SMMU_CB_RESUME);
+
+ return ret;
+}
+
+static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
+{
+ u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
+ struct arm_smmu_device *smmu = dev;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
+ gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
+ gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
+ gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
+
+ dev_err_ratelimited(smmu->dev,
+ "Unexpected global fault, this could be serious\n");
+ dev_err_ratelimited(smmu->dev,
+ "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n",
+ gfsr, gfsynr0, gfsynr1, gfsynr2);
+
+ writel(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
+ return IRQ_NONE;
+}
+
+static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
+{
+ u32 reg;
+ bool stage1;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ struct arm_smmu_device *smmu = root_cfg->smmu;
+ void __iomem *cb_base, *gr0_base, *gr1_base;
+
+ gr0_base = ARM_SMMU_GR0(smmu);
+ gr1_base = ARM_SMMU_GR1(smmu);
+ stage1 = root_cfg->cbar != CBAR_TYPE_S2_TRANS;
+ cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, root_cfg->cbndx);
+
+ /* CBAR */
+ reg = root_cfg->cbar |
+ (root_cfg->vmid << CBAR_VMID_SHIFT);
+ if (smmu->version == 1)
+ reg |= root_cfg->irptndx << CBAR_IRPTNDX_SHIFT;
+
+ /* Use the weakest memory type, so it is overridden by the pte */
+ if (stage1)
+ reg |= (CBAR_S1_MEMATTR_WB << CBAR_S1_MEMATTR_SHIFT);
+ writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(root_cfg->cbndx));
+
+ if (smmu->version > 1) {
+ /* CBA2R */
+#ifdef CONFIG_64BIT
+ reg = CBA2R_RW64_64BIT;
+#else
+ reg = CBA2R_RW64_32BIT;
+#endif
+ writel_relaxed(reg,
+ gr1_base + ARM_SMMU_GR1_CBA2R(root_cfg->cbndx));
+
+ /* TTBCR2 */
+ switch (smmu->input_size) {
+ case 32:
+ reg = (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT);
+ break;
+ case 36:
+ reg = (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT);
+ break;
+ case 39:
+ reg = (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT);
+ break;
+ case 42:
+ reg = (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT);
+ break;
+ case 44:
+ reg = (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT);
+ break;
+ case 48:
+ reg = (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT);
+ break;
+ }
+
+ switch (smmu->s1_output_size) {
+ case 32:
+ reg |= (TTBCR2_ADDR_32 << TTBCR2_PASIZE_SHIFT);
+ break;
+ case 36:
+ reg |= (TTBCR2_ADDR_36 << TTBCR2_PASIZE_SHIFT);
+ break;
+ case 39:
+ reg |= (TTBCR2_ADDR_40 << TTBCR2_PASIZE_SHIFT);
+ break;
+ case 42:
+ reg |= (TTBCR2_ADDR_42 << TTBCR2_PASIZE_SHIFT);
+ break;
+ case 44:
+ reg |= (TTBCR2_ADDR_44 << TTBCR2_PASIZE_SHIFT);
+ break;
+ case 48:
+ reg |= (TTBCR2_ADDR_48 << TTBCR2_PASIZE_SHIFT);
+ break;
+ }
+
+ if (stage1)
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2);
+ }
+
+ /* TTBR0 */
+ reg = __pa(root_cfg->pgd);
+#ifndef __BIG_ENDIAN
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
+ reg = (phys_addr_t)__pa(root_cfg->pgd) >> 32;
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
+#else
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
+ reg = (phys_addr_t)__pa(root_cfg->pgd) >> 32;
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
+#endif
+
+ /*
+ * TTBCR
+ * We use long descriptor, with inner-shareable WBWA tables in TTBR0.
+ */
+ if (smmu->version > 1) {
+ if (PAGE_SIZE == SZ_4K)
+ reg = TTBCR_TG0_4K;
+ else
+ reg = TTBCR_TG0_64K;
+
+ if (!stage1) {
+ switch (smmu->s2_output_size) {
+ case 32:
+ reg |= (TTBCR2_ADDR_32 << TTBCR_PASIZE_SHIFT);
+ break;
+ case 36:
+ reg |= (TTBCR2_ADDR_36 << TTBCR_PASIZE_SHIFT);
+ break;
+ case 40:
+ reg |= (TTBCR2_ADDR_40 << TTBCR_PASIZE_SHIFT);
+ break;
+ case 42:
+ reg |= (TTBCR2_ADDR_42 << TTBCR_PASIZE_SHIFT);
+ break;
+ case 44:
+ reg |= (TTBCR2_ADDR_44 << TTBCR_PASIZE_SHIFT);
+ break;
+ case 48:
+ reg |= (TTBCR2_ADDR_48 << TTBCR_PASIZE_SHIFT);
+ break;
+ }
+ } else {
+ reg |= (64 - smmu->s1_output_size) << TTBCR_T0SZ_SHIFT;
+ }
+ } else {
+ reg = 0;
+ }
+
+ reg |= TTBCR_EAE |
+ (TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
+ (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
+ (TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT) |
+ (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT);
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
+
+ /* MAIR0 (stage-1 only) */
+ if (stage1) {
+ reg = (MAIR_ATTR_NC << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_NC)) |
+ (MAIR_ATTR_WBRWA << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_CACHE)) |
+ (MAIR_ATTR_DEVICE << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_DEV));
+ writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
+ }
+
+ /* Nuke the TLB */
+ writel_relaxed(root_cfg->vmid, gr0_base + ARM_SMMU_GR0_TLBIVMID);
+ arm_smmu_tlb_sync(smmu);
+
+ /* SCTLR */
+ reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP;
+ if (stage1)
+ reg |= SCTLR_S1_ASIDPNE;
+#ifdef __BIG_ENDIAN
+ reg |= SCTLR_E;
+#endif
+ writel(reg, cb_base + ARM_SMMU_CB_SCTLR);
+}
+
+static int arm_smmu_init_domain_context(struct iommu_domain *domain,
+ struct device *dev)
+{
+ int irq, ret, start;
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ struct arm_smmu_device *smmu, *parent;
+
+ /*
+ * Walk the SMMU chain to find the root device for this chain.
+ * We assume that no masters have translations which terminate
+ * early, and therefore check that the root SMMU does indeed have
+ * a StreamID for the master in question.
+ */
+ parent = dev->archdata.iommu;
+ smmu_domain->output_mask = -1;
+ do {
+ smmu = parent;
+ smmu_domain->output_mask &= (1ULL << smmu->s2_output_size) - 1;
+ } while ((parent = find_parent_smmu(smmu)));
+
+ if (!find_smmu_master(smmu, dev->of_node)) {
+ dev_err(dev, "unable to find root SMMU for device\n");
+ return -ENODEV;
+ }
+
+ ret = __arm_smmu_alloc_bitmap(smmu->vmid_map, 0, ARM_SMMU_NUM_VMIDS);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ root_cfg->vmid = ret;
+ if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) {
+ /*
+ * We will likely want to change this if/when KVM gets
+ * involved.
+ */
+ root_cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
+ start = smmu->num_s2_context_banks;
+ } else if (smmu->features & ARM_SMMU_FEAT_TRANS_S2) {
+ root_cfg->cbar = CBAR_TYPE_S2_TRANS;
+ start = 0;
+ } else {
+ root_cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
+ start = smmu->num_s2_context_banks;
+ }
+
+ ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
+ smmu->num_context_banks);
+ if (IS_ERR_VALUE(ret))
+ goto out_free_vmid;
+
+ root_cfg->cbndx = ret;
+
+ if (smmu->version == 1) {
+ root_cfg->irptndx = atomic_inc_return(&smmu->irptndx);
+ root_cfg->irptndx %= smmu->num_context_irqs;
+ } else {
+ root_cfg->irptndx = root_cfg->cbndx;
+ }
+
+ irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
+ ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED,
+ "arm-smmu-context-fault", domain);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
+ root_cfg->irptndx, irq);
+ root_cfg->irptndx = -1;
+ goto out_free_context;
+ }
+
+ root_cfg->smmu = smmu;
+ arm_smmu_init_context_bank(smmu_domain);
+ return ret;
+
+out_free_context:
+ __arm_smmu_free_bitmap(smmu->context_map, root_cfg->cbndx);
+out_free_vmid:
+ __arm_smmu_free_bitmap(smmu->vmid_map, root_cfg->vmid);
+ return ret;
+}
+
+static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
+{
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ struct arm_smmu_device *smmu = root_cfg->smmu;
+ int irq;
+
+ if (!smmu)
+ return;
+
+ if (root_cfg->irptndx != -1) {
+ irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
+ free_irq(irq, domain);
+ }
+
+ __arm_smmu_free_bitmap(smmu->vmid_map, root_cfg->vmid);
+ __arm_smmu_free_bitmap(smmu->context_map, root_cfg->cbndx);
+}
+
+static int arm_smmu_domain_init(struct iommu_domain *domain)
+{
+ struct arm_smmu_domain *smmu_domain;
+ pgd_t *pgd;
+
+ /*
+ * Allocate the domain and initialise some of its data structures.
+ * We can't really do anything meaningful until we've added a
+ * master.
+ */
+ smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);
+ if (!smmu_domain)
+ return -ENOMEM;
+
+ pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL);
+ if (!pgd)
+ goto out_free_domain;
+ smmu_domain->root_cfg.pgd = pgd;
+
+ spin_lock_init(&smmu_domain->lock);
+ domain->priv = smmu_domain;
+ return 0;
+
+out_free_domain:
+ kfree(smmu_domain);
+ return -ENOMEM;
+}
+
+static void arm_smmu_free_ptes(pmd_t *pmd)
+{
+ pgtable_t table = pmd_pgtable(*pmd);
+ pgtable_page_dtor(table);
+ __free_page(table);
+}
+
+static void arm_smmu_free_pmds(pud_t *pud)
+{
+ int i;
+ pmd_t *pmd, *pmd_base = pmd_offset(pud, 0);
+
+ pmd = pmd_base;
+ for (i = 0; i < PTRS_PER_PMD; ++i) {
+ if (pmd_none(*pmd))
+ continue;
+
+ arm_smmu_free_ptes(pmd);
+ pmd++;
+ }
+
+ pmd_free(NULL, pmd_base);
+}
+
+static void arm_smmu_free_puds(pgd_t *pgd)
+{
+ int i;
+ pud_t *pud, *pud_base = pud_offset(pgd, 0);
+
+ pud = pud_base;
+ for (i = 0; i < PTRS_PER_PUD; ++i) {
+ if (pud_none(*pud))
+ continue;
+
+ arm_smmu_free_pmds(pud);
+ pud++;
+ }
+
+ pud_free(NULL, pud_base);
+}
+
+static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
+{
+ int i;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ pgd_t *pgd, *pgd_base = root_cfg->pgd;
+
+ /*
+ * Recursively free the page tables for this domain. We don't
+ * care about speculative TLB filling, because the TLB will be
+ * nuked next time this context bank is re-allocated and no devices
+ * currently map to these tables.
+ */
+ pgd = pgd_base;
+ for (i = 0; i < PTRS_PER_PGD; ++i) {
+ if (pgd_none(*pgd))
+ continue;
+ arm_smmu_free_puds(pgd);
+ pgd++;
+ }
+
+ kfree(pgd_base);
+}
+
+static void arm_smmu_domain_destroy(struct iommu_domain *domain)
+{
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ arm_smmu_destroy_domain_context(domain);
+ arm_smmu_free_pgtables(smmu_domain);
+ kfree(smmu_domain);
+}
+
+static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ int i;
+ struct arm_smmu_smr *smrs;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH))
+ return 0;
+
+ if (master->smrs)
+ return -EEXIST;
+
+ smrs = kmalloc(sizeof(*smrs) * master->num_streamids, GFP_KERNEL);
+ if (!smrs) {
+ dev_err(smmu->dev, "failed to allocate %d SMRs for master %s\n",
+ master->num_streamids, master->of_node->name);
+ return -ENOMEM;
+ }
+
+ /* Allocate the SMRs on the root SMMU */
+ for (i = 0; i < master->num_streamids; ++i) {
+ int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
+ smmu->num_mapping_groups);
+ if (IS_ERR_VALUE(idx)) {
+ dev_err(smmu->dev, "failed to allocate free SMR\n");
+ goto err_free_smrs;
+ }
+
+ smrs[i] = (struct arm_smmu_smr) {
+ .idx = idx,
+ .mask = 0, /* We don't currently share SMRs */
+ .id = master->streamids[i],
+ };
+ }
+
+ /* It worked! Now, poke the actual hardware */
+ for (i = 0; i < master->num_streamids; ++i) {
+ u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
+ smrs[i].mask << SMR_MASK_SHIFT;
+ writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
+ }
+
+ master->smrs = smrs;
+ return 0;
+
+err_free_smrs:
+ while (--i >= 0)
+ __arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
+ kfree(smrs);
+ return -ENOSPC;
+}
+
+static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ int i;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+ struct arm_smmu_smr *smrs = master->smrs;
+
+ /* Invalidate the SMRs before freeing back to the allocator */
+ for (i = 0; i < master->num_streamids; ++i) {
+ u8 idx = smrs[i].idx;
+ writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
+ __arm_smmu_free_bitmap(smmu->smr_map, idx);
+ }
+
+ master->smrs = NULL;
+ kfree(smrs);
+}
+
+static void arm_smmu_bypass_stream_mapping(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+ int i;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ for (i = 0; i < master->num_streamids; ++i) {
+ u16 sid = master->streamids[i];
+ writel_relaxed(S2CR_TYPE_BYPASS,
+ gr0_base + ARM_SMMU_GR0_S2CR(sid));
+ }
+}
+
+static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
+ struct arm_smmu_master *master)
+{
+ int i, ret;
+ struct arm_smmu_device *parent, *smmu = smmu_domain->root_cfg.smmu;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ ret = arm_smmu_master_configure_smrs(smmu, master);
+ if (ret)
+ return ret;
+
+ /* Bypass the leaves */
+ smmu = smmu_domain->leaf_smmu;
+ while ((parent = find_parent_smmu(smmu))) {
+ /*
+ * We won't have a StreamID match for anything but the root
+ * smmu, so we only need to worry about StreamID indexing,
+ * where we must install bypass entries in the S2CRs.
+ */
+ if (smmu->features & ARM_SMMU_FEAT_STREAM_MATCH)
+ continue;
+
+ arm_smmu_bypass_stream_mapping(smmu, master);
+ smmu = parent;
+ }
+
+ /* Now we're at the root, time to point at our context bank */
+ for (i = 0; i < master->num_streamids; ++i) {
+ u32 idx, s2cr;
+ idx = master->smrs ? master->smrs[i].idx : master->streamids[i];
+ s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) |
+ (smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT);
+ writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
+ }
+
+ return 0;
+}
+
+static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
+ struct arm_smmu_master *master)
+{
+ struct arm_smmu_device *smmu = smmu_domain->root_cfg.smmu;
+
+ /*
+ * We *must* clear the S2CR first, because freeing the SMR means
+ * that it can be re-allocated immediately.
+ */
+ arm_smmu_bypass_stream_mapping(smmu, master);
+ arm_smmu_master_free_smrs(smmu, master);
+}
+
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ int ret = -EINVAL;
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_device *device_smmu = dev->archdata.iommu;
+ struct arm_smmu_master *master;
+
+ if (!device_smmu) {
+ dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
+ return -ENXIO;
+ }
+
+ /*
+ * Sanity check the domain. We don't currently support domains
+ * that cross between different SMMU chains.
+ */
+ spin_lock(&smmu_domain->lock);
+ if (!smmu_domain->leaf_smmu) {
+ /* Now that we have a master, we can finalise the domain */
+ ret = arm_smmu_init_domain_context(domain, dev);
+ if (IS_ERR_VALUE(ret))
+ goto err_unlock;
+
+ smmu_domain->leaf_smmu = device_smmu;
+ } else if (smmu_domain->leaf_smmu != device_smmu) {
+ dev_err(dev,
+ "cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
+ dev_name(smmu_domain->leaf_smmu->dev),
+ dev_name(device_smmu->dev));
+ goto err_unlock;
+ }
+ spin_unlock(&smmu_domain->lock);
+
+ /* Looks ok, so add the device to the domain */
+ master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node);
+ if (!master)
+ return -ENODEV;
+
+ return arm_smmu_domain_add_master(smmu_domain, master);
+
+err_unlock:
+ spin_unlock(&smmu_domain->lock);
+ return ret;
+}
+
+static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_master *master;
+
+ master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node);
+ if (master)
+ arm_smmu_domain_remove_master(smmu_domain, master);
+}
+
+static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
+ size_t size)
+{
+ unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+
+ /*
+ * If the SMMU can't walk tables in the CPU caches, treat them
+ * like non-coherent DMA since we need to flush the new entries
+ * all the way out to memory. There's no possibility of recursion
+ * here as the SMMU table walker will not be wired through another
+ * SMMU.
+ */
+ if (!(smmu->features & ARM_SMMU_FEAT_COHERENT_WALK))
+ dma_map_page(smmu->dev, virt_to_page(addr), offset, size,
+ DMA_TO_DEVICE);
+}
+
+static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
+ unsigned long end)
+{
+ return !(addr & ~ARM_SMMU_PTE_CONT_MASK) &&
+ (addr + ARM_SMMU_PTE_CONT_SIZE <= end);
+}
+
+static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
+ unsigned long addr, unsigned long end,
+ unsigned long pfn, int flags, int stage)
+{
+ pte_t *pte, *start;
+ pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF;
+
+ if (pmd_none(*pmd)) {
+ /* Allocate a new set of tables */
+ pgtable_t table = alloc_page(PGALLOC_GFP);
+ if (!table)
+ return -ENOMEM;
+
+ arm_smmu_flush_pgtable(smmu, page_address(table),
+ ARM_SMMU_PTE_HWTABLE_SIZE);
+ pgtable_page_ctor(table);
+ pmd_populate(NULL, pmd, table);
+ arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd));
+ }
+
+ if (stage == 1) {
+ pteval |= ARM_SMMU_PTE_AP_UNPRIV;
+ if (!(flags & IOMMU_WRITE) && (flags & IOMMU_READ))
+ pteval |= ARM_SMMU_PTE_AP_RDONLY;
+
+ if (flags & IOMMU_CACHE)
+ pteval |= (MAIR_ATTR_IDX_CACHE <<
+ ARM_SMMU_PTE_ATTRINDX_SHIFT);
+ } else {
+ pteval |= ARM_SMMU_PTE_HAP_FAULT;
+ if (flags & IOMMU_READ)
+ pteval |= ARM_SMMU_PTE_HAP_READ;
+ if (flags & IOMMU_WRITE)
+ pteval |= ARM_SMMU_PTE_HAP_WRITE;
+ if (flags & IOMMU_CACHE)
+ pteval |= ARM_SMMU_PTE_MEMATTR_OIWB;
+ else
+ pteval |= ARM_SMMU_PTE_MEMATTR_NC;
+ }
+
+ /* If no access, create a faulting entry to avoid TLB fills */
+ if (!(flags & (IOMMU_READ | IOMMU_WRITE)))
+ pteval &= ~ARM_SMMU_PTE_PAGE;
+
+ pteval |= ARM_SMMU_PTE_SH_IS;
+ start = pmd_page_vaddr(*pmd) + pte_index(addr);
+ pte = start;
+
+ /*
+ * Install the page table entries. This is fairly complicated
+ * since we attempt to make use of the contiguous hint in the
+ * ptes where possible. The contiguous hint indicates a series
+ * of ARM_SMMU_PTE_CONT_ENTRIES ptes mapping a physically
+ * contiguous region with the following constraints:
+ *
+ * - The region start is aligned to ARM_SMMU_PTE_CONT_SIZE
+ * - Each pte in the region has the contiguous hint bit set
+ *
+ * This complicates unmapping (also handled by this code, when
+ * neither IOMMU_READ or IOMMU_WRITE are set) because it is
+ * possible, yet highly unlikely, that a client may unmap only
+ * part of a contiguous range. This requires clearing of the
+ * contiguous hint bits in the range before installing the new
+ * faulting entries.
+ *
+ * Note that re-mapping an address range without first unmapping
+ * it is not supported, so TLB invalidation is not required here
+ * and is instead performed at unmap and domain-init time.
+ */
+ do {
+ int i = 1;
+ pteval &= ~ARM_SMMU_PTE_CONT;
+
+ if (arm_smmu_pte_is_contiguous_range(addr, end)) {
+ i = ARM_SMMU_PTE_CONT_ENTRIES;
+ pteval |= ARM_SMMU_PTE_CONT;
+ } else if (pte_val(*pte) &
+ (ARM_SMMU_PTE_CONT | ARM_SMMU_PTE_PAGE)) {
+ int j;
+ pte_t *cont_start;
+ unsigned long idx = pte_index(addr);
+
+ idx &= ~(ARM_SMMU_PTE_CONT_ENTRIES - 1);
+ cont_start = pmd_page_vaddr(*pmd) + idx;
+ for (j = 0; j < ARM_SMMU_PTE_CONT_ENTRIES; ++j)
+ pte_val(*(cont_start + j)) &= ~ARM_SMMU_PTE_CONT;
+
+ arm_smmu_flush_pgtable(smmu, cont_start,
+ sizeof(*pte) *
+ ARM_SMMU_PTE_CONT_ENTRIES);
+ }
+
+ do {
+ *pte = pfn_pte(pfn, __pgprot(pteval));
+ } while (pte++, pfn++, addr += PAGE_SIZE, --i);
+ } while (addr != end);
+
+ arm_smmu_flush_pgtable(smmu, start, sizeof(*pte) * (pte - start));
+ return 0;
+}
+
+static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
+ unsigned long addr, unsigned long end,
+ phys_addr_t phys, int flags, int stage)
+{
+ int ret;
+ pmd_t *pmd;
+ unsigned long next, pfn = __phys_to_pfn(phys);
+
+#ifndef __PAGETABLE_PMD_FOLDED
+ if (pud_none(*pud)) {
+ pmd = pmd_alloc_one(NULL, addr);
+ if (!pmd)
+ return -ENOMEM;
+ } else
+#endif
+ pmd = pmd_offset(pud, addr);
+
+ do {
+ next = pmd_addr_end(addr, end);
+ ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, end, pfn,
+ flags, stage);
+ pud_populate(NULL, pud, pmd);
+ arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud));
+ phys += next - addr;
+ } while (pmd++, addr = next, addr < end);
+
+ return ret;
+}
+
+static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
+ unsigned long addr, unsigned long end,
+ phys_addr_t phys, int flags, int stage)
+{
+ int ret = 0;
+ pud_t *pud;
+ unsigned long next;
+
+#ifndef __PAGETABLE_PUD_FOLDED
+ if (pgd_none(*pgd)) {
+ pud = pud_alloc_one(NULL, addr);
+ if (!pud)
+ return -ENOMEM;
+ } else
+#endif
+ pud = pud_offset(pgd, addr);
+
+ do {
+ next = pud_addr_end(addr, end);
+ ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys,
+ flags, stage);
+ pgd_populate(NULL, pud, pgd);
+ arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd));
+ phys += next - addr;
+ } while (pud++, addr = next, addr < end);
+
+ return ret;
+}
+
+static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
+ unsigned long iova, phys_addr_t paddr,
+ size_t size, int flags)
+{
+ int ret, stage;
+ unsigned long end;
+ phys_addr_t input_mask, output_mask;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ pgd_t *pgd = root_cfg->pgd;
+ struct arm_smmu_device *smmu = root_cfg->smmu;
+
+ if (root_cfg->cbar == CBAR_TYPE_S2_TRANS) {
+ stage = 2;
+ output_mask = (1ULL << smmu->s2_output_size) - 1;
+ } else {
+ stage = 1;
+ output_mask = (1ULL << smmu->s1_output_size) - 1;
+ }
+
+ if (!pgd)
+ return -EINVAL;
+
+ if (size & ~PAGE_MASK)
+ return -EINVAL;
+
+ input_mask = (1ULL << smmu->input_size) - 1;
+ if ((phys_addr_t)iova & ~input_mask)
+ return -ERANGE;
+
+ if (paddr & ~output_mask)
+ return -ERANGE;
+
+ spin_lock(&smmu_domain->lock);
+ pgd += pgd_index(iova);
+ end = iova + size;
+ do {
+ unsigned long next = pgd_addr_end(iova, end);
+
+ ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr,
+ flags, stage);
+ if (ret)
+ goto out_unlock;
+
+ paddr += next - iova;
+ iova = next;
+ } while (pgd++, iova != end);
+
+out_unlock:
+ spin_unlock(&smmu_domain->lock);
+
+ /* Ensure new page tables are visible to the hardware walker */
+ if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK)
+ dsb();
+
+ return ret;
+}
+
+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int flags)
+{
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_device *smmu = smmu_domain->leaf_smmu;
+
+ if (!smmu_domain || !smmu)
+ return -ENODEV;
+
+ /* Check for silent address truncation up the SMMU chain. */
+ if ((phys_addr_t)iova & ~smmu_domain->output_mask)
+ return -ERANGE;
+
+ return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, flags);
+}
+
+static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
+ size_t size)
+{
+ int ret;
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ struct arm_smmu_device *smmu = root_cfg->smmu;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0);
+ writel_relaxed(root_cfg->vmid, gr0_base + ARM_SMMU_GR0_TLBIVMID);
+ arm_smmu_tlb_sync(smmu);
+ return ret ? ret : size;
+}
+
+static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
+ dma_addr_t iova)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+ struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+ struct arm_smmu_device *smmu = root_cfg->smmu;
+
+ spin_lock(&smmu_domain->lock);
+ pgd = root_cfg->pgd;
+ if (!pgd)
+ goto err_unlock;
+
+ pgd += pgd_index(iova);
+ if (pgd_none_or_clear_bad(pgd))
+ goto err_unlock;
+
+ pud = pud_offset(pgd, iova);
+ if (pud_none_or_clear_bad(pud))
+ goto err_unlock;
+
+ pmd = pmd_offset(pud, iova);
+ if (pmd_none_or_clear_bad(pmd))
+ goto err_unlock;
+
+ pte = pmd_page_vaddr(*pmd) + pte_index(iova);
+ if (pte_none(pte))
+ goto err_unlock;
+
+ spin_unlock(&smmu_domain->lock);
+ return __pfn_to_phys(pte_pfn(*pte)) | (iova & ~PAGE_MASK);
+
+err_unlock:
+ spin_unlock(&smmu_domain->lock);
+ dev_warn(smmu->dev,
+ "invalid (corrupt?) page tables detected for iova 0x%llx\n",
+ (unsigned long long)iova);
+ return -EINVAL;
+}
+
+static int arm_smmu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ unsigned long caps = 0;
+ struct arm_smmu_domain *smmu_domain = domain->priv;
+
+ if (smmu_domain->root_cfg.smmu->features & ARM_SMMU_FEAT_COHERENT_WALK)
+ caps |= IOMMU_CAP_CACHE_COHERENCY;
+
+ return !!(cap & caps);
+}
+
+static int arm_smmu_add_device(struct device *dev)
+{
+ struct arm_smmu_device *child, *parent, *smmu;
+ struct arm_smmu_master *master = NULL;
+
+ spin_lock(&arm_smmu_devices_lock);
+ list_for_each_entry(parent, &arm_smmu_devices, list) {
+ smmu = parent;
+
+ /* Try to find a child of the current SMMU. */
+ list_for_each_entry(child, &arm_smmu_devices, list) {
+ if (child->parent_of_node == parent->dev->of_node) {
+ /* Does the child sit above our master? */
+ master = find_smmu_master(child, dev->of_node);
+ if (master) {
+ smmu = NULL;
+ break;
+ }
+ }
+ }
+
+ /* We found some children, so keep searching. */
+ if (!smmu) {
+ master = NULL;
+ continue;
+ }
+
+ master = find_smmu_master(smmu, dev->of_node);
+ if (master)
+ break;
+ }
+ spin_unlock(&arm_smmu_devices_lock);
+
+ if (!master)
+ return -ENODEV;
+
+ dev->archdata.iommu = smmu;
+ return 0;
+}
+
+static void arm_smmu_remove_device(struct device *dev)
+{
+ dev->archdata.iommu = NULL;
+}
+
+static struct iommu_ops arm_smmu_ops = {
+ .domain_init = arm_smmu_domain_init,
+ .domain_destroy = arm_smmu_domain_destroy,
+ .attach_dev = arm_smmu_attach_dev,
+ .detach_dev = arm_smmu_detach_dev,
+ .map = arm_smmu_map,
+ .unmap = arm_smmu_unmap,
+ .iova_to_phys = arm_smmu_iova_to_phys,
+ .domain_has_cap = arm_smmu_domain_has_cap,
+ .add_device = arm_smmu_add_device,
+ .remove_device = arm_smmu_remove_device,
+ .pgsize_bitmap = (SECTION_SIZE |
+ ARM_SMMU_PTE_CONT_SIZE |
+ PAGE_SIZE),
+};
+
+static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+ int i = 0;
+ u32 scr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+
+ /* Mark all SMRn as invalid and all S2CRn as bypass */
+ for (i = 0; i < smmu->num_mapping_groups; ++i) {
+ writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(i));
+ writel_relaxed(S2CR_TYPE_BYPASS, gr0_base + ARM_SMMU_GR0_S2CR(i));
+ }
+
+ /* Invalidate the TLB, just in case */
+ writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL);
+ writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
+ writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
+
+ /* Enable fault reporting */
+ scr0 |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
+
+ /* Disable TLB broadcasting. */
+ scr0 |= (sCR0_VMIDPNE | sCR0_PTM);
+
+ /* Enable client access, but bypass when no mapping is found */
+ scr0 &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
+
+ /* Disable forced broadcasting */
+ scr0 &= ~sCR0_FB;
+
+ /* Don't upgrade barriers */
+ scr0 &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
+
+ /* Push the button */
+ arm_smmu_tlb_sync(smmu);
+ writel(scr0, gr0_base + ARM_SMMU_GR0_sCR0);
+}
+
+static int arm_smmu_id_size_to_bits(int size)
+{
+ switch (size) {
+ case 0:
+ return 32;
+ case 1:
+ return 36;
+ case 2:
+ return 40;
+ case 3:
+ return 42;
+ case 4:
+ return 44;
+ case 5:
+ default:
+ return 48;
+ }
+}
+
+static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
+{
+ unsigned long size;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+ u32 id;
+
+ dev_notice(smmu->dev, "probing hardware configuration...\n");
+
+ /* Primecell ID */
+ id = readl_relaxed(gr0_base + ARM_SMMU_GR0_PIDR2);
+ smmu->version = ((id >> PIDR2_ARCH_SHIFT) & PIDR2_ARCH_MASK) + 1;
+ dev_notice(smmu->dev, "SMMUv%d with:\n", smmu->version);
+
+ /* ID0 */
+ id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID0);
+#ifndef CONFIG_64BIT
+ if (((id >> ID0_PTFS_SHIFT) & ID0_PTFS_MASK) == ID0_PTFS_V8_ONLY) {
+ dev_err(smmu->dev, "\tno v7 descriptor support!\n");
+ return -ENODEV;
+ }
+#endif
+ if (id & ID0_S1TS) {
+ smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
+ dev_notice(smmu->dev, "\tstage 1 translation\n");
+ }
+
+ if (id & ID0_S2TS) {
+ smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
+ dev_notice(smmu->dev, "\tstage 2 translation\n");
+ }
+
+ if (id & ID0_NTS) {
+ smmu->features |= ARM_SMMU_FEAT_TRANS_NESTED;
+ dev_notice(smmu->dev, "\tnested translation\n");
+ }
+
+ if (!(smmu->features &
+ (ARM_SMMU_FEAT_TRANS_S1 | ARM_SMMU_FEAT_TRANS_S2 |
+ ARM_SMMU_FEAT_TRANS_NESTED))) {
+ dev_err(smmu->dev, "\tno translation support!\n");
+ return -ENODEV;
+ }
+
+ if (id & ID0_CTTW) {
+ smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK;
+ dev_notice(smmu->dev, "\tcoherent table walk\n");
+ }
+
+ if (id & ID0_SMS) {
+ u32 smr, sid, mask;
+
+ smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
+ smmu->num_mapping_groups = (id >> ID0_NUMSMRG_SHIFT) &
+ ID0_NUMSMRG_MASK;
+ if (smmu->num_mapping_groups == 0) {
+ dev_err(smmu->dev,
+ "stream-matching supported, but no SMRs present!\n");
+ return -ENODEV;
+ }
+
+ smr = SMR_MASK_MASK << SMR_MASK_SHIFT;
+ smr |= (SMR_ID_MASK << SMR_ID_SHIFT);
+ writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
+ smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+
+ mask = (smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK;
+ sid = (smr >> SMR_ID_SHIFT) & SMR_ID_MASK;
+ if ((mask & sid) != sid) {
+ dev_err(smmu->dev,
+ "SMR mask bits (0x%x) insufficient for ID field (0x%x)\n",
+ mask, sid);
+ return -ENODEV;
+ }
+
+ dev_notice(smmu->dev,
+ "\tstream matching with %u register groups, mask 0x%x",
+ smmu->num_mapping_groups, mask);
+ }
+
+ /* ID1 */
+ id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID1);
+ smmu->pagesize = (id & ID1_PAGESIZE) ? SZ_64K : SZ_4K;
+
+ /* Check that we ioremapped enough */
+ size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1);
+ size *= (smmu->pagesize << 1);
+ if (smmu->size < size)
+ dev_warn(smmu->dev,
+ "device is 0x%lx bytes but only mapped 0x%lx!\n",
+ size, smmu->size);
+
+ smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) &
+ ID1_NUMS2CB_MASK;
+ smmu->num_context_banks = (id >> ID1_NUMCB_SHIFT) & ID1_NUMCB_MASK;
+ if (smmu->num_s2_context_banks > smmu->num_context_banks) {
+ dev_err(smmu->dev, "impossible number of S2 context banks!\n");
+ return -ENODEV;
+ }
+ dev_notice(smmu->dev, "\t%u context banks (%u stage-2 only)\n",
+ smmu->num_context_banks, smmu->num_s2_context_banks);
+
+ /* ID2 */
+ id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2);
+ size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
+
+ /*
+ * Stage-1 output limited by stage-2 input size due to pgd
+ * allocation (PTRS_PER_PGD).
+ */
+#ifdef CONFIG_64BIT
+ /* Current maximum output size of 39 bits */
+ smmu->s1_output_size = min(39UL, size);
+#else
+ smmu->s1_output_size = min(32UL, size);
+#endif
+
+ /* The stage-2 output mask is also applied for bypass */
+ size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
+ smmu->s2_output_size = min((unsigned long)PHYS_MASK_SHIFT, size);
+
+ if (smmu->version == 1) {
+ smmu->input_size = 32;
+ } else {
+#ifdef CONFIG_64BIT
+ size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
+ size = min(39, arm_smmu_id_size_to_bits(size));
+#else
+ size = 32;
+#endif
+ smmu->input_size = size;
+
+ if ((PAGE_SIZE == SZ_4K && !(id & ID2_PTFS_4K)) ||
+ (PAGE_SIZE == SZ_64K && !(id & ID2_PTFS_64K)) ||
+ (PAGE_SIZE != SZ_4K && PAGE_SIZE != SZ_64K)) {
+ dev_err(smmu->dev, "CPU page size 0x%lx unsupported\n",
+ PAGE_SIZE);
+ return -ENODEV;
+ }
+ }
+
+ dev_notice(smmu->dev,
+ "\t%lu-bit VA, %lu-bit IPA, %lu-bit PA\n",
+ smmu->input_size, smmu->s1_output_size, smmu->s2_output_size);
+ return 0;
+}
+
+static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct arm_smmu_device *smmu;
+ struct device_node *dev_node;
+ struct device *dev = &pdev->dev;
+ struct rb_node *node;
+ struct of_phandle_args masterspec;
+ int num_irqs, i, err;
+
+ smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
+ if (!smmu) {
+ dev_err(dev, "failed to allocate arm_smmu_device\n");
+ return -ENOMEM;
+ }
+ smmu->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing base address/size\n");
+ return -ENODEV;
+ }
+
+ smmu->size = resource_size(res);
+ smmu->base = devm_request_and_ioremap(dev, res);
+ if (!smmu->base)
+ return -EADDRNOTAVAIL;
+
+ if (of_property_read_u32(dev->of_node, "#global-interrupts",
+ &smmu->num_global_irqs)) {
+ dev_err(dev, "missing #global-interrupts property\n");
+ return -ENODEV;
+ }
+
+ num_irqs = 0;
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) {
+ num_irqs++;
+ if (num_irqs > smmu->num_global_irqs)
+ smmu->num_context_irqs++;
+ }
+
+ if (num_irqs < smmu->num_global_irqs) {
+ dev_warn(dev, "found %d interrupts but expected at least %d\n",
+ num_irqs, smmu->num_global_irqs);
+ smmu->num_global_irqs = num_irqs;
+ }
+ smmu->num_context_irqs = num_irqs - smmu->num_global_irqs;
+
+ smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
+ GFP_KERNEL);
+ if (!smmu->irqs) {
+ dev_err(dev, "failed to allocate %d irqs\n", num_irqs);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_irqs; ++i) {
+ int irq = platform_get_irq(pdev, i);
+ if (irq < 0) {
+ dev_err(dev, "failed to get irq index %d\n", i);
+ return -ENODEV;
+ }
+ smmu->irqs[i] = irq;
+ }
+
+ i = 0;
+ smmu->masters = RB_ROOT;
+ while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters",
+ "#stream-id-cells", i,
+ &masterspec)) {
+ err = register_smmu_master(smmu, dev, &masterspec);
+ if (err) {
+ dev_err(dev, "failed to add master %s\n",
+ masterspec.np->name);
+ goto out_put_masters;
+ }
+
+ i++;
+ }
+ dev_notice(dev, "registered %d master devices\n", i);
+
+ if ((dev_node = of_parse_phandle(dev->of_node, "smmu-parent", 0)))
+ smmu->parent_of_node = dev_node;
+
+ err = arm_smmu_device_cfg_probe(smmu);
+ if (err)
+ goto out_put_parent;
+
+ if (smmu->version > 1 &&
+ smmu->num_context_banks != smmu->num_context_irqs) {
+ dev_err(dev,
+ "found only %d context interrupt(s) but %d required\n",
+ smmu->num_context_irqs, smmu->num_context_banks);
+ goto out_put_parent;
+ }
+
+ arm_smmu_device_reset(smmu);
+
+ for (i = 0; i < smmu->num_global_irqs; ++i) {
+ err = request_irq(smmu->irqs[i],
+ arm_smmu_global_fault,
+ IRQF_SHARED,
+ "arm-smmu global fault",
+ smmu);
+ if (err) {
+ dev_err(dev, "failed to request global IRQ %d (%u)\n",
+ i, smmu->irqs[i]);
+ goto out_free_irqs;
+ }
+ }
+
+ INIT_LIST_HEAD(&smmu->list);
+ spin_lock(&arm_smmu_devices_lock);
+ list_add(&smmu->list, &arm_smmu_devices);
+ spin_unlock(&arm_smmu_devices_lock);
+ return 0;
+
+out_free_irqs:
+ while (i--)
+ free_irq(smmu->irqs[i], smmu);
+
+out_put_parent:
+ if (smmu->parent_of_node)
+ of_node_put(smmu->parent_of_node);
+
+out_put_masters:
+ for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
+ struct arm_smmu_master *master;
+ master = container_of(node, struct arm_smmu_master, node);
+ of_node_put(master->of_node);
+ }
+
+ return err;
+}
+
+static int arm_smmu_device_remove(struct platform_device *pdev)
+{
+ int i;
+ struct device *dev = &pdev->dev;
+ struct arm_smmu_device *curr, *smmu = NULL;
+ struct rb_node *node;
+
+ spin_lock(&arm_smmu_devices_lock);
+ list_for_each_entry(curr, &arm_smmu_devices, list) {
+ if (curr->dev == dev) {
+ smmu = curr;
+ list_del(&smmu->list);
+ break;
+ }
+ }
+ spin_unlock(&arm_smmu_devices_lock);
+
+ if (!smmu)
+ return -ENODEV;
+
+ if (smmu->parent_of_node)
+ of_node_put(smmu->parent_of_node);
+
+ for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
+ struct arm_smmu_master *master;
+ master = container_of(node, struct arm_smmu_master, node);
+ of_node_put(master->of_node);
+ }
+
+ if (!bitmap_empty(smmu->vmid_map, ARM_SMMU_NUM_VMIDS))
+ dev_err(dev, "removing device with active domains!\n");
+
+ for (i = 0; i < smmu->num_global_irqs; ++i)
+ free_irq(smmu->irqs[i], smmu);
+
+ /* Turn the thing off */
+ writel(sCR0_CLIENTPD, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_sCR0);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id arm_smmu_of_match[] = {
+ { .compatible = "arm,smmu-v1", },
+ { .compatible = "arm,smmu-v2", },
+ { .compatible = "arm,mmu-400", },
+ { .compatible = "arm,mmu-500", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
+#endif
+
+static struct platform_driver arm_smmu_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "arm-smmu",
+ .of_match_table = of_match_ptr(arm_smmu_of_match),
+ },
+ .probe = arm_smmu_device_dt_probe,
+ .remove = arm_smmu_device_remove,
+};
+
+static int __init arm_smmu_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&arm_smmu_driver);
+ if (ret)
+ return ret;
+
+ /* Oh, for a proper bus abstraction */
+ if (!iommu_present(&platform_bus_type));
+ bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+
+ if (!iommu_present(&amba_bustype));
+ bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+
+ return 0;
+}
+
+static void __exit arm_smmu_exit(void)
+{
+ return platform_driver_unregister(&arm_smmu_driver);
+}
+
+module_init(arm_smmu_init);
+module_exit(arm_smmu_exit);
+
+MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index a7967ce..785675a 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -309,6 +309,7 @@
struct acpi_table_dmar *dmar;
struct acpi_dmar_header *entry_header;
int ret = 0;
+ int drhd_count = 0;
/*
* Do it again, earlier dmar_tbl mapping could be mapped with
@@ -347,6 +348,7 @@
switch (entry_header->type) {
case ACPI_DMAR_TYPE_HARDWARE_UNIT:
+ drhd_count++;
ret = dmar_parse_one_drhd(entry_header);
break;
case ACPI_DMAR_TYPE_RESERVED_MEMORY:
@@ -371,6 +373,8 @@
entry_header = ((void *)entry_header + entry_header->length);
}
+ if (drhd_count == 0)
+ pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
return ret;
}
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index b4f0e28..eec0d3e 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -4182,14 +4182,27 @@
/*
* If it's a multifunction device that does not support our
- * required ACS flags, add to the same group as function 0.
+ * required ACS flags, add to the same group as lowest numbered
+ * function that also does not suport the required ACS flags.
*/
if (dma_pdev->multifunction &&
- !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
- swap_pci_ref(&dma_pdev,
- pci_get_slot(dma_pdev->bus,
- PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
- 0)));
+ !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
+ u8 i, slot = PCI_SLOT(dma_pdev->devfn);
+
+ for (i = 0; i < 8; i++) {
+ struct pci_dev *tmp;
+
+ tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
+ if (!tmp)
+ continue;
+
+ if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
+ swap_pci_ref(&dma_pdev, tmp);
+ break;
+ }
+ pci_dev_put(tmp);
+ }
+ }
/*
* Devices on the root bus go through the iommu. If that's not us,
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 5b19b2d..f71673d 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -664,8 +664,7 @@
*/
if (x2apic_present)
- WARN(1, KERN_WARNING
- "Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
+ pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
return -1;
}
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index d8f98b1..fbe9ca7 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -754,6 +754,38 @@
}
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
+static size_t iommu_pgsize(struct iommu_domain *domain,
+ unsigned long addr_merge, size_t size)
+{
+ unsigned int pgsize_idx;
+ size_t pgsize;
+
+ /* Max page size that still fits into 'size' */
+ pgsize_idx = __fls(size);
+
+ /* need to consider alignment requirements ? */
+ if (likely(addr_merge)) {
+ /* Max page size allowed by address */
+ unsigned int align_pgsize_idx = __ffs(addr_merge);
+ pgsize_idx = min(pgsize_idx, align_pgsize_idx);
+ }
+
+ /* build a mask of acceptable page sizes */
+ pgsize = (1UL << (pgsize_idx + 1)) - 1;
+
+ /* throw away page sizes not supported by the hardware */
+ pgsize &= domain->ops->pgsize_bitmap;
+
+ /* make sure we're still sane */
+ BUG_ON(!pgsize);
+
+ /* pick the biggest page */
+ pgsize_idx = __fls(pgsize);
+ pgsize = 1UL << pgsize_idx;
+
+ return pgsize;
+}
+
int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
@@ -775,45 +807,18 @@
* size of the smallest page supported by the hardware
*/
if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
- pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
- "0x%x\n", iova, (unsigned long)paddr,
- (unsigned long)size, min_pagesz);
+ pr_err("unaligned: iova 0x%lx pa 0x%pa size 0x%zx min_pagesz 0x%x\n",
+ iova, &paddr, size, min_pagesz);
return -EINVAL;
}
- pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
- (unsigned long)paddr, (unsigned long)size);
+ pr_debug("map: iova 0x%lx pa 0x%pa size 0x%zx\n", iova, &paddr, size);
while (size) {
- unsigned long pgsize, addr_merge = iova | paddr;
- unsigned int pgsize_idx;
+ size_t pgsize = iommu_pgsize(domain, iova | paddr, size);
- /* Max page size that still fits into 'size' */
- pgsize_idx = __fls(size);
-
- /* need to consider alignment requirements ? */
- if (likely(addr_merge)) {
- /* Max page size allowed by both iova and paddr */
- unsigned int align_pgsize_idx = __ffs(addr_merge);
-
- pgsize_idx = min(pgsize_idx, align_pgsize_idx);
- }
-
- /* build a mask of acceptable page sizes */
- pgsize = (1UL << (pgsize_idx + 1)) - 1;
-
- /* throw away page sizes not supported by the hardware */
- pgsize &= domain->ops->pgsize_bitmap;
-
- /* make sure we're still sane */
- BUG_ON(!pgsize);
-
- /* pick the biggest page */
- pgsize_idx = __fls(pgsize);
- pgsize = 1UL << pgsize_idx;
-
- pr_debug("mapping: iova 0x%lx pa 0x%lx pgsize %lu\n", iova,
- (unsigned long)paddr, pgsize);
+ pr_debug("mapping: iova 0x%lx pa 0x%pa pgsize 0x%zx\n",
+ iova, &paddr, pgsize);
ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
if (ret)
@@ -850,27 +855,26 @@
* by the hardware
*/
if (!IS_ALIGNED(iova | size, min_pagesz)) {
- pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
- iova, (unsigned long)size, min_pagesz);
+ pr_err("unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
+ iova, size, min_pagesz);
return -EINVAL;
}
- pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
- (unsigned long)size);
+ pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size);
/*
* Keep iterating until we either unmap 'size' bytes (or more)
* or we hit an area that isn't mapped.
*/
while (unmapped < size) {
- size_t left = size - unmapped;
+ size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
- unmapped_page = domain->ops->unmap(domain, iova, left);
+ unmapped_page = domain->ops->unmap(domain, iova, pgsize);
if (!unmapped_page)
break;
- pr_debug("unmapped: iova 0x%lx size %lx\n", iova,
- (unsigned long)unmapped_page);
+ pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
+ iova, unmapped_page);
iova += unmapped_page;
unmapped += unmapped_page;
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index e02e5d7..0ba3766 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -833,16 +833,15 @@
iopgd = iopgd_offset(obj, da);
if (!iopgd_is_table(*iopgd)) {
- dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
- "*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
+ dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:px%08x\n",
+ obj->name, errs, da, iopgd, *iopgd);
return IRQ_NONE;
}
iopte = iopte_offset(iopgd, da);
- dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
- "pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
- iopte, *iopte);
+ dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x pte:0x%p *pte:0x%08x\n",
+ obj->name, errs, da, iopgd, *iopgd, iopte, *iopte);
return IRQ_NONE;
}
@@ -1235,14 +1234,16 @@
else if (iopte_is_large(*pte))
ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
else
- dev_err(dev, "bogus pte 0x%x, da 0x%lx", *pte, da);
+ dev_err(dev, "bogus pte 0x%x, da 0x%llx", *pte,
+ (unsigned long long)da);
} else {
if (iopgd_is_section(*pgd))
ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
else if (iopgd_is_super(*pgd))
ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
else
- dev_err(dev, "bogus pgd 0x%x, da 0x%lx", *pgd, da);
+ dev_err(dev, "bogus pgd 0x%x, da 0x%llx", *pgd,
+ (unsigned long long)da);
}
return ret;
diff --git a/drivers/iommu/omap-iopgtable.h b/drivers/iommu/omap-iopgtable.h
index cd4ae9e..f4003d5 100644
--- a/drivers/iommu/omap-iopgtable.h
+++ b/drivers/iommu/omap-iopgtable.h
@@ -95,4 +95,4 @@
#define iopte_offset(iopgd, da) (iopgd_page_vaddr(iopgd) + iopte_index(da))
#define to_iommu(dev) \
- (struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
+ ((struct omap_iommu *)platform_get_drvdata(to_platform_device(dev)))
diff --git a/drivers/iommu/omap-iovmm.c b/drivers/iommu/omap-iovmm.c
index 46d87569..d147259 100644
--- a/drivers/iommu/omap-iovmm.c
+++ b/drivers/iommu/omap-iovmm.c
@@ -102,8 +102,8 @@
}
if (i && sg->offset) {
- pr_err("%s: sg[%d] offset not allowed in internal "
- "entries\n", __func__, i);
+ pr_err("%s: sg[%d] offset not allowed in internal entries\n",
+ __func__, i);
return 0;
}
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 2065ef6..e65c41a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
+obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c
new file mode 100644
index 0000000..5552fc2
--- /dev/null
+++ b/drivers/irqchip/irq-moxart.c
@@ -0,0 +1,117 @@
+/*
+ * MOXA ART SoCs IRQ chip driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.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/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+
+#include <asm/exception.h>
+
+#include "irqchip.h"
+
+#define IRQ_SOURCE_REG 0
+#define IRQ_MASK_REG 0x04
+#define IRQ_CLEAR_REG 0x08
+#define IRQ_MODE_REG 0x0c
+#define IRQ_LEVEL_REG 0x10
+#define IRQ_STATUS_REG 0x14
+
+#define FIQ_SOURCE_REG 0x20
+#define FIQ_MASK_REG 0x24
+#define FIQ_CLEAR_REG 0x28
+#define FIQ_MODE_REG 0x2c
+#define FIQ_LEVEL_REG 0x30
+#define FIQ_STATUS_REG 0x34
+
+
+struct moxart_irq_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+ unsigned int interrupt_mask;
+};
+
+static struct moxart_irq_data intc;
+
+static asmlinkage void __exception_irq_entry handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat;
+ int hwirq;
+
+ irqstat = readl(intc.base + IRQ_STATUS_REG);
+
+ while (irqstat) {
+ hwirq = ffs(irqstat) - 1;
+ handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+ irqstat &= ~(1 << hwirq);
+ }
+}
+
+static int __init moxart_of_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+ struct irq_chip_generic *gc;
+
+ intc.base = of_iomap(node, 0);
+ if (!intc.base) {
+ pr_err("%s: unable to map IC registers\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ intc.domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops,
+ intc.base);
+ if (!intc.domain) {
+ pr_err("%s: unable to create IRQ domain\n", node->full_name);
+ return -EINVAL;
+ }
+
+ ret = irq_alloc_domain_generic_chips(intc.domain, 32, 1,
+ "MOXARTINTC", handle_edge_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("%s: could not allocate generic chip\n",
+ node->full_name);
+ irq_domain_remove(intc.domain);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "interrupt-mask",
+ &intc.interrupt_mask);
+ if (ret)
+ pr_err("%s: could not read interrupt-mask DT property\n",
+ node->full_name);
+
+ gc = irq_get_domain_generic_chip(intc.domain, 0);
+
+ gc->reg_base = intc.base;
+ gc->chip_types[0].regs.mask = IRQ_MASK_REG;
+ gc->chip_types[0].regs.ack = IRQ_CLEAR_REG;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ writel(0, intc.base + IRQ_MASK_REG);
+ writel(0xffffffff, intc.base + IRQ_CLEAR_REG);
+
+ writel(intc.interrupt_mask, intc.base + IRQ_MODE_REG);
+ writel(intc.interrupt_mask, intc.base + IRQ_LEVEL_REG);
+
+ set_handle_irq(handle_irq);
+
+ return 0;
+}
+IRQCHIP_DECLARE(moxa_moxart_ic, "moxa,moxart-ic", moxart_of_intc_init);
diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c
index 8d0c8b3..70bdf6e 100644
--- a/drivers/irqchip/irq-nvic.c
+++ b/drivers/irqchip/irq-nvic.c
@@ -84,7 +84,7 @@
return -ENOMEM;
}
- ret = irq_alloc_domain_generic_chips(nvic_irq_domain, 32, numbanks,
+ ret = irq_alloc_domain_generic_chips(nvic_irq_domain, 32, 1,
"nvic_irq", handle_fasteoi_irq,
clr, 0, IRQ_GC_INIT_MASK_CACHE);
if (ret) {
diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
index b66d4ae..a5438d8 100644
--- a/drivers/irqchip/irq-sun4i.c
+++ b/drivers/irqchip/irq-sun4i.c
@@ -38,7 +38,7 @@
static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs);
-void sun4i_irq_ack(struct irq_data *irqd)
+static void sun4i_irq_ack(struct irq_data *irqd)
{
unsigned int irq = irqd_to_hwirq(irqd);
unsigned int irq_off = irq % 32;
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index d970595..1846e7d 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -178,7 +178,8 @@
.xlate = irq_domain_xlate_onecell,
};
-asmlinkage void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs)
+static asmlinkage
+void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs)
{
u32 stat, i;
int irqnr, virq;
@@ -203,7 +204,8 @@
}
}
-int __init vt8500_irq_init(struct device_node *node, struct device_node *parent)
+static int __init vt8500_irq_init(struct device_node *node,
+ struct device_node *parent)
{
int irq, i;
struct device_node *np = node;
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 3bfc8f1..30b426e 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -412,4 +412,18 @@
If unsure, say N.
+config DM_SWITCH
+ tristate "Switch target support (EXPERIMENTAL)"
+ depends on BLK_DEV_DM
+ ---help---
+ This device-mapper target creates a device that supports an arbitrary
+ mapping of fixed-size regions of I/O across a fixed set of paths.
+ The path used for any specific region can be switched dynamically
+ by sending the target a message.
+
+ To compile this code as a module, choose M here: the module will
+ be called dm-switch.
+
+ If unsure, say N.
+
endif # MD
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 1439fd4..5ef78ef 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -40,6 +40,7 @@
obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o
obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o
+obj-$(CONFIG_DM_SWITCH) += dm-switch.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
obj-$(CONFIG_DM_PERSISTENT_DATA) += persistent-data/
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 0387e05..5227e07 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -145,6 +145,7 @@
unsigned long state;
unsigned long last_accessed;
struct dm_bufio_client *c;
+ struct list_head write_list;
struct bio bio;
struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
};
@@ -349,7 +350,7 @@
if (gfp_mask & __GFP_NORETRY)
noio_flag = memalloc_noio_save();
- ptr = __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+ ptr = __vmalloc(c->block_size, gfp_mask | __GFP_HIGHMEM, PAGE_KERNEL);
if (gfp_mask & __GFP_NORETRY)
memalloc_noio_restore(noio_flag);
@@ -630,7 +631,8 @@
* - Submit our write and don't wait on it. We set B_WRITING indicating
* that there is a write in progress.
*/
-static void __write_dirty_buffer(struct dm_buffer *b)
+static void __write_dirty_buffer(struct dm_buffer *b,
+ struct list_head *write_list)
{
if (!test_bit(B_DIRTY, &b->state))
return;
@@ -639,7 +641,24 @@
wait_on_bit_lock(&b->state, B_WRITING,
do_io_schedule, TASK_UNINTERRUPTIBLE);
- submit_io(b, WRITE, b->block, write_endio);
+ if (!write_list)
+ submit_io(b, WRITE, b->block, write_endio);
+ else
+ list_add_tail(&b->write_list, write_list);
+}
+
+static void __flush_write_list(struct list_head *write_list)
+{
+ struct blk_plug plug;
+ blk_start_plug(&plug);
+ while (!list_empty(write_list)) {
+ struct dm_buffer *b =
+ list_entry(write_list->next, struct dm_buffer, write_list);
+ list_del(&b->write_list);
+ submit_io(b, WRITE, b->block, write_endio);
+ dm_bufio_cond_resched();
+ }
+ blk_finish_plug(&plug);
}
/*
@@ -655,7 +674,7 @@
return;
wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE);
- __write_dirty_buffer(b);
+ __write_dirty_buffer(b, NULL);
wait_on_bit(&b->state, B_WRITING, do_io_schedule, TASK_UNINTERRUPTIBLE);
}
@@ -802,7 +821,8 @@
wake_up(&c->free_buffer_wait);
}
-static void __write_dirty_buffers_async(struct dm_bufio_client *c, int no_wait)
+static void __write_dirty_buffers_async(struct dm_bufio_client *c, int no_wait,
+ struct list_head *write_list)
{
struct dm_buffer *b, *tmp;
@@ -818,7 +838,7 @@
if (no_wait && test_bit(B_WRITING, &b->state))
return;
- __write_dirty_buffer(b);
+ __write_dirty_buffer(b, write_list);
dm_bufio_cond_resched();
}
}
@@ -853,7 +873,8 @@
* If we are over threshold_buffers, start freeing buffers.
* If we're over "limit_buffers", block until we get under the limit.
*/
-static void __check_watermark(struct dm_bufio_client *c)
+static void __check_watermark(struct dm_bufio_client *c,
+ struct list_head *write_list)
{
unsigned long threshold_buffers, limit_buffers;
@@ -872,7 +893,7 @@
}
if (c->n_buffers[LIST_DIRTY] > threshold_buffers)
- __write_dirty_buffers_async(c, 1);
+ __write_dirty_buffers_async(c, 1, write_list);
}
/*
@@ -897,7 +918,8 @@
*--------------------------------------------------------------*/
static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
- enum new_flag nf, int *need_submit)
+ enum new_flag nf, int *need_submit,
+ struct list_head *write_list)
{
struct dm_buffer *b, *new_b = NULL;
@@ -924,7 +946,7 @@
goto found_buffer;
}
- __check_watermark(c);
+ __check_watermark(c, write_list);
b = new_b;
b->hold_count = 1;
@@ -992,10 +1014,14 @@
int need_submit;
struct dm_buffer *b;
+ LIST_HEAD(write_list);
+
dm_bufio_lock(c);
- b = __bufio_new(c, block, nf, &need_submit);
+ b = __bufio_new(c, block, nf, &need_submit, &write_list);
dm_bufio_unlock(c);
+ __flush_write_list(&write_list);
+
if (!b)
return b;
@@ -1047,6 +1073,8 @@
{
struct blk_plug plug;
+ LIST_HEAD(write_list);
+
BUG_ON(dm_bufio_in_request());
blk_start_plug(&plug);
@@ -1055,7 +1083,15 @@
for (; n_blocks--; block++) {
int need_submit;
struct dm_buffer *b;
- b = __bufio_new(c, block, NF_PREFETCH, &need_submit);
+ b = __bufio_new(c, block, NF_PREFETCH, &need_submit,
+ &write_list);
+ if (unlikely(!list_empty(&write_list))) {
+ dm_bufio_unlock(c);
+ blk_finish_plug(&plug);
+ __flush_write_list(&write_list);
+ blk_start_plug(&plug);
+ dm_bufio_lock(c);
+ }
if (unlikely(b != NULL)) {
dm_bufio_unlock(c);
@@ -1069,7 +1105,6 @@
goto flush_plug;
dm_bufio_lock(c);
}
-
}
dm_bufio_unlock(c);
@@ -1126,11 +1161,14 @@
void dm_bufio_write_dirty_buffers_async(struct dm_bufio_client *c)
{
+ LIST_HEAD(write_list);
+
BUG_ON(dm_bufio_in_request());
dm_bufio_lock(c);
- __write_dirty_buffers_async(c, 0);
+ __write_dirty_buffers_async(c, 0, &write_list);
dm_bufio_unlock(c);
+ __flush_write_list(&write_list);
}
EXPORT_SYMBOL_GPL(dm_bufio_write_dirty_buffers_async);
@@ -1147,8 +1185,13 @@
unsigned long buffers_processed = 0;
struct dm_buffer *b, *tmp;
+ LIST_HEAD(write_list);
+
dm_bufio_lock(c);
- __write_dirty_buffers_async(c, 0);
+ __write_dirty_buffers_async(c, 0, &write_list);
+ dm_bufio_unlock(c);
+ __flush_write_list(&write_list);
+ dm_bufio_lock(c);
again:
list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_DIRTY], lru_list) {
@@ -1274,7 +1317,7 @@
BUG_ON(!b->hold_count);
BUG_ON(test_bit(B_READING, &b->state));
- __write_dirty_buffer(b);
+ __write_dirty_buffer(b, NULL);
if (b->hold_count == 1) {
wait_on_bit(&b->state, B_WRITING,
do_io_schedule, TASK_UNINTERRUPTIBLE);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index df44b60..0df3ec0 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -425,6 +425,10 @@
return cache->sectors_per_block_shift >= 0;
}
+/* gcc on ARM generates spurious references to __udivdi3 and __umoddi3 */
+#if defined(CONFIG_ARM) && __GNUC__ == 4 && __GNUC_MINOR__ <= 6
+__always_inline
+#endif
static dm_block_t block_div(dm_block_t b, uint32_t n)
{
do_div(b, n);
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index 7fcf21c..c80a0ec 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -176,7 +176,7 @@
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (!fc) {
- ti->error = "Cannot allocate linear context";
+ ti->error = "Cannot allocate context";
return -ENOMEM;
}
fc->start_time = jiffies;
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index aa04f02..f1b7586 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -36,6 +36,14 @@
struct dm_table *new_map;
};
+/*
+ * A dummy definition to make RCU happy.
+ * struct dm_table should never be dereferenced in this file.
+ */
+struct dm_table {
+ int undefined__;
+};
+
struct vers_iter {
size_t param_size;
struct dm_target_versions *vers, *old_vers;
@@ -242,9 +250,10 @@
return -EBUSY;
}
-static void __hash_remove(struct hash_cell *hc)
+static struct dm_table *__hash_remove(struct hash_cell *hc)
{
struct dm_table *table;
+ int srcu_idx;
/* remove from the dev hash */
list_del(&hc->uuid_list);
@@ -253,16 +262,18 @@
dm_set_mdptr(hc->md, NULL);
mutex_unlock(&dm_hash_cells_mutex);
- table = dm_get_live_table(hc->md);
- if (table) {
+ table = dm_get_live_table(hc->md, &srcu_idx);
+ if (table)
dm_table_event(table);
- dm_table_put(table);
- }
+ dm_put_live_table(hc->md, srcu_idx);
+ table = NULL;
if (hc->new_map)
- dm_table_destroy(hc->new_map);
+ table = hc->new_map;
dm_put(hc->md);
free_cell(hc);
+
+ return table;
}
static void dm_hash_remove_all(int keep_open_devices)
@@ -270,6 +281,7 @@
int i, dev_skipped;
struct hash_cell *hc;
struct mapped_device *md;
+ struct dm_table *t;
retry:
dev_skipped = 0;
@@ -287,10 +299,14 @@
continue;
}
- __hash_remove(hc);
+ t = __hash_remove(hc);
up_write(&_hash_lock);
+ if (t) {
+ dm_sync_table(md);
+ dm_table_destroy(t);
+ }
dm_put(md);
if (likely(keep_open_devices))
dm_destroy(md);
@@ -356,6 +372,7 @@
struct dm_table *table;
struct mapped_device *md;
unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
+ int srcu_idx;
/*
* duplicate new.
@@ -418,11 +435,10 @@
/*
* Wake up any dm event waiters.
*/
- table = dm_get_live_table(hc->md);
- if (table) {
+ table = dm_get_live_table(hc->md, &srcu_idx);
+ if (table)
dm_table_event(table);
- dm_table_put(table);
- }
+ dm_put_live_table(hc->md, srcu_idx);
if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr))
param->flags |= DM_UEVENT_GENERATED_FLAG;
@@ -620,11 +636,14 @@
* _hash_lock without first calling dm_table_put, because dm_table_destroy
* waits for this dm_table_put and could be called under this lock.
*/
-static struct dm_table *dm_get_inactive_table(struct mapped_device *md)
+static struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx)
{
struct hash_cell *hc;
struct dm_table *table = NULL;
+ /* increment rcu count, we don't care about the table pointer */
+ dm_get_live_table(md, srcu_idx);
+
down_read(&_hash_lock);
hc = dm_get_mdptr(md);
if (!hc || hc->md != md) {
@@ -633,8 +652,6 @@
}
table = hc->new_map;
- if (table)
- dm_table_get(table);
out:
up_read(&_hash_lock);
@@ -643,10 +660,11 @@
}
static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
- struct dm_ioctl *param)
+ struct dm_ioctl *param,
+ int *srcu_idx)
{
return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
- dm_get_inactive_table(md) : dm_get_live_table(md);
+ dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx);
}
/*
@@ -657,6 +675,7 @@
{
struct gendisk *disk = dm_disk(md);
struct dm_table *table;
+ int srcu_idx;
param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
DM_ACTIVE_PRESENT_FLAG);
@@ -676,26 +695,27 @@
param->event_nr = dm_get_event_nr(md);
param->target_count = 0;
- table = dm_get_live_table(md);
+ table = dm_get_live_table(md, &srcu_idx);
if (table) {
if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
if (get_disk_ro(disk))
param->flags |= DM_READONLY_FLAG;
param->target_count = dm_table_get_num_targets(table);
}
- dm_table_put(table);
param->flags |= DM_ACTIVE_PRESENT_FLAG;
}
+ dm_put_live_table(md, srcu_idx);
if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
- table = dm_get_inactive_table(md);
+ int srcu_idx;
+ table = dm_get_inactive_table(md, &srcu_idx);
if (table) {
if (!(dm_table_get_mode(table) & FMODE_WRITE))
param->flags |= DM_READONLY_FLAG;
param->target_count = dm_table_get_num_targets(table);
- dm_table_put(table);
}
+ dm_put_live_table(md, srcu_idx);
}
}
@@ -796,6 +816,7 @@
struct hash_cell *hc;
struct mapped_device *md;
int r;
+ struct dm_table *t;
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
@@ -819,9 +840,14 @@
return r;
}
- __hash_remove(hc);
+ t = __hash_remove(hc);
up_write(&_hash_lock);
+ if (t) {
+ dm_sync_table(md);
+ dm_table_destroy(t);
+ }
+
if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr))
param->flags |= DM_UEVENT_GENERATED_FLAG;
@@ -986,6 +1012,7 @@
old_map = dm_swap_table(md, new_map);
if (IS_ERR(old_map)) {
+ dm_sync_table(md);
dm_table_destroy(new_map);
dm_put(md);
return PTR_ERR(old_map);
@@ -1003,6 +1030,10 @@
param->flags |= DM_UEVENT_GENERATED_FLAG;
}
+ /*
+ * Since dm_swap_table synchronizes RCU, nobody should be in
+ * read-side critical section already.
+ */
if (old_map)
dm_table_destroy(old_map);
@@ -1125,6 +1156,7 @@
int r = 0;
struct mapped_device *md;
struct dm_table *table;
+ int srcu_idx;
md = find_device(param);
if (!md)
@@ -1145,11 +1177,10 @@
*/
__dev_status(md, param);
- table = dm_get_live_or_inactive_table(md, param);
- if (table) {
+ table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
+ if (table)
retrieve_status(table, param, param_size);
- dm_table_put(table);
- }
+ dm_put_live_table(md, srcu_idx);
out:
dm_put(md);
@@ -1221,7 +1252,7 @@
{
int r;
struct hash_cell *hc;
- struct dm_table *t;
+ struct dm_table *t, *old_map = NULL;
struct mapped_device *md;
struct target_type *immutable_target_type;
@@ -1277,14 +1308,14 @@
hc = dm_get_mdptr(md);
if (!hc || hc->md != md) {
DMWARN("device has been removed from the dev hash table.");
- dm_table_destroy(t);
up_write(&_hash_lock);
+ dm_table_destroy(t);
r = -ENXIO;
goto out;
}
if (hc->new_map)
- dm_table_destroy(hc->new_map);
+ old_map = hc->new_map;
hc->new_map = t;
up_write(&_hash_lock);
@@ -1292,6 +1323,11 @@
__dev_status(md, param);
out:
+ if (old_map) {
+ dm_sync_table(md);
+ dm_table_destroy(old_map);
+ }
+
dm_put(md);
return r;
@@ -1301,6 +1337,7 @@
{
struct hash_cell *hc;
struct mapped_device *md;
+ struct dm_table *old_map = NULL;
down_write(&_hash_lock);
@@ -1312,7 +1349,7 @@
}
if (hc->new_map) {
- dm_table_destroy(hc->new_map);
+ old_map = hc->new_map;
hc->new_map = NULL;
}
@@ -1321,6 +1358,10 @@
__dev_status(hc->md, param);
md = hc->md;
up_write(&_hash_lock);
+ if (old_map) {
+ dm_sync_table(md);
+ dm_table_destroy(old_map);
+ }
dm_put(md);
return 0;
@@ -1370,6 +1411,7 @@
{
struct mapped_device *md;
struct dm_table *table;
+ int srcu_idx;
md = find_device(param);
if (!md)
@@ -1377,11 +1419,10 @@
__dev_status(md, param);
- table = dm_get_live_or_inactive_table(md, param);
- if (table) {
+ table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
+ if (table)
retrieve_deps(table, param, param_size);
- dm_table_put(table);
- }
+ dm_put_live_table(md, srcu_idx);
dm_put(md);
@@ -1396,6 +1437,7 @@
{
struct mapped_device *md;
struct dm_table *table;
+ int srcu_idx;
md = find_device(param);
if (!md)
@@ -1403,11 +1445,10 @@
__dev_status(md, param);
- table = dm_get_live_or_inactive_table(md, param);
- if (table) {
+ table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
+ if (table)
retrieve_status(table, param, param_size);
- dm_table_put(table);
- }
+ dm_put_live_table(md, srcu_idx);
dm_put(md);
@@ -1443,6 +1484,7 @@
struct dm_target_msg *tmsg = (void *) param + param->data_start;
size_t maxlen;
char *result = get_result_buffer(param, param_size, &maxlen);
+ int srcu_idx;
md = find_device(param);
if (!md)
@@ -1470,9 +1512,9 @@
if (r <= 1)
goto out_argv;
- table = dm_get_live_table(md);
+ table = dm_get_live_table(md, &srcu_idx);
if (!table)
- goto out_argv;
+ goto out_table;
if (dm_deleting_md(md)) {
r = -ENXIO;
@@ -1491,7 +1533,7 @@
}
out_table:
- dm_table_put(table);
+ dm_put_live_table(md, srcu_idx);
out_argv:
kfree(argv);
out:
@@ -1644,7 +1686,10 @@
}
if (!dmi) {
- dmi = __vmalloc(param_kernel->data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH, PAGE_KERNEL);
+ unsigned noio_flag;
+ noio_flag = memalloc_noio_save();
+ dmi = __vmalloc(param_kernel->data_size, GFP_NOIO | __GFP_REPEAT | __GFP_HIGH | __GFP_HIGHMEM, PAGE_KERNEL);
+ memalloc_noio_restore(noio_flag);
if (dmi)
*param_flags |= DM_PARAMS_VMALLOC;
}
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index bdf26f5..5adede1 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1561,7 +1561,6 @@
unsigned long flags;
int r;
-again:
bdev = NULL;
mode = 0;
r = 0;
@@ -1579,7 +1578,7 @@
}
if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
- r = -EAGAIN;
+ r = -ENOTCONN;
else if (!bdev)
r = -EIO;
@@ -1591,11 +1590,8 @@
if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
r = scsi_verify_blk_ioctl(NULL, cmd);
- if (r == -EAGAIN && !fatal_signal_pending(current)) {
+ if (r == -ENOTCONN && !fatal_signal_pending(current))
queue_work(kmultipathd, &m->process_queued_ios);
- msleep(10);
- goto again;
- }
return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
}
diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c
new file mode 100644
index 0000000..ff9ac4b
--- /dev/null
+++ b/drivers/md/dm-switch.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2010-2012 by Dell Inc. All rights reserved.
+ * Copyright (C) 2011-2013 Red Hat, Inc.
+ *
+ * This file is released under the GPL.
+ *
+ * dm-switch is a device-mapper target that maps IO to underlying block
+ * devices efficiently when there are a large number of fixed-sized
+ * address regions but there is no simple pattern to allow for a compact
+ * mapping representation such as dm-stripe.
+ */
+
+#include <linux/device-mapper.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+
+#define DM_MSG_PREFIX "switch"
+
+/*
+ * One region_table_slot_t holds <region_entries_per_slot> region table
+ * entries each of which is <region_table_entry_bits> in size.
+ */
+typedef unsigned long region_table_slot_t;
+
+/*
+ * A device with the offset to its start sector.
+ */
+struct switch_path {
+ struct dm_dev *dmdev;
+ sector_t start;
+};
+
+/*
+ * Context block for a dm switch device.
+ */
+struct switch_ctx {
+ struct dm_target *ti;
+
+ unsigned nr_paths; /* Number of paths in path_list. */
+
+ unsigned region_size; /* Region size in 512-byte sectors */
+ unsigned long nr_regions; /* Number of regions making up the device */
+ signed char region_size_bits; /* log2 of region_size or -1 */
+
+ unsigned char region_table_entry_bits; /* Number of bits in one region table entry */
+ unsigned char region_entries_per_slot; /* Number of entries in one region table slot */
+ signed char region_entries_per_slot_bits; /* log2 of region_entries_per_slot or -1 */
+
+ region_table_slot_t *region_table; /* Region table */
+
+ /*
+ * Array of dm devices to switch between.
+ */
+ struct switch_path path_list[0];
+};
+
+static struct switch_ctx *alloc_switch_ctx(struct dm_target *ti, unsigned nr_paths,
+ unsigned region_size)
+{
+ struct switch_ctx *sctx;
+
+ sctx = kzalloc(sizeof(struct switch_ctx) + nr_paths * sizeof(struct switch_path),
+ GFP_KERNEL);
+ if (!sctx)
+ return NULL;
+
+ sctx->ti = ti;
+ sctx->region_size = region_size;
+
+ ti->private = sctx;
+
+ return sctx;
+}
+
+static int alloc_region_table(struct dm_target *ti, unsigned nr_paths)
+{
+ struct switch_ctx *sctx = ti->private;
+ sector_t nr_regions = ti->len;
+ sector_t nr_slots;
+
+ if (!(sctx->region_size & (sctx->region_size - 1)))
+ sctx->region_size_bits = __ffs(sctx->region_size);
+ else
+ sctx->region_size_bits = -1;
+
+ sctx->region_table_entry_bits = 1;
+ while (sctx->region_table_entry_bits < sizeof(region_table_slot_t) * 8 &&
+ (region_table_slot_t)1 << sctx->region_table_entry_bits < nr_paths)
+ sctx->region_table_entry_bits++;
+
+ sctx->region_entries_per_slot = (sizeof(region_table_slot_t) * 8) / sctx->region_table_entry_bits;
+ if (!(sctx->region_entries_per_slot & (sctx->region_entries_per_slot - 1)))
+ sctx->region_entries_per_slot_bits = __ffs(sctx->region_entries_per_slot);
+ else
+ sctx->region_entries_per_slot_bits = -1;
+
+ if (sector_div(nr_regions, sctx->region_size))
+ nr_regions++;
+
+ sctx->nr_regions = nr_regions;
+ if (sctx->nr_regions != nr_regions || sctx->nr_regions >= ULONG_MAX) {
+ ti->error = "Region table too large";
+ return -EINVAL;
+ }
+
+ nr_slots = nr_regions;
+ if (sector_div(nr_slots, sctx->region_entries_per_slot))
+ nr_slots++;
+
+ if (nr_slots > ULONG_MAX / sizeof(region_table_slot_t)) {
+ ti->error = "Region table too large";
+ return -EINVAL;
+ }
+
+ sctx->region_table = vmalloc(nr_slots * sizeof(region_table_slot_t));
+ if (!sctx->region_table) {
+ ti->error = "Cannot allocate region table";
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void switch_get_position(struct switch_ctx *sctx, unsigned long region_nr,
+ unsigned long *region_index, unsigned *bit)
+{
+ if (sctx->region_entries_per_slot_bits >= 0) {
+ *region_index = region_nr >> sctx->region_entries_per_slot_bits;
+ *bit = region_nr & (sctx->region_entries_per_slot - 1);
+ } else {
+ *region_index = region_nr / sctx->region_entries_per_slot;
+ *bit = region_nr % sctx->region_entries_per_slot;
+ }
+
+ *bit *= sctx->region_table_entry_bits;
+}
+
+/*
+ * Find which path to use at given offset.
+ */
+static unsigned switch_get_path_nr(struct switch_ctx *sctx, sector_t offset)
+{
+ unsigned long region_index;
+ unsigned bit, path_nr;
+ sector_t p;
+
+ p = offset;
+ if (sctx->region_size_bits >= 0)
+ p >>= sctx->region_size_bits;
+ else
+ sector_div(p, sctx->region_size);
+
+ switch_get_position(sctx, p, ®ion_index, &bit);
+ path_nr = (ACCESS_ONCE(sctx->region_table[region_index]) >> bit) &
+ ((1 << sctx->region_table_entry_bits) - 1);
+
+ /* This can only happen if the processor uses non-atomic stores. */
+ if (unlikely(path_nr >= sctx->nr_paths))
+ path_nr = 0;
+
+ return path_nr;
+}
+
+static void switch_region_table_write(struct switch_ctx *sctx, unsigned long region_nr,
+ unsigned value)
+{
+ unsigned long region_index;
+ unsigned bit;
+ region_table_slot_t pte;
+
+ switch_get_position(sctx, region_nr, ®ion_index, &bit);
+
+ pte = sctx->region_table[region_index];
+ pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit);
+ pte |= (region_table_slot_t)value << bit;
+ sctx->region_table[region_index] = pte;
+}
+
+/*
+ * Fill the region table with an initial round robin pattern.
+ */
+static void initialise_region_table(struct switch_ctx *sctx)
+{
+ unsigned path_nr = 0;
+ unsigned long region_nr;
+
+ for (region_nr = 0; region_nr < sctx->nr_regions; region_nr++) {
+ switch_region_table_write(sctx, region_nr, path_nr);
+ if (++path_nr >= sctx->nr_paths)
+ path_nr = 0;
+ }
+}
+
+static int parse_path(struct dm_arg_set *as, struct dm_target *ti)
+{
+ struct switch_ctx *sctx = ti->private;
+ unsigned long long start;
+ int r;
+
+ r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
+ &sctx->path_list[sctx->nr_paths].dmdev);
+ if (r) {
+ ti->error = "Device lookup failed";
+ return r;
+ }
+
+ if (kstrtoull(dm_shift_arg(as), 10, &start) || start != (sector_t)start) {
+ ti->error = "Invalid device starting offset";
+ dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev);
+ return -EINVAL;
+ }
+
+ sctx->path_list[sctx->nr_paths].start = start;
+
+ sctx->nr_paths++;
+
+ return 0;
+}
+
+/*
+ * Destructor: Don't free the dm_target, just the ti->private data (if any).
+ */
+static void switch_dtr(struct dm_target *ti)
+{
+ struct switch_ctx *sctx = ti->private;
+
+ while (sctx->nr_paths--)
+ dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev);
+
+ vfree(sctx->region_table);
+ kfree(sctx);
+}
+
+/*
+ * Constructor arguments:
+ * <num_paths> <region_size> <num_optional_args> [<optional_args>...]
+ * [<dev_path> <offset>]+
+ *
+ * Optional args are to allow for future extension: currently this
+ * parameter must be 0.
+ */
+static int switch_ctr(struct dm_target *ti, unsigned argc, char **argv)
+{
+ static struct dm_arg _args[] = {
+ {1, (KMALLOC_MAX_SIZE - sizeof(struct switch_ctx)) / sizeof(struct switch_path), "Invalid number of paths"},
+ {1, UINT_MAX, "Invalid region size"},
+ {0, 0, "Invalid number of optional args"},
+ };
+
+ struct switch_ctx *sctx;
+ struct dm_arg_set as;
+ unsigned nr_paths, region_size, nr_optional_args;
+ int r;
+
+ as.argc = argc;
+ as.argv = argv;
+
+ r = dm_read_arg(_args, &as, &nr_paths, &ti->error);
+ if (r)
+ return -EINVAL;
+
+ r = dm_read_arg(_args + 1, &as, ®ion_size, &ti->error);
+ if (r)
+ return r;
+
+ r = dm_read_arg_group(_args + 2, &as, &nr_optional_args, &ti->error);
+ if (r)
+ return r;
+ /* parse optional arguments here, if we add any */
+
+ if (as.argc != nr_paths * 2) {
+ ti->error = "Incorrect number of path arguments";
+ return -EINVAL;
+ }
+
+ sctx = alloc_switch_ctx(ti, nr_paths, region_size);
+ if (!sctx) {
+ ti->error = "Cannot allocate redirection context";
+ return -ENOMEM;
+ }
+
+ r = dm_set_target_max_io_len(ti, region_size);
+ if (r)
+ goto error;
+
+ while (as.argc) {
+ r = parse_path(&as, ti);
+ if (r)
+ goto error;
+ }
+
+ r = alloc_region_table(ti, nr_paths);
+ if (r)
+ goto error;
+
+ initialise_region_table(sctx);
+
+ /* For UNMAP, sending the request down any path is sufficient */
+ ti->num_discard_bios = 1;
+
+ return 0;
+
+error:
+ switch_dtr(ti);
+
+ return r;
+}
+
+static int switch_map(struct dm_target *ti, struct bio *bio)
+{
+ struct switch_ctx *sctx = ti->private;
+ sector_t offset = dm_target_offset(ti, bio->bi_sector);
+ unsigned path_nr = switch_get_path_nr(sctx, offset);
+
+ bio->bi_bdev = sctx->path_list[path_nr].dmdev->bdev;
+ bio->bi_sector = sctx->path_list[path_nr].start + offset;
+
+ return DM_MAPIO_REMAPPED;
+}
+
+/*
+ * We need to parse hex numbers in the message as quickly as possible.
+ *
+ * This table-based hex parser improves performance.
+ * It improves a time to load 1000000 entries compared to the condition-based
+ * parser.
+ * table-based parser condition-based parser
+ * PA-RISC 0.29s 0.31s
+ * Opteron 0.0495s 0.0498s
+ */
+static const unsigned char hex_table[256] = {
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+};
+
+static __always_inline unsigned long parse_hex(const char **string)
+{
+ unsigned char d;
+ unsigned long r = 0;
+
+ while ((d = hex_table[(unsigned char)**string]) < 16) {
+ r = (r << 4) | d;
+ (*string)++;
+ }
+
+ return r;
+}
+
+static int process_set_region_mappings(struct switch_ctx *sctx,
+ unsigned argc, char **argv)
+{
+ unsigned i;
+ unsigned long region_index = 0;
+
+ for (i = 1; i < argc; i++) {
+ unsigned long path_nr;
+ const char *string = argv[i];
+
+ if (*string == ':')
+ region_index++;
+ else {
+ region_index = parse_hex(&string);
+ if (unlikely(*string != ':')) {
+ DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
+ return -EINVAL;
+ }
+ }
+
+ string++;
+ if (unlikely(!*string)) {
+ DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
+ return -EINVAL;
+ }
+
+ path_nr = parse_hex(&string);
+ if (unlikely(*string)) {
+ DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
+ return -EINVAL;
+ }
+ if (unlikely(region_index >= sctx->nr_regions)) {
+ DMWARN("invalid set_region_mappings region number: %lu >= %lu", region_index, sctx->nr_regions);
+ return -EINVAL;
+ }
+ if (unlikely(path_nr >= sctx->nr_paths)) {
+ DMWARN("invalid set_region_mappings device: %lu >= %u", path_nr, sctx->nr_paths);
+ return -EINVAL;
+ }
+
+ switch_region_table_write(sctx, region_index, path_nr);
+ }
+
+ return 0;
+}
+
+/*
+ * Messages are processed one-at-a-time.
+ *
+ * Only set_region_mappings is supported.
+ */
+static int switch_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+ static DEFINE_MUTEX(message_mutex);
+
+ struct switch_ctx *sctx = ti->private;
+ int r = -EINVAL;
+
+ mutex_lock(&message_mutex);
+
+ if (!strcasecmp(argv[0], "set_region_mappings"))
+ r = process_set_region_mappings(sctx, argc, argv);
+ else
+ DMWARN("Unrecognised message received.");
+
+ mutex_unlock(&message_mutex);
+
+ return r;
+}
+
+static void switch_status(struct dm_target *ti, status_type_t type,
+ unsigned status_flags, char *result, unsigned maxlen)
+{
+ struct switch_ctx *sctx = ti->private;
+ unsigned sz = 0;
+ int path_nr;
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ result[0] = '\0';
+ break;
+
+ case STATUSTYPE_TABLE:
+ DMEMIT("%u %u 0", sctx->nr_paths, sctx->region_size);
+ for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++)
+ DMEMIT(" %s %llu", sctx->path_list[path_nr].dmdev->name,
+ (unsigned long long)sctx->path_list[path_nr].start);
+ break;
+ }
+}
+
+/*
+ * Switch ioctl:
+ *
+ * Passthrough all ioctls to the path for sector 0
+ */
+static int switch_ioctl(struct dm_target *ti, unsigned cmd,
+ unsigned long arg)
+{
+ struct switch_ctx *sctx = ti->private;
+ struct block_device *bdev;
+ fmode_t mode;
+ unsigned path_nr;
+ int r = 0;
+
+ path_nr = switch_get_path_nr(sctx, 0);
+
+ bdev = sctx->path_list[path_nr].dmdev->bdev;
+ mode = sctx->path_list[path_nr].dmdev->mode;
+
+ /*
+ * Only pass ioctls through if the device sizes match exactly.
+ */
+ if (ti->len + sctx->path_list[path_nr].start != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
+ r = scsi_verify_blk_ioctl(NULL, cmd);
+
+ return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+}
+
+static int switch_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn, void *data)
+{
+ struct switch_ctx *sctx = ti->private;
+ int path_nr;
+ int r;
+
+ for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++) {
+ r = fn(ti, sctx->path_list[path_nr].dmdev,
+ sctx->path_list[path_nr].start, ti->len, data);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static struct target_type switch_target = {
+ .name = "switch",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = switch_ctr,
+ .dtr = switch_dtr,
+ .map = switch_map,
+ .message = switch_message,
+ .status = switch_status,
+ .ioctl = switch_ioctl,
+ .iterate_devices = switch_iterate_devices,
+};
+
+static int __init dm_switch_init(void)
+{
+ int r;
+
+ r = dm_register_target(&switch_target);
+ if (r < 0)
+ DMERR("dm_register_target() failed %d", r);
+
+ return r;
+}
+
+static void __exit dm_switch_exit(void)
+{
+ dm_unregister_target(&switch_target);
+}
+
+module_init(dm_switch_init);
+module_exit(dm_switch_exit);
+
+MODULE_DESCRIPTION(DM_NAME " dynamic path switching target");
+MODULE_AUTHOR("Kevin D. O'Kelley <Kevin_OKelley@dell.com>");
+MODULE_AUTHOR("Narendran Ganapathy <Narendran_Ganapathy@dell.com>");
+MODULE_AUTHOR("Jim Ramsay <Jim_Ramsay@dell.com>");
+MODULE_AUTHOR("Mikulas Patocka <mpatocka@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 1ff252a..f221812 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -26,22 +26,8 @@
#define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
-/*
- * The table has always exactly one reference from either mapped_device->map
- * or hash_cell->new_map. This reference is not counted in table->holders.
- * A pair of dm_create_table/dm_destroy_table functions is used for table
- * creation/destruction.
- *
- * Temporary references from the other code increase table->holders. A pair
- * of dm_table_get/dm_table_put functions is used to manipulate it.
- *
- * When the table is about to be destroyed, we wait for table->holders to
- * drop to zero.
- */
-
struct dm_table {
struct mapped_device *md;
- atomic_t holders;
unsigned type;
/* btree table */
@@ -208,7 +194,6 @@
INIT_LIST_HEAD(&t->devices);
INIT_LIST_HEAD(&t->target_callbacks);
- atomic_set(&t->holders, 0);
if (!num_targets)
num_targets = KEYS_PER_NODE;
@@ -246,10 +231,6 @@
if (!t)
return;
- while (atomic_read(&t->holders))
- msleep(1);
- smp_mb();
-
/* free the indexes */
if (t->depth >= 2)
vfree(t->index[t->depth - 2]);
@@ -274,22 +255,6 @@
kfree(t);
}
-void dm_table_get(struct dm_table *t)
-{
- atomic_inc(&t->holders);
-}
-EXPORT_SYMBOL(dm_table_get);
-
-void dm_table_put(struct dm_table *t)
-{
- if (!t)
- return;
-
- smp_mb__before_atomic_dec();
- atomic_dec(&t->holders);
-}
-EXPORT_SYMBOL(dm_table_put);
-
/*
* Checks to see if we need to extend highs or targets.
*/
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index b948fd8..4b7941d 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -451,7 +451,7 @@
goto no_prefetch_cluster;
if (unlikely(cluster & (cluster - 1)))
- cluster = 1 << (fls(cluster) - 1);
+ cluster = 1 << __fls(cluster);
hash_block_start &= ~(sector_t)(cluster - 1);
hash_block_end |= cluster - 1;
@@ -695,8 +695,8 @@
goto bad;
}
- if (sscanf(argv[0], "%d%c", &num, &dummy) != 1 ||
- num < 0 || num > 1) {
+ if (sscanf(argv[0], "%u%c", &num, &dummy) != 1 ||
+ num > 1) {
ti->error = "Invalid version";
r = -EINVAL;
goto bad;
@@ -723,7 +723,7 @@
r = -EINVAL;
goto bad;
}
- v->data_dev_block_bits = ffs(num) - 1;
+ v->data_dev_block_bits = __ffs(num);
if (sscanf(argv[4], "%u%c", &num, &dummy) != 1 ||
!num || (num & (num - 1)) ||
@@ -733,7 +733,7 @@
r = -EINVAL;
goto bad;
}
- v->hash_dev_block_bits = ffs(num) - 1;
+ v->hash_dev_block_bits = __ffs(num);
if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
(sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
@@ -812,7 +812,7 @@
}
v->hash_per_block_bits =
- fls((1 << v->hash_dev_block_bits) / v->digest_size) - 1;
+ __fls((1 << v->hash_dev_block_bits) / v->digest_size);
v->levels = 0;
if (v->data_blocks)
@@ -831,9 +831,8 @@
for (i = v->levels - 1; i >= 0; i--) {
sector_t s;
v->hash_level_block[i] = hash_position;
- s = verity_position_at_level(v, v->data_blocks, i);
- s = (s >> v->hash_per_block_bits) +
- !!(s & ((1 << v->hash_per_block_bits) - 1));
+ s = (v->data_blocks + ((sector_t)1 << ((i + 1) * v->hash_per_block_bits)) - 1)
+ >> ((i + 1) * v->hash_per_block_bits);
if (hash_position + s < hash_position) {
ti->error = "Hash device offset overflow";
r = -E2BIG;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index d5370a9..9e39d2b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -117,15 +117,29 @@
#define DMF_MERGE_IS_OPTIONAL 6
/*
+ * A dummy definition to make RCU happy.
+ * struct dm_table should never be dereferenced in this file.
+ */
+struct dm_table {
+ int undefined__;
+};
+
+/*
* Work processed by per-device workqueue.
*/
struct mapped_device {
- struct rw_semaphore io_lock;
+ struct srcu_struct io_barrier;
struct mutex suspend_lock;
- rwlock_t map_lock;
atomic_t holders;
atomic_t open_count;
+ /*
+ * The current mapping.
+ * Use dm_get_live_table{_fast} or take suspend_lock for
+ * dereference.
+ */
+ struct dm_table *map;
+
unsigned long flags;
struct request_queue *queue;
@@ -155,11 +169,6 @@
struct workqueue_struct *wq;
/*
- * The current mapping.
- */
- struct dm_table *map;
-
- /*
* io objects are allocated from here.
*/
mempool_t *io_pool;
@@ -386,10 +395,14 @@
unsigned int cmd, unsigned long arg)
{
struct mapped_device *md = bdev->bd_disk->private_data;
- struct dm_table *map = dm_get_live_table(md);
+ int srcu_idx;
+ struct dm_table *map;
struct dm_target *tgt;
int r = -ENOTTY;
+retry:
+ map = dm_get_live_table(md, &srcu_idx);
+
if (!map || !dm_table_get_size(map))
goto out;
@@ -408,7 +421,12 @@
r = tgt->type->ioctl(tgt, cmd, arg);
out:
- dm_table_put(map);
+ dm_put_live_table(md, srcu_idx);
+
+ if (r == -ENOTCONN) {
+ msleep(10);
+ goto retry;
+ }
return r;
}
@@ -502,20 +520,39 @@
/*
* Everyone (including functions in this file), should use this
* function to access the md->map field, and make sure they call
- * dm_table_put() when finished.
+ * dm_put_live_table() when finished.
*/
-struct dm_table *dm_get_live_table(struct mapped_device *md)
+struct dm_table *dm_get_live_table(struct mapped_device *md, int *srcu_idx) __acquires(md->io_barrier)
{
- struct dm_table *t;
- unsigned long flags;
+ *srcu_idx = srcu_read_lock(&md->io_barrier);
- read_lock_irqsave(&md->map_lock, flags);
- t = md->map;
- if (t)
- dm_table_get(t);
- read_unlock_irqrestore(&md->map_lock, flags);
+ return srcu_dereference(md->map, &md->io_barrier);
+}
- return t;
+void dm_put_live_table(struct mapped_device *md, int srcu_idx) __releases(md->io_barrier)
+{
+ srcu_read_unlock(&md->io_barrier, srcu_idx);
+}
+
+void dm_sync_table(struct mapped_device *md)
+{
+ synchronize_srcu(&md->io_barrier);
+ synchronize_rcu_expedited();
+}
+
+/*
+ * A fast alternative to dm_get_live_table/dm_put_live_table.
+ * The caller must not block between these two functions.
+ */
+static struct dm_table *dm_get_live_table_fast(struct mapped_device *md) __acquires(RCU)
+{
+ rcu_read_lock();
+ return rcu_dereference(md->map);
+}
+
+static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
+{
+ rcu_read_unlock();
}
/*
@@ -1349,17 +1386,18 @@
/*
* Entry point to split a bio into clones and submit them to the targets.
*/
-static void __split_and_process_bio(struct mapped_device *md, struct bio *bio)
+static void __split_and_process_bio(struct mapped_device *md,
+ struct dm_table *map, struct bio *bio)
{
struct clone_info ci;
int error = 0;
- ci.map = dm_get_live_table(md);
- if (unlikely(!ci.map)) {
+ if (unlikely(!map)) {
bio_io_error(bio);
return;
}
+ ci.map = map;
ci.md = md;
ci.io = alloc_io(md);
ci.io->error = 0;
@@ -1386,7 +1424,6 @@
/* drop the extra reference count */
dec_pending(ci.io, error);
- dm_table_put(ci.map);
}
/*-----------------------------------------------------------------
* CRUD END
@@ -1397,7 +1434,7 @@
struct bio_vec *biovec)
{
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_live_table(md);
+ struct dm_table *map = dm_get_live_table_fast(md);
struct dm_target *ti;
sector_t max_sectors;
int max_size = 0;
@@ -1407,7 +1444,7 @@
ti = dm_table_find_target(map, bvm->bi_sector);
if (!dm_target_is_valid(ti))
- goto out_table;
+ goto out;
/*
* Find maximum amount of I/O that won't need splitting
@@ -1436,10 +1473,8 @@
max_size = 0;
-out_table:
- dm_table_put(map);
-
out:
+ dm_put_live_table_fast(md);
/*
* Always allow an entire first page
*/
@@ -1458,8 +1493,10 @@
int rw = bio_data_dir(bio);
struct mapped_device *md = q->queuedata;
int cpu;
+ int srcu_idx;
+ struct dm_table *map;
- down_read(&md->io_lock);
+ map = dm_get_live_table(md, &srcu_idx);
cpu = part_stat_lock();
part_stat_inc(cpu, &dm_disk(md)->part0, ios[rw]);
@@ -1468,7 +1505,7 @@
/* if we're suspended, we have to queue this io for later */
if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags))) {
- up_read(&md->io_lock);
+ dm_put_live_table(md, srcu_idx);
if (bio_rw(bio) != READA)
queue_io(md, bio);
@@ -1477,8 +1514,8 @@
return;
}
- __split_and_process_bio(md, bio);
- up_read(&md->io_lock);
+ __split_and_process_bio(md, map, bio);
+ dm_put_live_table(md, srcu_idx);
return;
}
@@ -1664,7 +1701,8 @@
static void dm_request_fn(struct request_queue *q)
{
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_live_table(md);
+ int srcu_idx;
+ struct dm_table *map = dm_get_live_table(md, &srcu_idx);
struct dm_target *ti;
struct request *rq, *clone;
sector_t pos;
@@ -1719,7 +1757,7 @@
delay_and_out:
blk_delay_queue(q, HZ / 10);
out:
- dm_table_put(map);
+ dm_put_live_table(md, srcu_idx);
}
int dm_underlying_device_busy(struct request_queue *q)
@@ -1732,14 +1770,14 @@
{
int r;
struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_live_table(md);
+ struct dm_table *map = dm_get_live_table_fast(md);
if (!map || test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags))
r = 1;
else
r = dm_table_any_busy_target(map);
- dm_table_put(map);
+ dm_put_live_table_fast(md);
return r;
}
@@ -1751,7 +1789,7 @@
struct dm_table *map;
if (!test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) {
- map = dm_get_live_table(md);
+ map = dm_get_live_table_fast(md);
if (map) {
/*
* Request-based dm cares about only own queue for
@@ -1762,9 +1800,8 @@
bdi_bits;
else
r = dm_table_any_congested(map, bdi_bits);
-
- dm_table_put(map);
}
+ dm_put_live_table_fast(md);
}
return r;
@@ -1869,12 +1906,14 @@
if (r < 0)
goto bad_minor;
+ r = init_srcu_struct(&md->io_barrier);
+ if (r < 0)
+ goto bad_io_barrier;
+
md->type = DM_TYPE_NONE;
- init_rwsem(&md->io_lock);
mutex_init(&md->suspend_lock);
mutex_init(&md->type_lock);
spin_lock_init(&md->deferred_lock);
- rwlock_init(&md->map_lock);
atomic_set(&md->holders, 1);
atomic_set(&md->open_count, 0);
atomic_set(&md->event_nr, 0);
@@ -1937,6 +1976,8 @@
bad_disk:
blk_cleanup_queue(md->queue);
bad_queue:
+ cleanup_srcu_struct(&md->io_barrier);
+bad_io_barrier:
free_minor(minor);
bad_minor:
module_put(THIS_MODULE);
@@ -1960,6 +2001,7 @@
bioset_free(md->bs);
blk_integrity_unregister(md->disk);
del_gendisk(md->disk);
+ cleanup_srcu_struct(&md->io_barrier);
free_minor(minor);
spin_lock(&_minor_lock);
@@ -2102,7 +2144,6 @@
struct dm_table *old_map;
struct request_queue *q = md->queue;
sector_t size;
- unsigned long flags;
int merge_is_optional;
size = dm_table_get_size(t);
@@ -2131,9 +2172,8 @@
merge_is_optional = dm_table_merge_is_optional(t);
- write_lock_irqsave(&md->map_lock, flags);
old_map = md->map;
- md->map = t;
+ rcu_assign_pointer(md->map, t);
md->immutable_target_type = dm_table_get_immutable_target_type(t);
dm_table_set_restrictions(t, q, limits);
@@ -2141,7 +2181,7 @@
set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
else
clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
- write_unlock_irqrestore(&md->map_lock, flags);
+ dm_sync_table(md);
return old_map;
}
@@ -2152,15 +2192,13 @@
static struct dm_table *__unbind(struct mapped_device *md)
{
struct dm_table *map = md->map;
- unsigned long flags;
if (!map)
return NULL;
dm_table_event_callback(map, NULL, NULL);
- write_lock_irqsave(&md->map_lock, flags);
- md->map = NULL;
- write_unlock_irqrestore(&md->map_lock, flags);
+ rcu_assign_pointer(md->map, NULL);
+ dm_sync_table(md);
return map;
}
@@ -2312,11 +2350,12 @@
static void __dm_destroy(struct mapped_device *md, bool wait)
{
struct dm_table *map;
+ int srcu_idx;
might_sleep();
spin_lock(&_minor_lock);
- map = dm_get_live_table(md);
+ map = dm_get_live_table(md, &srcu_idx);
idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md))));
set_bit(DMF_FREEING, &md->flags);
spin_unlock(&_minor_lock);
@@ -2326,6 +2365,9 @@
dm_table_postsuspend_targets(map);
}
+ /* dm_put_live_table must be before msleep, otherwise deadlock is possible */
+ dm_put_live_table(md, srcu_idx);
+
/*
* Rare, but there may be I/O requests still going to complete,
* for example. Wait for all references to disappear.
@@ -2340,7 +2382,6 @@
dm_device_name(md), atomic_read(&md->holders));
dm_sysfs_exit(md);
- dm_table_put(map);
dm_table_destroy(__unbind(md));
free_dev(md);
}
@@ -2397,8 +2438,10 @@
struct mapped_device *md = container_of(work, struct mapped_device,
work);
struct bio *c;
+ int srcu_idx;
+ struct dm_table *map;
- down_read(&md->io_lock);
+ map = dm_get_live_table(md, &srcu_idx);
while (!test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) {
spin_lock_irq(&md->deferred_lock);
@@ -2408,17 +2451,13 @@
if (!c)
break;
- up_read(&md->io_lock);
-
if (dm_request_based(md))
generic_make_request(c);
else
- __split_and_process_bio(md, c);
-
- down_read(&md->io_lock);
+ __split_and_process_bio(md, map, c);
}
- up_read(&md->io_lock);
+ dm_put_live_table(md, srcu_idx);
}
static void dm_queue_flush(struct mapped_device *md)
@@ -2450,10 +2489,10 @@
* reappear.
*/
if (dm_table_has_no_data_devices(table)) {
- live_map = dm_get_live_table(md);
+ live_map = dm_get_live_table_fast(md);
if (live_map)
limits = md->queue->limits;
- dm_table_put(live_map);
+ dm_put_live_table_fast(md);
}
if (!live_map) {
@@ -2533,7 +2572,7 @@
goto out_unlock;
}
- map = dm_get_live_table(md);
+ map = md->map;
/*
* DMF_NOFLUSH_SUSPENDING must be set before presuspend.
@@ -2554,7 +2593,7 @@
if (!noflush && do_lockfs) {
r = lock_fs(md);
if (r)
- goto out;
+ goto out_unlock;
}
/*
@@ -2569,9 +2608,8 @@
* (dm_wq_work), we set BMF_BLOCK_IO_FOR_SUSPEND and call
* flush_workqueue(md->wq).
*/
- down_write(&md->io_lock);
set_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags);
- up_write(&md->io_lock);
+ synchronize_srcu(&md->io_barrier);
/*
* Stop md->queue before flushing md->wq in case request-based
@@ -2589,10 +2627,9 @@
*/
r = dm_wait_for_completion(md, TASK_INTERRUPTIBLE);
- down_write(&md->io_lock);
if (noflush)
clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags);
- up_write(&md->io_lock);
+ synchronize_srcu(&md->io_barrier);
/* were we interrupted ? */
if (r < 0) {
@@ -2602,7 +2639,7 @@
start_queue(md->queue);
unlock_fs(md);
- goto out; /* pushback list is already flushed, so skip flush */
+ goto out_unlock; /* pushback list is already flushed, so skip flush */
}
/*
@@ -2615,9 +2652,6 @@
dm_table_postsuspend_targets(map);
-out:
- dm_table_put(map);
-
out_unlock:
mutex_unlock(&md->suspend_lock);
return r;
@@ -2632,7 +2666,7 @@
if (!dm_suspended_md(md))
goto out;
- map = dm_get_live_table(md);
+ map = md->map;
if (!map || !dm_table_get_size(map))
goto out;
@@ -2656,7 +2690,6 @@
r = 0;
out:
- dm_table_put(map);
mutex_unlock(&md->suspend_lock);
return r;
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
index fe907f2..3077949 100644
--- a/drivers/media/common/saa7146/saa7146_video.c
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -1,7 +1,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <media/saa7146_vv.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
#include <linux/module.h>
@@ -988,26 +987,6 @@
return err;
}
-static int vidioc_g_chip_ident(struct file *file, void *__fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match))
- chip->ident = V4L2_IDENT_SAA7146;
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
- return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
- core, g_chip_ident, chip);
-}
-
const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
@@ -1018,7 +997,6 @@
.vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
.vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
.vidioc_overlay = vidioc_overlay,
.vidioc_g_fbuf = vidioc_g_fbuf,
@@ -1039,7 +1017,6 @@
const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
index 45ac9ee..a142f79 100644
--- a/drivers/media/common/siano/smscoreapi.c
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -1154,7 +1154,7 @@
char *fw_filename = smscore_get_fw_filename(coredev, mode);
if (!fw_filename) {
- sms_info("mode %d not supported on this device", mode);
+ sms_err("mode %d not supported on this device", mode);
return -ENOENT;
}
sms_debug("Firmware name: %s", fw_filename);
@@ -1165,23 +1165,24 @@
rc = request_firmware(&fw, fw_filename, coredev->device);
if (rc < 0) {
- sms_info("failed to open \"%s\"", fw_filename);
+ sms_err("failed to open firmware file \"%s\"", fw_filename);
return rc;
}
sms_info("read fw %s, buffer size=0x%zx", fw_filename, fw->size);
fw_buf = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),
GFP_KERNEL | GFP_DMA);
if (!fw_buf) {
- sms_info("failed to allocate firmware buffer");
- return -ENOMEM;
- }
- memcpy(fw_buf, fw->data, fw->size);
- fw_buf_size = fw->size;
+ sms_err("failed to allocate firmware buffer");
+ rc = -ENOMEM;
+ } else {
+ memcpy(fw_buf, fw->data, fw->size);
+ fw_buf_size = fw->size;
- rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
- smscore_load_firmware_family2(coredev, fw_buf, fw_buf_size)
- : loadfirmware_handler(coredev->context, fw_buf,
- fw_buf_size);
+ rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
+ smscore_load_firmware_family2(coredev, fw_buf, fw_buf_size)
+ : loadfirmware_handler(coredev->context, fw_buf,
+ fw_buf_size);
+ }
kfree(fw_buf);
release_firmware(fw);
diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
index 297f1b2..0862622 100644
--- a/drivers/media/common/siano/smsdvb-main.c
+++ b/drivers/media/common/siano/smsdvb-main.c
@@ -140,6 +140,7 @@
case DEVICE_MODE_ISDBT:
case DEVICE_MODE_ISDBT_BDA:
n_layers = 4;
+ break;
default:
n_layers = 1;
}
diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c
index cc1e172..c7dace6 100644
--- a/drivers/media/common/tveeprom.c
+++ b/drivers/media/common/tveeprom.c
@@ -40,7 +40,6 @@
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
MODULE_AUTHOR("John Klar");
@@ -67,13 +66,10 @@
* The Hauppauge eeprom uses an 8bit field to determine which
* tuner formats the tuner supports.
*/
-static struct HAUPPAUGE_TUNER_FMT
-{
+static const struct {
int id;
- char *name;
-}
-hauppauge_tuner_fmt[] =
-{
+ const char * const name;
+} hauppauge_tuner_fmt[] = {
{ V4L2_STD_UNKNOWN, " UNKNOWN" },
{ V4L2_STD_UNKNOWN, " FM" },
{ V4L2_STD_B|V4L2_STD_GH, " PAL(B/G)" },
@@ -88,13 +84,10 @@
supplying this information. Note that many tuners where only used for
testing and never made it to the outside world. So you will only see
a subset in actual produced cards. */
-static struct HAUPPAUGE_TUNER
-{
+static const struct {
int id;
- char *name;
-}
-hauppauge_tuner[] =
-{
+ const char * const name;
+} hauppauge_tuner[] = {
/* 0-9 */
{ TUNER_ABSENT, "None" },
{ TUNER_ABSENT, "External" },
@@ -298,69 +291,66 @@
{ TUNER_ABSENT, "NXP 18272S"},
};
-/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are
+/* Use TVEEPROM_AUDPROC_INTERNAL for those audio 'chips' that are
* internal to a video chip, i.e. not a separate audio chip. */
-static struct HAUPPAUGE_AUDIOIC
-{
+static const struct {
u32 id;
- char *name;
-}
-audioIC[] =
-{
+ const char * const name;
+} audio_ic[] = {
/* 0-4 */
- { V4L2_IDENT_NONE, "None" },
- { V4L2_IDENT_UNKNOWN, "TEA6300" },
- { V4L2_IDENT_UNKNOWN, "TEA6320" },
- { V4L2_IDENT_UNKNOWN, "TDA9850" },
- { V4L2_IDENT_MSPX4XX, "MSP3400C" },
+ { TVEEPROM_AUDPROC_NONE, "None" },
+ { TVEEPROM_AUDPROC_OTHER, "TEA6300" },
+ { TVEEPROM_AUDPROC_OTHER, "TEA6320" },
+ { TVEEPROM_AUDPROC_OTHER, "TDA9850" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3400C" },
/* 5-9 */
- { V4L2_IDENT_MSPX4XX, "MSP3410D" },
- { V4L2_IDENT_MSPX4XX, "MSP3415" },
- { V4L2_IDENT_MSPX4XX, "MSP3430" },
- { V4L2_IDENT_MSPX4XX, "MSP3438" },
- { V4L2_IDENT_UNKNOWN, "CS5331" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3410D" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3415" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3430" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3438" },
+ { TVEEPROM_AUDPROC_OTHER, "CS5331" },
/* 10-14 */
- { V4L2_IDENT_MSPX4XX, "MSP3435" },
- { V4L2_IDENT_MSPX4XX, "MSP3440" },
- { V4L2_IDENT_MSPX4XX, "MSP3445" },
- { V4L2_IDENT_MSPX4XX, "MSP3411" },
- { V4L2_IDENT_MSPX4XX, "MSP3416" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3435" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3440" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3445" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3411" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3416" },
/* 15-19 */
- { V4L2_IDENT_MSPX4XX, "MSP3425" },
- { V4L2_IDENT_MSPX4XX, "MSP3451" },
- { V4L2_IDENT_MSPX4XX, "MSP3418" },
- { V4L2_IDENT_UNKNOWN, "Type 0x12" },
- { V4L2_IDENT_UNKNOWN, "OKI7716" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3425" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3451" },
+ { TVEEPROM_AUDPROC_MSP, "MSP3418" },
+ { TVEEPROM_AUDPROC_OTHER, "Type 0x12" },
+ { TVEEPROM_AUDPROC_OTHER, "OKI7716" },
/* 20-24 */
- { V4L2_IDENT_MSPX4XX, "MSP4410" },
- { V4L2_IDENT_MSPX4XX, "MSP4420" },
- { V4L2_IDENT_MSPX4XX, "MSP4440" },
- { V4L2_IDENT_MSPX4XX, "MSP4450" },
- { V4L2_IDENT_MSPX4XX, "MSP4408" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4410" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4420" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4440" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4450" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4408" },
/* 25-29 */
- { V4L2_IDENT_MSPX4XX, "MSP4418" },
- { V4L2_IDENT_MSPX4XX, "MSP4428" },
- { V4L2_IDENT_MSPX4XX, "MSP4448" },
- { V4L2_IDENT_MSPX4XX, "MSP4458" },
- { V4L2_IDENT_MSPX4XX, "Type 0x1d" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4418" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4428" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4448" },
+ { TVEEPROM_AUDPROC_MSP, "MSP4458" },
+ { TVEEPROM_AUDPROC_MSP, "Type 0x1d" },
/* 30-34 */
- { V4L2_IDENT_AMBIGUOUS, "CX880" },
- { V4L2_IDENT_AMBIGUOUS, "CX881" },
- { V4L2_IDENT_AMBIGUOUS, "CX883" },
- { V4L2_IDENT_AMBIGUOUS, "CX882" },
- { V4L2_IDENT_AMBIGUOUS, "CX25840" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX880" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX881" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX883" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX882" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25840" },
/* 35-39 */
- { V4L2_IDENT_AMBIGUOUS, "CX25841" },
- { V4L2_IDENT_AMBIGUOUS, "CX25842" },
- { V4L2_IDENT_AMBIGUOUS, "CX25843" },
- { V4L2_IDENT_AMBIGUOUS, "CX23418" },
- { V4L2_IDENT_AMBIGUOUS, "CX23885" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25841" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25842" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX25843" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23418" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23885" },
/* 40-44 */
- { V4L2_IDENT_AMBIGUOUS, "CX23888" },
- { V4L2_IDENT_AMBIGUOUS, "SAA7131" },
- { V4L2_IDENT_AMBIGUOUS, "CX23887" },
- { V4L2_IDENT_AMBIGUOUS, "SAA7164" },
- { V4L2_IDENT_AMBIGUOUS, "AU8522" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23888" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7131" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23887" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7164" },
+ { TVEEPROM_AUDPROC_INTERNAL, "AU8522" },
};
/* This list is supplied by Hauppauge. Thanks! */
@@ -453,11 +443,11 @@
int i, j, len, done, beenhere, tag, start;
int tuner1 = 0, t_format1 = 0, audioic = -1;
- char *t_name1 = NULL;
+ const char *t_name1 = NULL;
const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" };
int tuner2 = 0, t_format2 = 0;
- char *t_name2 = NULL;
+ const char *t_name2 = NULL;
const char *t_fmt_name2[8] = { " none", "", "", "", "", "", "", "" };
memset(tvee, 0, sizeof(*tvee));
@@ -545,10 +535,10 @@
to indicate 4052 mux was removed in favor of using MSP
inputs directly. */
audioic = eeprom_data[i+2] & 0x7f;
- if (audioic < ARRAY_SIZE(audioIC))
- tvee->audio_processor = audioIC[audioic].id;
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tvee->audio_processor = audio_ic[audioic].id;
else
- tvee->audio_processor = V4L2_IDENT_UNKNOWN;
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
break;
/* case 0x03: tag 'EEInfo' */
@@ -578,10 +568,10 @@
to indicate 4052 mux was removed in favor of using MSP
inputs directly. */
audioic = eeprom_data[i+1] & 0x7f;
- if (audioic < ARRAY_SIZE(audioIC))
- tvee->audio_processor = audioIC[audioic].id;
+ if (audioic < ARRAY_SIZE(audio_ic))
+ tvee->audio_processor = audio_ic[audioic].id;
else
- tvee->audio_processor = V4L2_IDENT_UNKNOWN;
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
break;
@@ -726,11 +716,11 @@
t_fmt_name2[6], t_fmt_name2[7], t_format2);
if (audioic < 0) {
tveeprom_info("audio processor is unknown (no idx)\n");
- tvee->audio_processor = V4L2_IDENT_UNKNOWN;
+ tvee->audio_processor = TVEEPROM_AUDPROC_OTHER;
} else {
- if (audioic < ARRAY_SIZE(audioIC))
+ if (audioic < ARRAY_SIZE(audio_ic))
tveeprom_info("audio processor is %s (idx %d)\n",
- audioIC[audioic].name, audioic);
+ audio_ic[audioic].name, audioic);
else
tveeprom_info("audio processor is unknown (idx %d)\n",
audioic);
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index a1a3a51..0b4616b 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -377,10 +377,8 @@
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
buffer2_len);
}
- if (ret < 0) {
- dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ if (ret < 0)
dmxdevfilter->buffer.error = ret;
- }
if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
dmxdevfilter->state = DMXDEV_STATE_DONE;
spin_unlock(&dmxdevfilter->dev->lock);
@@ -416,10 +414,8 @@
ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
if (ret == buffer1_len)
ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
- if (ret < 0) {
- dvb_ringbuffer_flush(buffer);
+ if (ret < 0)
buffer->error = ret;
- }
spin_unlock(&dmxdevfilter->dev->lock);
wake_up(&buffer->queue);
return 0;
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index 335a8f4..886da16 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -367,4 +367,6 @@
#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002
#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004
#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500
+#define USB_PID_CPYTO_REDI_PC50A 0xa803
+#define USB_PID_CTVDIGDUAL_V2 0xe410
#endif
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
index 2099f21..23a0d05 100644
--- a/drivers/media/dvb-frontends/au8522_decoder.c
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -35,7 +35,6 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-device.h>
#include "au8522.h"
#include "au8522_priv.h"
@@ -524,13 +523,8 @@
static int au8522_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct au8522_state *state = to_state(sd);
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = au8522_readreg(state, reg->reg & 0xffff);
return 0;
}
@@ -538,13 +532,8 @@
static int au8522_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct au8522_state *state = to_state(sd);
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
au8522_writereg(state, reg->reg, reg->val & 0xff);
return 0;
}
@@ -636,20 +625,10 @@
return 0;
}
-static int au8522_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct au8522_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops au8522_core_ops = {
.log_status = v4l2_ctrl_subdev_log_status,
- .g_chip_ident = au8522_g_chip_ident,
.reset = au8522_reset,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = au8522_g_register,
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index a54182d..9053614 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -3406,7 +3406,7 @@
{
struct dib8000_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
- int l, i, active, time, ret, time_slave = FE_CALLBACK_TIME_NEVER;
+ int l, i, active, time, time_slave = FE_CALLBACK_TIME_NEVER;
u8 exit_condition, index_frontend;
u32 delay, callback_time;
@@ -3553,7 +3553,7 @@
}
}
- return ret;
+ return 0;
}
static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h
index e666718..f22eb9f 100644
--- a/drivers/media/dvb-frontends/drxk.h
+++ b/drivers/media/dvb-frontends/drxk.h
@@ -8,7 +8,7 @@
/**
* struct drxk_config - Configure the initial parameters for DRX-K
*
- * @adr: I2C Address of the DRX-K
+ * @adr: I2C address of the DRX-K
* @parallel_ts: True means that the device uses parallel TS,
* Serial otherwise.
* @dynamic_clk: True means that the clock will be dynamically
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index ec24d71..082014d 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -21,6 +21,8 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -34,35 +36,36 @@
#include "dvb_frontend.h"
#include "drxk.h"
#include "drxk_hard.h"
+#include "dvb_math.h"
-static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode);
-static int PowerDownQAM(struct drxk_state *state);
-static int SetDVBTStandard(struct drxk_state *state,
- enum OperationMode oMode);
-static int SetQAMStandard(struct drxk_state *state,
- enum OperationMode oMode);
-static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset);
-static int SetDVBTStandard(struct drxk_state *state,
- enum OperationMode oMode);
-static int DVBTStart(struct drxk_state *state);
-static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset);
-static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus);
-static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus);
-static int SwitchAntennaToQAM(struct drxk_state *state);
-static int SwitchAntennaToDVBT(struct drxk_state *state);
+static int power_down_dvbt(struct drxk_state *state, bool set_power_mode);
+static int power_down_qam(struct drxk_state *state);
+static int set_dvbt_standard(struct drxk_state *state,
+ enum operation_mode o_mode);
+static int set_qam_standard(struct drxk_state *state,
+ enum operation_mode o_mode);
+static int set_qam(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset);
+static int set_dvbt_standard(struct drxk_state *state,
+ enum operation_mode o_mode);
+static int dvbt_start(struct drxk_state *state);
+static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset);
+static int get_qam_lock_status(struct drxk_state *state, u32 *p_lock_status);
+static int get_dvbt_lock_status(struct drxk_state *state, u32 *p_lock_status);
+static int switch_antenna_to_qam(struct drxk_state *state);
+static int switch_antenna_to_dvbt(struct drxk_state *state);
-static bool IsDVBT(struct drxk_state *state)
+static bool is_dvbt(struct drxk_state *state)
{
- return state->m_OperationMode == OM_DVBT;
+ return state->m_operation_mode == OM_DVBT;
}
-static bool IsQAM(struct drxk_state *state)
+static bool is_qam(struct drxk_state *state)
{
- return state->m_OperationMode == OM_QAM_ITU_A ||
- state->m_OperationMode == OM_QAM_ITU_B ||
- state->m_OperationMode == OM_QAM_ITU_C;
+ return state->m_operation_mode == OM_QAM_ITU_A ||
+ state->m_operation_mode == OM_QAM_ITU_B ||
+ state->m_operation_mode == OM_QAM_ITU_C;
}
#define NOA1ROM 0
@@ -165,7 +168,7 @@
#define dprintk(level, fmt, arg...) do { \
if (debug >= level) \
- printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg); \
+ pr_debug(fmt, ##arg); \
} while (0)
@@ -186,8 +189,10 @@
u32 R0 = 0;
R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */
- Q1 = a / c; /* integer part, only the 4 least significant bits
- will be visible in the result */
+ Q1 = a / c; /*
+ * integer part, only the 4 least significant
+ * bits will be visible in the result
+ */
/* division using radix 16, 7 nibbles in the result */
for (i = 0; i < 7; i++) {
@@ -201,98 +206,9 @@
return Q1;
}
-static u32 Log10Times100(u32 x)
+static inline u32 log10times100(u32 value)
{
- static const u8 scale = 15;
- static const u8 indexWidth = 5;
- u8 i = 0;
- u32 y = 0;
- u32 d = 0;
- u32 k = 0;
- u32 r = 0;
- /*
- log2lut[n] = (1<<scale) * 200 * log2(1.0 + ((1.0/(1<<INDEXWIDTH)) * n))
- 0 <= n < ((1<<INDEXWIDTH)+1)
- */
-
- static const u32 log2lut[] = {
- 0, /* 0.000000 */
- 290941, /* 290941.300628 */
- 573196, /* 573196.476418 */
- 847269, /* 847269.179851 */
- 1113620, /* 1113620.489452 */
- 1372674, /* 1372673.576986 */
- 1624818, /* 1624817.752104 */
- 1870412, /* 1870411.981536 */
- 2109788, /* 2109787.962654 */
- 2343253, /* 2343252.817465 */
- 2571091, /* 2571091.461923 */
- 2793569, /* 2793568.696416 */
- 3010931, /* 3010931.055901 */
- 3223408, /* 3223408.452106 */
- 3431216, /* 3431215.635215 */
- 3634553, /* 3634553.498355 */
- 3833610, /* 3833610.244726 */
- 4028562, /* 4028562.434393 */
- 4219576, /* 4219575.925308 */
- 4406807, /* 4406806.721144 */
- 4590402, /* 4590401.736809 */
- 4770499, /* 4770499.491025 */
- 4947231, /* 4947230.734179 */
- 5120719, /* 5120719.018555 */
- 5291081, /* 5291081.217197 */
- 5458428, /* 5458427.996830 */
- 5622864, /* 5622864.249668 */
- 5784489, /* 5784489.488298 */
- 5943398, /* 5943398.207380 */
- 6099680, /* 6099680.215452 */
- 6253421, /* 6253420.939751 */
- 6404702, /* 6404701.706649 */
- 6553600, /* 6553600.000000 */
- };
-
-
- if (x == 0)
- return 0;
-
- /* Scale x (normalize) */
- /* computing y in log(x/y) = log(x) - log(y) */
- if ((x & ((0xffffffff) << (scale + 1))) == 0) {
- for (k = scale; k > 0; k--) {
- if (x & (((u32) 1) << scale))
- break;
- x <<= 1;
- }
- } else {
- for (k = scale; k < 31; k++) {
- if ((x & (((u32) (-1)) << (scale + 1))) == 0)
- break;
- x >>= 1;
- }
- }
- /*
- Now x has binary point between bit[scale] and bit[scale-1]
- and 1.0 <= x < 2.0 */
-
- /* correction for divison: log(x) = log(x/y)+log(y) */
- y = k * ((((u32) 1) << scale) * 200);
-
- /* remove integer part */
- x &= ((((u32) 1) << scale) - 1);
- /* get index */
- i = (u8) (x >> (scale - indexWidth));
- /* compute delta (x - a) */
- d = x & ((((u32) 1) << (scale - indexWidth)) - 1);
- /* compute log, multiplication (d* (..)) must be within range ! */
- y += log2lut[i] +
- ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth));
- /* Conver to log10() */
- y /= 108853; /* (log2(10) << scale) */
- r = (y >> 1);
- /* rounding */
- if (y & ((u32) 1))
- r++;
- return r;
+ return (100L * intlog10(value)) >> 24;
}
/****************************************************************************/
@@ -344,15 +260,15 @@
if (debug > 2) {
int i;
for (i = 0; i < len; i++)
- printk(KERN_CONT " %02x", data[i]);
- printk(KERN_CONT "\n");
+ pr_cont(" %02x", data[i]);
+ pr_cont("\n");
}
status = drxk_i2c_transfer(state, &msg, 1);
if (status >= 0 && status != 1)
status = -EIO;
if (status < 0)
- printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr);
+ pr_err("i2c write error at addr 0x%02x\n", adr);
return status;
}
@@ -371,22 +287,22 @@
status = drxk_i2c_transfer(state, msgs, 2);
if (status != 2) {
if (debug > 2)
- printk(KERN_CONT ": ERROR!\n");
+ pr_cont(": ERROR!\n");
if (status >= 0)
status = -EIO;
- printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr);
+ pr_err("i2c read error at addr 0x%02x\n", adr);
return status;
}
if (debug > 2) {
int i;
dprintk(2, ": read from");
for (i = 0; i < len; i++)
- printk(KERN_CONT " %02x", msg[i]);
- printk(KERN_CONT ", value = ");
+ pr_cont(" %02x", msg[i]);
+ pr_cont(", value = ");
for (i = 0; i < alen; i++)
- printk(KERN_CONT " %02x", answ[i]);
- printk(KERN_CONT "\n");
+ pr_cont(" %02x", answ[i]);
+ pr_cont("\n");
}
return 0;
}
@@ -520,55 +436,55 @@
return write32_flags(state, reg, data, 0);
}
-static int write_block(struct drxk_state *state, u32 Address,
- const int BlockSize, const u8 pBlock[])
+static int write_block(struct drxk_state *state, u32 address,
+ const int block_size, const u8 p_block[])
{
- int status = 0, BlkSize = BlockSize;
- u8 Flags = 0;
+ int status = 0, blk_size = block_size;
+ u8 flags = 0;
if (state->single_master)
- Flags |= 0xC0;
+ flags |= 0xC0;
- while (BlkSize > 0) {
- int Chunk = BlkSize > state->m_ChunkSize ?
- state->m_ChunkSize : BlkSize;
- u8 *AdrBuf = &state->Chunk[0];
- u32 AdrLength = 0;
+ while (blk_size > 0) {
+ int chunk = blk_size > state->m_chunk_size ?
+ state->m_chunk_size : blk_size;
+ u8 *adr_buf = &state->chunk[0];
+ u32 adr_length = 0;
- if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) {
- AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01);
- AdrBuf[1] = ((Address >> 16) & 0xFF);
- AdrBuf[2] = ((Address >> 24) & 0xFF);
- AdrBuf[3] = ((Address >> 7) & 0xFF);
- AdrBuf[2] |= Flags;
- AdrLength = 4;
- if (Chunk == state->m_ChunkSize)
- Chunk -= 2;
+ if (DRXDAP_FASI_LONG_FORMAT(address) || (flags != 0)) {
+ adr_buf[0] = (((address << 1) & 0xFF) | 0x01);
+ adr_buf[1] = ((address >> 16) & 0xFF);
+ adr_buf[2] = ((address >> 24) & 0xFF);
+ adr_buf[3] = ((address >> 7) & 0xFF);
+ adr_buf[2] |= flags;
+ adr_length = 4;
+ if (chunk == state->m_chunk_size)
+ chunk -= 2;
} else {
- AdrBuf[0] = ((Address << 1) & 0xFF);
- AdrBuf[1] = (((Address >> 16) & 0x0F) |
- ((Address >> 18) & 0xF0));
- AdrLength = 2;
+ adr_buf[0] = ((address << 1) & 0xFF);
+ adr_buf[1] = (((address >> 16) & 0x0F) |
+ ((address >> 18) & 0xF0));
+ adr_length = 2;
}
- memcpy(&state->Chunk[AdrLength], pBlock, Chunk);
- dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags);
+ memcpy(&state->chunk[adr_length], p_block, chunk);
+ dprintk(2, "(0x%08x, 0x%02x)\n", address, flags);
if (debug > 1) {
int i;
- if (pBlock)
- for (i = 0; i < Chunk; i++)
- printk(KERN_CONT " %02x", pBlock[i]);
- printk(KERN_CONT "\n");
+ if (p_block)
+ for (i = 0; i < chunk; i++)
+ pr_cont(" %02x", p_block[i]);
+ pr_cont("\n");
}
status = i2c_write(state, state->demod_address,
- &state->Chunk[0], Chunk + AdrLength);
+ &state->chunk[0], chunk + adr_length);
if (status < 0) {
- printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
- __func__, Address);
+ pr_err("%s: i2c write error at addr 0x%02x\n",
+ __func__, address);
break;
}
- pBlock += Chunk;
- Address += (Chunk >> 1);
- BlkSize -= Chunk;
+ p_block += chunk;
+ address += (chunk >> 1);
+ blk_size -= chunk;
}
return status;
}
@@ -577,11 +493,11 @@
#define DRXK_MAX_RETRIES_POWERUP 20
#endif
-static int PowerUpDevice(struct drxk_state *state)
+static int power_up_device(struct drxk_state *state)
{
int status;
u8 data = 0;
- u16 retryCount = 0;
+ u16 retry_count = 0;
dprintk(1, "\n");
@@ -591,15 +507,15 @@
data = 0;
status = i2c_write(state, state->demod_address,
&data, 1);
- msleep(10);
- retryCount++;
+ usleep_range(10000, 11000);
+ retry_count++;
if (status < 0)
continue;
status = i2c_read1(state, state->demod_address,
&data);
} while (status < 0 &&
- (retryCount < DRXK_MAX_RETRIES_POWERUP));
- if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP)
+ (retry_count < DRXK_MAX_RETRIES_POWERUP));
+ if (status < 0 && retry_count >= DRXK_MAX_RETRIES_POWERUP)
goto error;
}
@@ -615,11 +531,11 @@
if (status < 0)
goto error;
- state->m_currentPowerMode = DRX_POWER_UP;
+ state->m_current_power_mode = DRX_POWER_UP;
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -631,106 +547,106 @@
* FIXME: most (all?) of the values bellow should be moved into
* struct drxk_config, as they are probably board-specific
*/
- u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO;
- u32 ulVSBIfAgcOutputLevel = 0;
- u32 ulVSBIfAgcMinLevel = 0;
- u32 ulVSBIfAgcMaxLevel = 0x7FFF;
- u32 ulVSBIfAgcSpeed = 3;
+ u32 ul_vsb_if_agc_mode = DRXK_AGC_CTRL_AUTO;
+ u32 ul_vsb_if_agc_output_level = 0;
+ u32 ul_vsb_if_agc_min_level = 0;
+ u32 ul_vsb_if_agc_max_level = 0x7FFF;
+ u32 ul_vsb_if_agc_speed = 3;
- u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO;
- u32 ulVSBRfAgcOutputLevel = 0;
- u32 ulVSBRfAgcMinLevel = 0;
- u32 ulVSBRfAgcMaxLevel = 0x7FFF;
- u32 ulVSBRfAgcSpeed = 3;
- u32 ulVSBRfAgcTop = 9500;
- u32 ulVSBRfAgcCutOffCurrent = 4000;
+ u32 ul_vsb_rf_agc_mode = DRXK_AGC_CTRL_AUTO;
+ u32 ul_vsb_rf_agc_output_level = 0;
+ u32 ul_vsb_rf_agc_min_level = 0;
+ u32 ul_vsb_rf_agc_max_level = 0x7FFF;
+ u32 ul_vsb_rf_agc_speed = 3;
+ u32 ul_vsb_rf_agc_top = 9500;
+ u32 ul_vsb_rf_agc_cut_off_current = 4000;
- u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO;
- u32 ulATVIfAgcOutputLevel = 0;
- u32 ulATVIfAgcMinLevel = 0;
- u32 ulATVIfAgcMaxLevel = 0;
- u32 ulATVIfAgcSpeed = 3;
+ u32 ul_atv_if_agc_mode = DRXK_AGC_CTRL_AUTO;
+ u32 ul_atv_if_agc_output_level = 0;
+ u32 ul_atv_if_agc_min_level = 0;
+ u32 ul_atv_if_agc_max_level = 0;
+ u32 ul_atv_if_agc_speed = 3;
- u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF;
- u32 ulATVRfAgcOutputLevel = 0;
- u32 ulATVRfAgcMinLevel = 0;
- u32 ulATVRfAgcMaxLevel = 0;
- u32 ulATVRfAgcTop = 9500;
- u32 ulATVRfAgcCutOffCurrent = 4000;
- u32 ulATVRfAgcSpeed = 3;
+ u32 ul_atv_rf_agc_mode = DRXK_AGC_CTRL_OFF;
+ u32 ul_atv_rf_agc_output_level = 0;
+ u32 ul_atv_rf_agc_min_level = 0;
+ u32 ul_atv_rf_agc_max_level = 0;
+ u32 ul_atv_rf_agc_top = 9500;
+ u32 ul_atv_rf_agc_cut_off_current = 4000;
+ u32 ul_atv_rf_agc_speed = 3;
u32 ulQual83 = DEFAULT_MER_83;
u32 ulQual93 = DEFAULT_MER_93;
- u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
- u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+ u32 ul_mpeg_lock_time_out = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ u32 ul_demod_lock_time_out = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
/* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
/* io_pad_cfg_mode output mode is drive always */
/* io_pad_cfg_drive is set to power 2 (23 mA) */
- u32 ulGPIOCfg = 0x0113;
- u32 ulInvertTSClock = 0;
- u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
- u32 ulDVBTBitrate = 50000000;
- u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
+ u32 ul_gpio_cfg = 0x0113;
+ u32 ul_invert_ts_clock = 0;
+ u32 ul_ts_data_strength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
+ u32 ul_dvbt_bitrate = 50000000;
+ u32 ul_dvbc_bitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
- u32 ulInsertRSByte = 0;
+ u32 ul_insert_rs_byte = 0;
- u32 ulRfMirror = 1;
- u32 ulPowerDown = 0;
+ u32 ul_rf_mirror = 1;
+ u32 ul_power_down = 0;
dprintk(1, "\n");
- state->m_hasLNA = false;
- state->m_hasDVBT = false;
- state->m_hasDVBC = false;
- state->m_hasATV = false;
- state->m_hasOOB = false;
- state->m_hasAudio = false;
+ state->m_has_lna = false;
+ state->m_has_dvbt = false;
+ state->m_has_dvbc = false;
+ state->m_has_atv = false;
+ state->m_has_oob = false;
+ state->m_has_audio = false;
- if (!state->m_ChunkSize)
- state->m_ChunkSize = 124;
+ if (!state->m_chunk_size)
+ state->m_chunk_size = 124;
- state->m_oscClockFreq = 0;
- state->m_smartAntInverted = false;
- state->m_bPDownOpenBridge = false;
+ state->m_osc_clock_freq = 0;
+ state->m_smart_ant_inverted = false;
+ state->m_b_p_down_open_bridge = false;
/* real system clock frequency in kHz */
- state->m_sysClockFreq = 151875;
+ state->m_sys_clock_freq = 151875;
/* Timing div, 250ns/Psys */
/* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */
- state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) *
+ state->m_hi_cfg_timing_div = ((state->m_sys_clock_freq / 1000) *
HI_I2C_DELAY) / 1000;
/* Clipping */
- if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
- state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
- state->m_HICfgWakeUpKey = (state->demod_address << 1);
+ if (state->m_hi_cfg_timing_div > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+ state->m_hi_cfg_timing_div = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+ state->m_hi_cfg_wake_up_key = (state->demod_address << 1);
/* port/bridge/power down ctrl */
- state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+ state->m_hi_cfg_ctrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
- state->m_bPowerDown = (ulPowerDown != 0);
+ state->m_b_power_down = (ul_power_down != 0);
- state->m_DRXK_A3_PATCH_CODE = false;
+ state->m_drxk_a3_patch_code = false;
/* Init AGC and PGA parameters */
/* VSB IF */
- state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode);
- state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel);
- state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel);
- state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel);
- state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed);
- state->m_vsbPgaCfg = 140;
+ state->m_vsb_if_agc_cfg.ctrl_mode = ul_vsb_if_agc_mode;
+ state->m_vsb_if_agc_cfg.output_level = ul_vsb_if_agc_output_level;
+ state->m_vsb_if_agc_cfg.min_output_level = ul_vsb_if_agc_min_level;
+ state->m_vsb_if_agc_cfg.max_output_level = ul_vsb_if_agc_max_level;
+ state->m_vsb_if_agc_cfg.speed = ul_vsb_if_agc_speed;
+ state->m_vsb_pga_cfg = 140;
/* VSB RF */
- state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode);
- state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel);
- state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel);
- state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel);
- state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed);
- state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop);
- state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent);
- state->m_vsbPreSawCfg.reference = 0x07;
- state->m_vsbPreSawCfg.usePreSaw = true;
+ state->m_vsb_rf_agc_cfg.ctrl_mode = ul_vsb_rf_agc_mode;
+ state->m_vsb_rf_agc_cfg.output_level = ul_vsb_rf_agc_output_level;
+ state->m_vsb_rf_agc_cfg.min_output_level = ul_vsb_rf_agc_min_level;
+ state->m_vsb_rf_agc_cfg.max_output_level = ul_vsb_rf_agc_max_level;
+ state->m_vsb_rf_agc_cfg.speed = ul_vsb_rf_agc_speed;
+ state->m_vsb_rf_agc_cfg.top = ul_vsb_rf_agc_top;
+ state->m_vsb_rf_agc_cfg.cut_off_current = ul_vsb_rf_agc_cut_off_current;
+ state->m_vsb_pre_saw_cfg.reference = 0x07;
+ state->m_vsb_pre_saw_cfg.use_pre_saw = true;
state->m_Quality83percent = DEFAULT_MER_83;
state->m_Quality93percent = DEFAULT_MER_93;
@@ -740,127 +656,127 @@
}
/* ATV IF */
- state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode);
- state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel);
- state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel);
- state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel);
- state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed);
+ state->m_atv_if_agc_cfg.ctrl_mode = ul_atv_if_agc_mode;
+ state->m_atv_if_agc_cfg.output_level = ul_atv_if_agc_output_level;
+ state->m_atv_if_agc_cfg.min_output_level = ul_atv_if_agc_min_level;
+ state->m_atv_if_agc_cfg.max_output_level = ul_atv_if_agc_max_level;
+ state->m_atv_if_agc_cfg.speed = ul_atv_if_agc_speed;
/* ATV RF */
- state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode);
- state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel);
- state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel);
- state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel);
- state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed);
- state->m_atvRfAgcCfg.top = (ulATVRfAgcTop);
- state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent);
- state->m_atvPreSawCfg.reference = 0x04;
- state->m_atvPreSawCfg.usePreSaw = true;
+ state->m_atv_rf_agc_cfg.ctrl_mode = ul_atv_rf_agc_mode;
+ state->m_atv_rf_agc_cfg.output_level = ul_atv_rf_agc_output_level;
+ state->m_atv_rf_agc_cfg.min_output_level = ul_atv_rf_agc_min_level;
+ state->m_atv_rf_agc_cfg.max_output_level = ul_atv_rf_agc_max_level;
+ state->m_atv_rf_agc_cfg.speed = ul_atv_rf_agc_speed;
+ state->m_atv_rf_agc_cfg.top = ul_atv_rf_agc_top;
+ state->m_atv_rf_agc_cfg.cut_off_current = ul_atv_rf_agc_cut_off_current;
+ state->m_atv_pre_saw_cfg.reference = 0x04;
+ state->m_atv_pre_saw_cfg.use_pre_saw = true;
/* DVBT RF */
- state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
- state->m_dvbtRfAgcCfg.outputLevel = 0;
- state->m_dvbtRfAgcCfg.minOutputLevel = 0;
- state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF;
- state->m_dvbtRfAgcCfg.top = 0x2100;
- state->m_dvbtRfAgcCfg.cutOffCurrent = 4000;
- state->m_dvbtRfAgcCfg.speed = 1;
+ state->m_dvbt_rf_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_OFF;
+ state->m_dvbt_rf_agc_cfg.output_level = 0;
+ state->m_dvbt_rf_agc_cfg.min_output_level = 0;
+ state->m_dvbt_rf_agc_cfg.max_output_level = 0xFFFF;
+ state->m_dvbt_rf_agc_cfg.top = 0x2100;
+ state->m_dvbt_rf_agc_cfg.cut_off_current = 4000;
+ state->m_dvbt_rf_agc_cfg.speed = 1;
/* DVBT IF */
- state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
- state->m_dvbtIfAgcCfg.outputLevel = 0;
- state->m_dvbtIfAgcCfg.minOutputLevel = 0;
- state->m_dvbtIfAgcCfg.maxOutputLevel = 9000;
- state->m_dvbtIfAgcCfg.top = 13424;
- state->m_dvbtIfAgcCfg.cutOffCurrent = 0;
- state->m_dvbtIfAgcCfg.speed = 3;
- state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30;
- state->m_dvbtIfAgcCfg.IngainTgtMax = 30000;
+ state->m_dvbt_if_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_AUTO;
+ state->m_dvbt_if_agc_cfg.output_level = 0;
+ state->m_dvbt_if_agc_cfg.min_output_level = 0;
+ state->m_dvbt_if_agc_cfg.max_output_level = 9000;
+ state->m_dvbt_if_agc_cfg.top = 13424;
+ state->m_dvbt_if_agc_cfg.cut_off_current = 0;
+ state->m_dvbt_if_agc_cfg.speed = 3;
+ state->m_dvbt_if_agc_cfg.fast_clip_ctrl_delay = 30;
+ state->m_dvbt_if_agc_cfg.ingain_tgt_max = 30000;
/* state->m_dvbtPgaCfg = 140; */
- state->m_dvbtPreSawCfg.reference = 4;
- state->m_dvbtPreSawCfg.usePreSaw = false;
+ state->m_dvbt_pre_saw_cfg.reference = 4;
+ state->m_dvbt_pre_saw_cfg.use_pre_saw = false;
/* QAM RF */
- state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
- state->m_qamRfAgcCfg.outputLevel = 0;
- state->m_qamRfAgcCfg.minOutputLevel = 6023;
- state->m_qamRfAgcCfg.maxOutputLevel = 27000;
- state->m_qamRfAgcCfg.top = 0x2380;
- state->m_qamRfAgcCfg.cutOffCurrent = 4000;
- state->m_qamRfAgcCfg.speed = 3;
+ state->m_qam_rf_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_OFF;
+ state->m_qam_rf_agc_cfg.output_level = 0;
+ state->m_qam_rf_agc_cfg.min_output_level = 6023;
+ state->m_qam_rf_agc_cfg.max_output_level = 27000;
+ state->m_qam_rf_agc_cfg.top = 0x2380;
+ state->m_qam_rf_agc_cfg.cut_off_current = 4000;
+ state->m_qam_rf_agc_cfg.speed = 3;
/* QAM IF */
- state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
- state->m_qamIfAgcCfg.outputLevel = 0;
- state->m_qamIfAgcCfg.minOutputLevel = 0;
- state->m_qamIfAgcCfg.maxOutputLevel = 9000;
- state->m_qamIfAgcCfg.top = 0x0511;
- state->m_qamIfAgcCfg.cutOffCurrent = 0;
- state->m_qamIfAgcCfg.speed = 3;
- state->m_qamIfAgcCfg.IngainTgtMax = 5119;
- state->m_qamIfAgcCfg.FastClipCtrlDelay = 50;
+ state->m_qam_if_agc_cfg.ctrl_mode = DRXK_AGC_CTRL_AUTO;
+ state->m_qam_if_agc_cfg.output_level = 0;
+ state->m_qam_if_agc_cfg.min_output_level = 0;
+ state->m_qam_if_agc_cfg.max_output_level = 9000;
+ state->m_qam_if_agc_cfg.top = 0x0511;
+ state->m_qam_if_agc_cfg.cut_off_current = 0;
+ state->m_qam_if_agc_cfg.speed = 3;
+ state->m_qam_if_agc_cfg.ingain_tgt_max = 5119;
+ state->m_qam_if_agc_cfg.fast_clip_ctrl_delay = 50;
- state->m_qamPgaCfg = 140;
- state->m_qamPreSawCfg.reference = 4;
- state->m_qamPreSawCfg.usePreSaw = false;
+ state->m_qam_pga_cfg = 140;
+ state->m_qam_pre_saw_cfg.reference = 4;
+ state->m_qam_pre_saw_cfg.use_pre_saw = false;
- state->m_OperationMode = OM_NONE;
- state->m_DrxkState = DRXK_UNINITIALIZED;
+ state->m_operation_mode = OM_NONE;
+ state->m_drxk_state = DRXK_UNINITIALIZED;
/* MPEG output configuration */
- state->m_enableMPEGOutput = true; /* If TRUE; enable MPEG ouput */
- state->m_insertRSByte = false; /* If TRUE; insert RS byte */
- state->m_invertDATA = false; /* If TRUE; invert DATA signals */
- state->m_invertERR = false; /* If TRUE; invert ERR signal */
- state->m_invertSTR = false; /* If TRUE; invert STR signals */
- state->m_invertVAL = false; /* If TRUE; invert VAL signals */
- state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
+ state->m_enable_mpeg_output = true; /* If TRUE; enable MPEG ouput */
+ state->m_insert_rs_byte = false; /* If TRUE; insert RS byte */
+ state->m_invert_data = false; /* If TRUE; invert DATA signals */
+ state->m_invert_err = false; /* If TRUE; invert ERR signal */
+ state->m_invert_str = false; /* If TRUE; invert STR signals */
+ state->m_invert_val = false; /* If TRUE; invert VAL signals */
+ state->m_invert_clk = (ul_invert_ts_clock != 0); /* If TRUE; invert CLK signals */
/* If TRUE; static MPEG clockrate will be used;
otherwise clockrate will adapt to the bitrate of the TS */
- state->m_DVBTBitrate = ulDVBTBitrate;
- state->m_DVBCBitrate = ulDVBCBitrate;
+ state->m_dvbt_bitrate = ul_dvbt_bitrate;
+ state->m_dvbc_bitrate = ul_dvbc_bitrate;
- state->m_TSDataStrength = (ulTSDataStrength & 0x07);
+ state->m_ts_data_strength = (ul_ts_data_strength & 0x07);
/* Maximum bitrate in b/s in case static clockrate is selected */
- state->m_mpegTsStaticBitrate = 19392658;
- state->m_disableTEIhandling = false;
+ state->m_mpeg_ts_static_bitrate = 19392658;
+ state->m_disable_te_ihandling = false;
- if (ulInsertRSByte)
- state->m_insertRSByte = true;
+ if (ul_insert_rs_byte)
+ state->m_insert_rs_byte = true;
- state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
- if (ulMpegLockTimeOut < 10000)
- state->m_MpegLockTimeOut = ulMpegLockTimeOut;
- state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
- if (ulDemodLockTimeOut < 10000)
- state->m_DemodLockTimeOut = ulDemodLockTimeOut;
+ state->m_mpeg_lock_time_out = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ if (ul_mpeg_lock_time_out < 10000)
+ state->m_mpeg_lock_time_out = ul_mpeg_lock_time_out;
+ state->m_demod_lock_time_out = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+ if (ul_demod_lock_time_out < 10000)
+ state->m_demod_lock_time_out = ul_demod_lock_time_out;
/* QAM defaults */
- state->m_Constellation = DRX_CONSTELLATION_AUTO;
- state->m_qamInterleaveMode = DRXK_QAM_I12_J17;
- state->m_fecRsPlen = 204 * 8; /* fecRsPlen annex A */
- state->m_fecRsPrescale = 1;
+ state->m_constellation = DRX_CONSTELLATION_AUTO;
+ state->m_qam_interleave_mode = DRXK_QAM_I12_J17;
+ state->m_fec_rs_plen = 204 * 8; /* fecRsPlen annex A */
+ state->m_fec_rs_prescale = 1;
- state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM;
- state->m_agcFastClipCtrlDelay = 0;
+ state->m_sqi_speed = DRXK_DVBT_SQI_SPEED_MEDIUM;
+ state->m_agcfast_clip_ctrl_delay = 0;
- state->m_GPIOCfg = (ulGPIOCfg);
+ state->m_gpio_cfg = ul_gpio_cfg;
- state->m_bPowerDown = false;
- state->m_currentPowerMode = DRX_POWER_DOWN;
+ state->m_b_power_down = false;
+ state->m_current_power_mode = DRX_POWER_DOWN;
- state->m_rfmirror = (ulRfMirror == 0);
- state->m_IfAgcPol = false;
+ state->m_rfmirror = (ul_rf_mirror == 0);
+ state->m_if_agc_pol = false;
return 0;
}
-static int DRXX_Open(struct drxk_state *state)
+static int drxx_open(struct drxk_state *state)
{
int status = 0;
u32 jtag = 0;
@@ -869,7 +785,8 @@
dprintk(1, "\n");
/* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
/* Check device id */
@@ -888,14 +805,14 @@
status = write16(state, SIO_TOP_COMM_KEY__A, key);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int GetDeviceCapabilities(struct drxk_state *state)
+static int get_device_capabilities(struct drxk_state *state)
{
- u16 sioPdrOhwCfg = 0;
- u32 sioTopJtagidLo = 0;
+ u16 sio_pdr_ohw_cfg = 0;
+ u32 sio_top_jtagid_lo = 0;
int status;
const char *spin = "";
@@ -903,197 +820,196 @@
/* driver 0.9.0 */
/* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
if (status < 0)
goto error;
- status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg);
+ status = read16(state, SIO_PDR_OHW_CFG__A, &sio_pdr_ohw_cfg);
if (status < 0)
goto error;
status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
if (status < 0)
goto error;
- switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+ switch ((sio_pdr_ohw_cfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
case 0:
/* ignore (bypass ?) */
break;
case 1:
/* 27 MHz */
- state->m_oscClockFreq = 27000;
+ state->m_osc_clock_freq = 27000;
break;
case 2:
/* 20.25 MHz */
- state->m_oscClockFreq = 20250;
+ state->m_osc_clock_freq = 20250;
break;
case 3:
/* 4 MHz */
- state->m_oscClockFreq = 20250;
+ state->m_osc_clock_freq = 20250;
break;
default:
- printk(KERN_ERR "drxk: Clock Frequency is unknown\n");
+ pr_err("Clock Frequency is unknown\n");
return -EINVAL;
}
/*
Determine device capabilities
Based on pinning v14
*/
- status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo);
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &sio_top_jtagid_lo);
if (status < 0)
goto error;
- printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo);
+ pr_info("status = 0x%08x\n", sio_top_jtagid_lo);
/* driver 0.9.0 */
- switch ((sioTopJtagidLo >> 29) & 0xF) {
+ switch ((sio_top_jtagid_lo >> 29) & 0xF) {
case 0:
- state->m_deviceSpin = DRXK_SPIN_A1;
+ state->m_device_spin = DRXK_SPIN_A1;
spin = "A1";
break;
case 2:
- state->m_deviceSpin = DRXK_SPIN_A2;
+ state->m_device_spin = DRXK_SPIN_A2;
spin = "A2";
break;
case 3:
- state->m_deviceSpin = DRXK_SPIN_A3;
+ state->m_device_spin = DRXK_SPIN_A3;
spin = "A3";
break;
default:
- state->m_deviceSpin = DRXK_SPIN_UNKNOWN;
+ state->m_device_spin = DRXK_SPIN_UNKNOWN;
status = -EINVAL;
- printk(KERN_ERR "drxk: Spin %d unknown\n",
- (sioTopJtagidLo >> 29) & 0xF);
+ pr_err("Spin %d unknown\n", (sio_top_jtagid_lo >> 29) & 0xF);
goto error2;
}
- switch ((sioTopJtagidLo >> 12) & 0xFF) {
+ switch ((sio_top_jtagid_lo >> 12) & 0xFF) {
case 0x13:
/* typeId = DRX3913K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = false;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = false;
- state->m_hasGPIO1 = false;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = false;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = false;
+ state->m_has_gpio1 = false;
+ state->m_has_irqn = false;
break;
case 0x15:
/* typeId = DRX3915K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = false;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = false;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
case 0x16:
/* typeId = DRX3916K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = false;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = false;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
case 0x18:
/* typeId = DRX3918K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = false;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = false;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
case 0x21:
/* typeId = DRX3921K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
case 0x23:
/* typeId = DRX3923K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
case 0x25:
/* typeId = DRX3925K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = true;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
case 0x26:
/* typeId = DRX3926K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
+ state->m_has_lna = false;
+ state->m_has_oob = false;
+ state->m_has_atv = true;
+ state->m_has_audio = false;
+ state->m_has_dvbt = true;
+ state->m_has_dvbc = true;
+ state->m_has_sawsw = true;
+ state->m_has_gpio2 = true;
+ state->m_has_gpio1 = true;
+ state->m_has_irqn = false;
break;
default:
- printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n",
- ((sioTopJtagidLo >> 12) & 0xFF));
+ pr_err("DeviceID 0x%02x not supported\n",
+ ((sio_top_jtagid_lo >> 12) & 0xFF));
status = -EINVAL;
goto error2;
}
- printk(KERN_INFO
- "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
- ((sioTopJtagidLo >> 12) & 0xFF), spin,
- state->m_oscClockFreq / 1000,
- state->m_oscClockFreq % 1000);
+ pr_info("detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
+ ((sio_top_jtagid_lo >> 12) & 0xFF), spin,
+ state->m_osc_clock_freq / 1000,
+ state->m_osc_clock_freq % 1000);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
error2:
return status;
}
-static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult)
+static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result)
{
int status;
bool powerdown_cmd;
@@ -1105,37 +1021,37 @@
if (status < 0)
goto error;
if (cmd == SIO_HI_RA_RAM_CMD_RESET)
- msleep(1);
+ usleep_range(1000, 2000);
powerdown_cmd =
(bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
- ((state->m_HICfgCtrl) &
+ ((state->m_hi_cfg_ctrl) &
SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
if (powerdown_cmd == false) {
/* Wait until command rdy */
- u32 retryCount = 0;
- u16 waitCmd;
+ u32 retry_count = 0;
+ u16 wait_cmd;
do {
- msleep(1);
- retryCount += 1;
+ usleep_range(1000, 2000);
+ retry_count += 1;
status = read16(state, SIO_HI_RA_RAM_CMD__A,
- &waitCmd);
- } while ((status < 0) && (retryCount < DRXK_MAX_RETRIES)
- && (waitCmd != 0));
+ &wait_cmd);
+ } while ((status < 0) && (retry_count < DRXK_MAX_RETRIES)
+ && (wait_cmd != 0));
if (status < 0)
goto error;
- status = read16(state, SIO_HI_RA_RAM_RES__A, pResult);
+ status = read16(state, SIO_HI_RA_RAM_RES__A, p_result);
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int HI_CfgCommand(struct drxk_state *state)
+static int hi_cfg_command(struct drxk_state *state)
{
int status;
@@ -1143,61 +1059,68 @@
mutex_lock(&state->mutex);
- status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout);
+ status = write16(state, SIO_HI_RA_RAM_PAR_6__A,
+ state->m_hi_cfg_timeout);
if (status < 0)
goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl);
+ status = write16(state, SIO_HI_RA_RAM_PAR_5__A,
+ state->m_hi_cfg_ctrl);
if (status < 0)
goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey);
+ status = write16(state, SIO_HI_RA_RAM_PAR_4__A,
+ state->m_hi_cfg_wake_up_key);
if (status < 0)
goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay);
+ status = write16(state, SIO_HI_RA_RAM_PAR_3__A,
+ state->m_hi_cfg_bridge_delay);
if (status < 0)
goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv);
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A,
+ state->m_hi_cfg_timing_div);
if (status < 0)
goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A,
+ SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
if (status < 0)
goto error;
- status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
+ status = hi_command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
if (status < 0)
goto error;
- state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ state->m_hi_cfg_ctrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
error:
mutex_unlock(&state->mutex);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int InitHI(struct drxk_state *state)
+static int init_hi(struct drxk_state *state)
{
dprintk(1, "\n");
- state->m_HICfgWakeUpKey = (state->demod_address << 1);
- state->m_HICfgTimeout = 0x96FF;
+ state->m_hi_cfg_wake_up_key = (state->demod_address << 1);
+ state->m_hi_cfg_timeout = 0x96FF;
/* port/bridge/power down ctrl */
- state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+ state->m_hi_cfg_ctrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
- return HI_CfgCommand(state);
+ return hi_cfg_command(state);
}
-static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
+static int mpegts_configure_pins(struct drxk_state *state, bool mpeg_enable)
{
int status = -1;
- u16 sioPdrMclkCfg = 0;
- u16 sioPdrMdxCfg = 0;
+ u16 sio_pdr_mclk_cfg = 0;
+ u16 sio_pdr_mdx_cfg = 0;
u16 err_cfg = 0;
dprintk(1, ": mpeg %s, %s mode\n",
- mpegEnable ? "enable" : "disable",
- state->m_enableParallel ? "parallel" : "serial");
+ mpeg_enable ? "enable" : "disable",
+ state->m_enable_parallel ? "parallel" : "serial");
/* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
@@ -1206,7 +1129,7 @@
if (status < 0)
goto error;
- if (mpegEnable == false) {
+ if (mpeg_enable == false) {
/* Set MPEG TS pads to inputmode */
status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
if (status < 0)
@@ -1246,19 +1169,19 @@
goto error;
} else {
/* Enable MPEG output */
- sioPdrMdxCfg =
- ((state->m_TSDataStrength <<
+ sio_pdr_mdx_cfg =
+ ((state->m_ts_data_strength <<
SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003);
- sioPdrMclkCfg = ((state->m_TSClockkStrength <<
+ sio_pdr_mclk_cfg = ((state->m_ts_clockk_strength <<
SIO_PDR_MCLK_CFG_DRIVE__B) |
0x0003);
- status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, sio_pdr_mdx_cfg);
if (status < 0)
goto error;
if (state->enable_merr_cfg)
- err_cfg = sioPdrMdxCfg;
+ err_cfg = sio_pdr_mdx_cfg;
status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
if (status < 0)
@@ -1267,31 +1190,38 @@
if (status < 0)
goto error;
- if (state->m_enableParallel == true) {
+ if (state->m_enable_parallel == true) {
/* paralel -> enable MD1 to MD7 */
- status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD1_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD2_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD3_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD4_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD5_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD6_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD7_CFG__A,
+ sio_pdr_mdx_cfg);
if (status < 0)
goto error;
} else {
- sioPdrMdxCfg = ((state->m_TSDataStrength <<
+ sio_pdr_mdx_cfg = ((state->m_ts_data_strength <<
SIO_PDR_MD0_CFG_DRIVE__B)
| 0x0003);
/* serial -> disable MD1 to MD7 */
@@ -1317,10 +1247,10 @@
if (status < 0)
goto error;
}
- status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg);
+ status = write16(state, SIO_PDR_MCLK_CFG__A, sio_pdr_mclk_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg);
+ status = write16(state, SIO_PDR_MD0_CFG__A, sio_pdr_mdx_cfg);
if (status < 0)
goto error;
}
@@ -1332,21 +1262,21 @@
status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int MPEGTSDisable(struct drxk_state *state)
+static int mpegts_disable(struct drxk_state *state)
{
dprintk(1, "\n");
- return MPEGTSConfigurePins(state, false);
+ return mpegts_configure_pins(state, false);
}
-static int BLChainCmd(struct drxk_state *state,
- u16 romOffset, u16 nrOfElements, u32 timeOut)
+static int bl_chain_cmd(struct drxk_state *state,
+ u16 rom_offset, u16 nr_of_elements, u32 time_out)
{
- u16 blStatus = 0;
+ u16 bl_status = 0;
int status;
unsigned long end;
@@ -1355,46 +1285,46 @@
status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN);
if (status < 0)
goto error;
- status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset);
+ status = write16(state, SIO_BL_CHAIN_ADDR__A, rom_offset);
if (status < 0)
goto error;
- status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements);
+ status = write16(state, SIO_BL_CHAIN_LEN__A, nr_of_elements);
if (status < 0)
goto error;
status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
if (status < 0)
goto error;
- end = jiffies + msecs_to_jiffies(timeOut);
+ end = jiffies + msecs_to_jiffies(time_out);
do {
- msleep(1);
- status = read16(state, SIO_BL_STATUS__A, &blStatus);
+ usleep_range(1000, 2000);
+ status = read16(state, SIO_BL_STATUS__A, &bl_status);
if (status < 0)
goto error;
- } while ((blStatus == 0x1) &&
+ } while ((bl_status == 0x1) &&
((time_is_after_jiffies(end))));
- if (blStatus == 0x1) {
- printk(KERN_ERR "drxk: SIO not ready\n");
+ if (bl_status == 0x1) {
+ pr_err("SIO not ready\n");
status = -EINVAL;
goto error2;
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
error2:
mutex_unlock(&state->mutex);
return status;
}
-static int DownloadMicrocode(struct drxk_state *state,
- const u8 pMCImage[], u32 Length)
+static int download_microcode(struct drxk_state *state,
+ const u8 p_mc_image[], u32 length)
{
- const u8 *pSrc = pMCImage;
- u32 Address;
- u16 nBlocks;
- u16 BlockSize;
+ const u8 *p_src = p_mc_image;
+ u32 address;
+ u16 n_blocks;
+ u16 block_size;
u32 offset = 0;
u32 i;
int status = 0;
@@ -1404,130 +1334,131 @@
/* down the drain (we don't care about MAGIC_WORD) */
#if 0
/* For future reference */
- Drain = (pSrc[0] << 8) | pSrc[1];
+ drain = (p_src[0] << 8) | p_src[1];
#endif
- pSrc += sizeof(u16);
+ p_src += sizeof(u16);
offset += sizeof(u16);
- nBlocks = (pSrc[0] << 8) | pSrc[1];
- pSrc += sizeof(u16);
+ n_blocks = (p_src[0] << 8) | p_src[1];
+ p_src += sizeof(u16);
offset += sizeof(u16);
- for (i = 0; i < nBlocks; i += 1) {
- Address = (pSrc[0] << 24) | (pSrc[1] << 16) |
- (pSrc[2] << 8) | pSrc[3];
- pSrc += sizeof(u32);
+ for (i = 0; i < n_blocks; i += 1) {
+ address = (p_src[0] << 24) | (p_src[1] << 16) |
+ (p_src[2] << 8) | p_src[3];
+ p_src += sizeof(u32);
offset += sizeof(u32);
- BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16);
- pSrc += sizeof(u16);
+ block_size = ((p_src[0] << 8) | p_src[1]) * sizeof(u16);
+ p_src += sizeof(u16);
offset += sizeof(u16);
#if 0
/* For future reference */
- Flags = (pSrc[0] << 8) | pSrc[1];
+ flags = (p_src[0] << 8) | p_src[1];
#endif
- pSrc += sizeof(u16);
+ p_src += sizeof(u16);
offset += sizeof(u16);
#if 0
/* For future reference */
- BlockCRC = (pSrc[0] << 8) | pSrc[1];
+ block_crc = (p_src[0] << 8) | p_src[1];
#endif
- pSrc += sizeof(u16);
+ p_src += sizeof(u16);
offset += sizeof(u16);
- if (offset + BlockSize > Length) {
- printk(KERN_ERR "drxk: Firmware is corrupted.\n");
+ if (offset + block_size > length) {
+ pr_err("Firmware is corrupted.\n");
return -EINVAL;
}
- status = write_block(state, Address, BlockSize, pSrc);
+ status = write_block(state, address, block_size, p_src);
if (status < 0) {
- printk(KERN_ERR "drxk: Error %d while loading firmware\n", status);
+ pr_err("Error %d while loading firmware\n", status);
break;
}
- pSrc += BlockSize;
- offset += BlockSize;
+ p_src += block_size;
+ offset += block_size;
}
return status;
}
-static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable)
+static int dvbt_enable_ofdm_token_ring(struct drxk_state *state, bool enable)
{
int status;
u16 data = 0;
- u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
- u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
+ u16 desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
+ u16 desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
unsigned long end;
dprintk(1, "\n");
if (enable == false) {
- desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
- desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
+ desired_ctrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
+ desired_status = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
}
status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
- if (status >= 0 && data == desiredStatus) {
+ if (status >= 0 && data == desired_status) {
/* tokenring already has correct status */
return status;
}
/* Disable/enable dvbt tokenring bridge */
- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl);
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desired_ctrl);
end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT);
do {
status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
- if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end))
+ if ((status >= 0 && data == desired_status)
+ || time_is_after_jiffies(end))
break;
- msleep(1);
+ usleep_range(1000, 2000);
} while (1);
- if (data != desiredStatus) {
- printk(KERN_ERR "drxk: SIO not ready\n");
+ if (data != desired_status) {
+ pr_err("SIO not ready\n");
return -EINVAL;
}
return status;
}
-static int MPEGTSStop(struct drxk_state *state)
+static int mpegts_stop(struct drxk_state *state)
{
int status = 0;
- u16 fecOcSncMode = 0;
- u16 fecOcIprMode = 0;
+ u16 fec_oc_snc_mode = 0;
+ u16 fec_oc_ipr_mode = 0;
dprintk(1, "\n");
/* Gracefull shutdown (byte boundaries) */
- status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+ status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode);
if (status < 0)
goto error;
- fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
- status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+ fec_oc_snc_mode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fec_oc_snc_mode);
if (status < 0)
goto error;
/* Suppress MCLK during absence of data */
- status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode);
+ status = read16(state, FEC_OC_IPR_MODE__A, &fec_oc_ipr_mode);
if (status < 0)
goto error;
- fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
- status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode);
+ fec_oc_ipr_mode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
+ status = write16(state, FEC_OC_IPR_MODE__A, fec_oc_ipr_mode);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
static int scu_command(struct drxk_state *state,
- u16 cmd, u8 parameterLen,
- u16 *parameter, u8 resultLen, u16 *result)
+ u16 cmd, u8 parameter_len,
+ u16 *parameter, u8 result_len, u16 *result)
{
#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15
#error DRXK register mapping no longer compatible with this routine!
#endif
- u16 curCmd = 0;
+ u16 cur_cmd = 0;
int status = -EINVAL;
unsigned long end;
u8 buffer[34];
@@ -1537,9 +1468,9 @@
dprintk(1, "\n");
- if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) ||
- ((resultLen > 0) && (result == NULL))) {
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ if ((cmd == 0) || ((parameter_len > 0) && (parameter == NULL)) ||
+ ((result_len > 0) && (result == NULL))) {
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -1547,7 +1478,7 @@
/* assume that the command register is ready
since it is checked afterwards */
- for (ii = parameterLen - 1; ii >= 0; ii -= 1) {
+ for (ii = parameter_len - 1; ii >= 0; ii -= 1) {
buffer[cnt++] = (parameter[ii] & 0xFF);
buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF);
}
@@ -1555,27 +1486,28 @@
buffer[cnt++] = ((cmd >> 8) & 0xFF);
write_block(state, SCU_RAM_PARAM_0__A -
- (parameterLen - 1), cnt, buffer);
+ (parameter_len - 1), cnt, buffer);
/* Wait until SCU has processed command */
end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME);
do {
- msleep(1);
- status = read16(state, SCU_RAM_COMMAND__A, &curCmd);
+ usleep_range(1000, 2000);
+ status = read16(state, SCU_RAM_COMMAND__A, &cur_cmd);
if (status < 0)
goto error;
- } while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
- if (curCmd != DRX_SCU_READY) {
- printk(KERN_ERR "drxk: SCU not ready\n");
+ } while (!(cur_cmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
+ if (cur_cmd != DRX_SCU_READY) {
+ pr_err("SCU not ready\n");
status = -EIO;
goto error2;
}
/* read results */
- if ((resultLen > 0) && (result != NULL)) {
+ if ((result_len > 0) && (result != NULL)) {
s16 err;
int ii;
- for (ii = resultLen - 1; ii >= 0; ii -= 1) {
- status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]);
+ for (ii = result_len - 1; ii >= 0; ii -= 1) {
+ status = read16(state, SCU_RAM_PARAM_0__A - ii,
+ &result[ii]);
if (status < 0)
goto error;
}
@@ -1603,7 +1535,7 @@
sprintf(errname, "ERROR: %d\n", err);
p = errname;
}
- printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd);
+ pr_err("%s while sending cmd 0x%04x with params:", p, cmd);
print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt);
status = -EINVAL;
goto error2;
@@ -1611,13 +1543,13 @@
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
error2:
mutex_unlock(&state->mutex);
return status;
}
-static int SetIqmAf(struct drxk_state *state, bool active)
+static int set_iqm_af(struct drxk_state *state, bool active)
{
u16 data = 0;
int status;
@@ -1647,14 +1579,14 @@
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode)
+static int ctrl_power_mode(struct drxk_state *state, enum drx_power_mode *mode)
{
int status = 0;
- u16 sioCcPwdMode = 0;
+ u16 sio_cc_pwd_mode = 0;
dprintk(1, "\n");
@@ -1664,19 +1596,19 @@
switch (*mode) {
case DRX_POWER_UP:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE;
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_NONE;
break;
case DRXK_POWER_DOWN_OFDM:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM;
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OFDM;
break;
case DRXK_POWER_DOWN_CORE:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
break;
case DRXK_POWER_DOWN_PLL:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL;
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_PLL;
break;
case DRX_POWER_DOWN:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC;
+ sio_cc_pwd_mode = SIO_CC_PWD_MODE_LEVEL_OSC;
break;
default:
/* Unknow sleep mode */
@@ -1684,15 +1616,15 @@
}
/* If already in requested power mode, do nothing */
- if (state->m_currentPowerMode == *mode)
+ if (state->m_current_power_mode == *mode)
return 0;
/* For next steps make sure to start from DRX_POWER_UP mode */
- if (state->m_currentPowerMode != DRX_POWER_UP) {
- status = PowerUpDevice(state);
+ if (state->m_current_power_mode != DRX_POWER_UP) {
+ status = power_up_device(state);
if (status < 0)
goto error;
- status = DVBTEnableOFDMTokenRing(state, true);
+ status = dvbt_enable_ofdm_token_ring(state, true);
if (status < 0)
goto error;
}
@@ -1709,31 +1641,31 @@
/* Power down device */
/* stop all comm_exec */
/* Stop and power down previous standard */
- switch (state->m_OperationMode) {
+ switch (state->m_operation_mode) {
case OM_DVBT:
- status = MPEGTSStop(state);
+ status = mpegts_stop(state);
if (status < 0)
goto error;
- status = PowerDownDVBT(state, false);
+ status = power_down_dvbt(state, false);
if (status < 0)
goto error;
break;
case OM_QAM_ITU_A:
case OM_QAM_ITU_C:
- status = MPEGTSStop(state);
+ status = mpegts_stop(state);
if (status < 0)
goto error;
- status = PowerDownQAM(state);
+ status = power_down_qam(state);
if (status < 0)
goto error;
break;
default:
break;
}
- status = DVBTEnableOFDMTokenRing(state, false);
+ status = dvbt_enable_ofdm_token_ring(state, false);
if (status < 0)
goto error;
- status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode);
+ status = write16(state, SIO_CC_PWD_MODE__A, sio_cc_pwd_mode);
if (status < 0)
goto error;
status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
@@ -1741,26 +1673,26 @@
goto error;
if (*mode != DRXK_POWER_DOWN_OFDM) {
- state->m_HICfgCtrl |=
+ state->m_hi_cfg_ctrl |=
SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
- status = HI_CfgCommand(state);
+ status = hi_cfg_command(state);
if (status < 0)
goto error;
}
}
- state->m_currentPowerMode = *mode;
+ state->m_current_power_mode = *mode;
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode)
+static int power_down_dvbt(struct drxk_state *state, bool set_power_mode)
{
- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
- u16 cmdResult = 0;
+ enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM;
+ u16 cmd_result = 0;
u16 data = 0;
int status;
@@ -1771,11 +1703,17 @@
goto error;
if (data == SCU_COMM_EXEC_ACTIVE) {
/* Send OFDM stop command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
/* Send OFDM reset command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
}
@@ -1792,24 +1730,24 @@
goto error;
/* powerdown AFE */
- status = SetIqmAf(state, false);
+ status = set_iqm_af(state, false);
if (status < 0)
goto error;
/* powerdown to OFDM mode */
- if (setPowerMode) {
- status = CtrlPowerMode(state, &powerMode);
+ if (set_power_mode) {
+ status = ctrl_power_mode(state, &power_mode);
if (status < 0)
goto error;
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SetOperationMode(struct drxk_state *state,
- enum OperationMode oMode)
+static int setoperation_mode(struct drxk_state *state,
+ enum operation_mode o_mode)
{
int status = 0;
@@ -1821,36 +1759,37 @@
*/
/* disable HW lock indicator */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
/* Device is already at the required mode */
- if (state->m_OperationMode == oMode)
+ if (state->m_operation_mode == o_mode)
return 0;
- switch (state->m_OperationMode) {
+ switch (state->m_operation_mode) {
/* OM_NONE was added for start up */
case OM_NONE:
break;
case OM_DVBT:
- status = MPEGTSStop(state);
+ status = mpegts_stop(state);
if (status < 0)
goto error;
- status = PowerDownDVBT(state, true);
+ status = power_down_dvbt(state, true);
if (status < 0)
goto error;
- state->m_OperationMode = OM_NONE;
+ state->m_operation_mode = OM_NONE;
break;
case OM_QAM_ITU_A: /* fallthrough */
case OM_QAM_ITU_C:
- status = MPEGTSStop(state);
+ status = mpegts_stop(state);
if (status < 0)
goto error;
- status = PowerDownQAM(state);
+ status = power_down_qam(state);
if (status < 0)
goto error;
- state->m_OperationMode = OM_NONE;
+ state->m_operation_mode = OM_NONE;
break;
case OM_QAM_ITU_B:
default:
@@ -1861,20 +1800,20 @@
/*
Power up new standard
*/
- switch (oMode) {
+ switch (o_mode) {
case OM_DVBT:
dprintk(1, ": DVB-T\n");
- state->m_OperationMode = oMode;
- status = SetDVBTStandard(state, oMode);
+ state->m_operation_mode = o_mode;
+ status = set_dvbt_standard(state, o_mode);
if (status < 0)
goto error;
break;
case OM_QAM_ITU_A: /* fallthrough */
case OM_QAM_ITU_C:
dprintk(1, ": DVB-C Annex %c\n",
- (state->m_OperationMode == OM_QAM_ITU_A) ? 'A' : 'C');
- state->m_OperationMode = oMode;
- status = SetQAMStandard(state, oMode);
+ (state->m_operation_mode == OM_QAM_ITU_A) ? 'A' : 'C');
+ state->m_operation_mode = o_mode;
+ status = set_qam_standard(state, o_mode);
if (status < 0)
goto error;
break;
@@ -1884,121 +1823,121 @@
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int Start(struct drxk_state *state, s32 offsetFreq,
- s32 IntermediateFrequency)
+static int start(struct drxk_state *state, s32 offset_freq,
+ s32 intermediate_frequency)
{
int status = -EINVAL;
- u16 IFreqkHz;
- s32 OffsetkHz = offsetFreq / 1000;
+ u16 i_freqk_hz;
+ s32 offsetk_hz = offset_freq / 1000;
dprintk(1, "\n");
- if (state->m_DrxkState != DRXK_STOPPED &&
- state->m_DrxkState != DRXK_DTV_STARTED)
+ if (state->m_drxk_state != DRXK_STOPPED &&
+ state->m_drxk_state != DRXK_DTV_STARTED)
goto error;
- state->m_bMirrorFreqSpect = (state->props.inversion == INVERSION_ON);
+ state->m_b_mirror_freq_spect = (state->props.inversion == INVERSION_ON);
- if (IntermediateFrequency < 0) {
- state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect;
- IntermediateFrequency = -IntermediateFrequency;
+ if (intermediate_frequency < 0) {
+ state->m_b_mirror_freq_spect = !state->m_b_mirror_freq_spect;
+ intermediate_frequency = -intermediate_frequency;
}
- switch (state->m_OperationMode) {
+ switch (state->m_operation_mode) {
case OM_QAM_ITU_A:
case OM_QAM_ITU_C:
- IFreqkHz = (IntermediateFrequency / 1000);
- status = SetQAM(state, IFreqkHz, OffsetkHz);
+ i_freqk_hz = (intermediate_frequency / 1000);
+ status = set_qam(state, i_freqk_hz, offsetk_hz);
if (status < 0)
goto error;
- state->m_DrxkState = DRXK_DTV_STARTED;
+ state->m_drxk_state = DRXK_DTV_STARTED;
break;
case OM_DVBT:
- IFreqkHz = (IntermediateFrequency / 1000);
- status = MPEGTSStop(state);
+ i_freqk_hz = (intermediate_frequency / 1000);
+ status = mpegts_stop(state);
if (status < 0)
goto error;
- status = SetDVBT(state, IFreqkHz, OffsetkHz);
+ status = set_dvbt(state, i_freqk_hz, offsetk_hz);
if (status < 0)
goto error;
- status = DVBTStart(state);
+ status = dvbt_start(state);
if (status < 0)
goto error;
- state->m_DrxkState = DRXK_DTV_STARTED;
+ state->m_drxk_state = DRXK_DTV_STARTED;
break;
default:
break;
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int ShutDown(struct drxk_state *state)
+static int shut_down(struct drxk_state *state)
{
dprintk(1, "\n");
- MPEGTSStop(state);
+ mpegts_stop(state);
return 0;
}
-static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus)
+static int get_lock_status(struct drxk_state *state, u32 *p_lock_status)
{
int status = -EINVAL;
dprintk(1, "\n");
- if (pLockStatus == NULL)
+ if (p_lock_status == NULL)
goto error;
- *pLockStatus = NOT_LOCKED;
+ *p_lock_status = NOT_LOCKED;
/* define the SCU command code */
- switch (state->m_OperationMode) {
+ switch (state->m_operation_mode) {
case OM_QAM_ITU_A:
case OM_QAM_ITU_B:
case OM_QAM_ITU_C:
- status = GetQAMLockStatus(state, pLockStatus);
+ status = get_qam_lock_status(state, p_lock_status);
break;
case OM_DVBT:
- status = GetDVBTLockStatus(state, pLockStatus);
+ status = get_dvbt_lock_status(state, p_lock_status);
break;
default:
break;
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int MPEGTSStart(struct drxk_state *state)
+static int mpegts_start(struct drxk_state *state)
{
int status;
- u16 fecOcSncMode = 0;
+ u16 fec_oc_snc_mode = 0;
/* Allow OC to sync again */
- status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+ status = read16(state, FEC_OC_SNC_MODE__A, &fec_oc_snc_mode);
if (status < 0)
goto error;
- fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
- status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+ fec_oc_snc_mode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fec_oc_snc_mode);
if (status < 0)
goto error;
status = write16(state, FEC_OC_SNC_UNLOCK__A, 1);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int MPEGTSDtoInit(struct drxk_state *state)
+static int mpegts_dto_init(struct drxk_state *state)
{
int status;
@@ -2040,68 +1979,68 @@
status = write16(state, FEC_OC_SNC_HWM__A, 12);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int MPEGTSDtoSetup(struct drxk_state *state,
- enum OperationMode oMode)
+static int mpegts_dto_setup(struct drxk_state *state,
+ enum operation_mode o_mode)
{
int status;
- u16 fecOcRegMode = 0; /* FEC_OC_MODE register value */
- u16 fecOcRegIprMode = 0; /* FEC_OC_IPR_MODE register value */
- u16 fecOcDtoMode = 0; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcFctMode = 0; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcDtoPeriod = 2; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcDtoBurstLen = 188; /* FEC_OC_IPR_INVERT register value */
- u32 fecOcRcnCtlRate = 0; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcTmdMode = 0;
- u16 fecOcTmdIntUpdRate = 0;
- u32 maxBitRate = 0;
- bool staticCLK = false;
+ u16 fec_oc_reg_mode = 0; /* FEC_OC_MODE register value */
+ u16 fec_oc_reg_ipr_mode = 0; /* FEC_OC_IPR_MODE register value */
+ u16 fec_oc_dto_mode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_fct_mode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_dto_period = 2; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_dto_burst_len = 188; /* FEC_OC_IPR_INVERT register value */
+ u32 fec_oc_rcn_ctl_rate = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fec_oc_tmd_mode = 0;
+ u16 fec_oc_tmd_int_upd_rate = 0;
+ u32 max_bit_rate = 0;
+ bool static_clk = false;
dprintk(1, "\n");
/* Check insertion of the Reed-Solomon parity bytes */
- status = read16(state, FEC_OC_MODE__A, &fecOcRegMode);
+ status = read16(state, FEC_OC_MODE__A, &fec_oc_reg_mode);
if (status < 0)
goto error;
- status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode);
+ status = read16(state, FEC_OC_IPR_MODE__A, &fec_oc_reg_ipr_mode);
if (status < 0)
goto error;
- fecOcRegMode &= (~FEC_OC_MODE_PARITY__M);
- fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
- if (state->m_insertRSByte == true) {
+ fec_oc_reg_mode &= (~FEC_OC_MODE_PARITY__M);
+ fec_oc_reg_ipr_mode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+ if (state->m_insert_rs_byte == true) {
/* enable parity symbol forward */
- fecOcRegMode |= FEC_OC_MODE_PARITY__M;
+ fec_oc_reg_mode |= FEC_OC_MODE_PARITY__M;
/* MVAL disable during parity bytes */
- fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
/* TS burst length to 204 */
- fecOcDtoBurstLen = 204;
+ fec_oc_dto_burst_len = 204;
}
/* Check serial or parrallel output */
- fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
- if (state->m_enableParallel == false) {
+ fec_oc_reg_ipr_mode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+ if (state->m_enable_parallel == false) {
/* MPEG data output is serial -> set ipr_mode[0] */
- fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M;
+ fec_oc_reg_ipr_mode |= FEC_OC_IPR_MODE_SERIAL__M;
}
- switch (oMode) {
+ switch (o_mode) {
case OM_DVBT:
- maxBitRate = state->m_DVBTBitrate;
- fecOcTmdMode = 3;
- fecOcRcnCtlRate = 0xC00000;
- staticCLK = state->m_DVBTStaticCLK;
+ max_bit_rate = state->m_dvbt_bitrate;
+ fec_oc_tmd_mode = 3;
+ fec_oc_rcn_ctl_rate = 0xC00000;
+ static_clk = state->m_dvbt_static_clk;
break;
case OM_QAM_ITU_A: /* fallthrough */
case OM_QAM_ITU_C:
- fecOcTmdMode = 0x0004;
- fecOcRcnCtlRate = 0xD2B4EE; /* good for >63 Mb/s */
- maxBitRate = state->m_DVBCBitrate;
- staticCLK = state->m_DVBCStaticCLK;
+ fec_oc_tmd_mode = 0x0004;
+ fec_oc_rcn_ctl_rate = 0xD2B4EE; /* good for >63 Mb/s */
+ max_bit_rate = state->m_dvbc_bitrate;
+ static_clk = state->m_dvbc_static_clk;
break;
default:
status = -EINVAL;
@@ -2110,83 +2049,84 @@
goto error;
/* Configure DTO's */
- if (staticCLK) {
- u32 bitRate = 0;
+ if (static_clk) {
+ u32 bit_rate = 0;
/* Rational DTO for MCLK source (static MCLK rate),
Dynamic DTO for optimal grouping
(avoid intra-packet gaps),
DTO offset enable to sync TS burst with MSTRT */
- fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M |
+ fec_oc_dto_mode = (FEC_OC_DTO_MODE_DYNAMIC__M |
FEC_OC_DTO_MODE_OFFSET_ENABLE__M);
- fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M |
+ fec_oc_fct_mode = (FEC_OC_FCT_MODE_RAT_ENA__M |
FEC_OC_FCT_MODE_VIRT_ENA__M);
/* Check user defined bitrate */
- bitRate = maxBitRate;
- if (bitRate > 75900000UL) { /* max is 75.9 Mb/s */
- bitRate = 75900000UL;
+ bit_rate = max_bit_rate;
+ if (bit_rate > 75900000UL) { /* max is 75.9 Mb/s */
+ bit_rate = 75900000UL;
}
/* Rational DTO period:
dto_period = (Fsys / bitrate) - 2
- Result should be floored,
+ result should be floored,
to make sure >= requested bitrate
*/
- fecOcDtoPeriod = (u16) (((state->m_sysClockFreq)
- * 1000) / bitRate);
- if (fecOcDtoPeriod <= 2)
- fecOcDtoPeriod = 0;
+ fec_oc_dto_period = (u16) (((state->m_sys_clock_freq)
+ * 1000) / bit_rate);
+ if (fec_oc_dto_period <= 2)
+ fec_oc_dto_period = 0;
else
- fecOcDtoPeriod -= 2;
- fecOcTmdIntUpdRate = 8;
+ fec_oc_dto_period -= 2;
+ fec_oc_tmd_int_upd_rate = 8;
} else {
- /* (commonAttr->staticCLK == false) => dynamic mode */
- fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M;
- fecOcFctMode = FEC_OC_FCT_MODE__PRE;
- fecOcTmdIntUpdRate = 5;
+ /* (commonAttr->static_clk == false) => dynamic mode */
+ fec_oc_dto_mode = FEC_OC_DTO_MODE_DYNAMIC__M;
+ fec_oc_fct_mode = FEC_OC_FCT_MODE__PRE;
+ fec_oc_tmd_int_upd_rate = 5;
}
/* Write appropriate registers with requested configuration */
- status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen);
+ status = write16(state, FEC_OC_DTO_BURST_LEN__A, fec_oc_dto_burst_len);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod);
+ status = write16(state, FEC_OC_DTO_PERIOD__A, fec_oc_dto_period);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode);
+ status = write16(state, FEC_OC_DTO_MODE__A, fec_oc_dto_mode);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode);
+ status = write16(state, FEC_OC_FCT_MODE__A, fec_oc_fct_mode);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_MODE__A, fecOcRegMode);
+ status = write16(state, FEC_OC_MODE__A, fec_oc_reg_mode);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode);
+ status = write16(state, FEC_OC_IPR_MODE__A, fec_oc_reg_ipr_mode);
if (status < 0)
goto error;
/* Rate integration settings */
- status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate);
+ status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fec_oc_rcn_ctl_rate);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate);
+ status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A,
+ fec_oc_tmd_int_upd_rate);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode);
+ status = write16(state, FEC_OC_TMD_MODE__A, fec_oc_tmd_mode);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int MPEGTSConfigurePolarity(struct drxk_state *state)
+static int mpegts_configure_polarity(struct drxk_state *state)
{
- u16 fecOcRegIprInvert = 0;
+ u16 fec_oc_reg_ipr_invert = 0;
/* Data mask for the output data byte */
- u16 InvertDataMask =
+ u16 invert_data_mask =
FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
@@ -2195,40 +2135,40 @@
dprintk(1, "\n");
/* Control selective inversion of output bits */
- fecOcRegIprInvert &= (~(InvertDataMask));
- if (state->m_invertDATA == true)
- fecOcRegIprInvert |= InvertDataMask;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M));
- if (state->m_invertERR == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
- if (state->m_invertSTR == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
- if (state->m_invertVAL == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
- if (state->m_invertCLK == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M;
+ fec_oc_reg_ipr_invert &= (~(invert_data_mask));
+ if (state->m_invert_data == true)
+ fec_oc_reg_ipr_invert |= invert_data_mask;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+ if (state->m_invert_err == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MERR__M;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+ if (state->m_invert_str == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MSTRT__M;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+ if (state->m_invert_val == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MVAL__M;
+ fec_oc_reg_ipr_invert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+ if (state->m_invert_clk == true)
+ fec_oc_reg_ipr_invert |= FEC_OC_IPR_INVERT_MCLK__M;
- return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert);
+ return write16(state, FEC_OC_IPR_INVERT__A, fec_oc_reg_ipr_invert);
}
#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
-static int SetAgcRf(struct drxk_state *state,
- struct SCfgAgc *pAgcCfg, bool isDTV)
+static int set_agc_rf(struct drxk_state *state,
+ struct s_cfg_agc *p_agc_cfg, bool is_dtv)
{
int status = -EINVAL;
u16 data = 0;
- struct SCfgAgc *pIfAgcSettings;
+ struct s_cfg_agc *p_if_agc_settings;
dprintk(1, "\n");
- if (pAgcCfg == NULL)
+ if (p_agc_cfg == NULL)
goto error;
- switch (pAgcCfg->ctrlMode) {
+ switch (p_agc_cfg->ctrl_mode) {
case DRXK_AGC_CTRL_AUTO:
/* Enable RF AGC DAC */
status = read16(state, IQM_AF_STDBY__A, &data);
@@ -2246,7 +2186,7 @@
data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
/* Polarity */
- if (state->m_RfAgcPol)
+ if (state->m_rf_agc_pol)
data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
else
data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
@@ -2260,7 +2200,7 @@
goto error;
data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
- data |= (~(pAgcCfg->speed <<
+ data |= (~(p_agc_cfg->speed <<
SCU_RAM_AGC_KI_RED_RAGC_RED__B)
& SCU_RAM_AGC_KI_RED_RAGC_RED__M);
@@ -2268,30 +2208,34 @@
if (status < 0)
goto error;
- if (IsDVBT(state))
- pIfAgcSettings = &state->m_dvbtIfAgcCfg;
- else if (IsQAM(state))
- pIfAgcSettings = &state->m_qamIfAgcCfg;
+ if (is_dvbt(state))
+ p_if_agc_settings = &state->m_dvbt_if_agc_cfg;
+ else if (is_qam(state))
+ p_if_agc_settings = &state->m_qam_if_agc_cfg;
else
- pIfAgcSettings = &state->m_atvIfAgcCfg;
- if (pIfAgcSettings == NULL) {
+ p_if_agc_settings = &state->m_atv_if_agc_cfg;
+ if (p_if_agc_settings == NULL) {
status = -EINVAL;
goto error;
}
/* Set TOP, only if IF-AGC is in AUTO mode */
- if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO)
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top);
+ if (p_if_agc_settings->ctrl_mode == DRXK_AGC_CTRL_AUTO)
+ status = write16(state,
+ SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ p_agc_cfg->top);
if (status < 0)
goto error;
/* Cut-Off current */
- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent);
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A,
+ p_agc_cfg->cut_off_current);
if (status < 0)
goto error;
/* Max. output level */
- status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel);
+ status = write16(state, SCU_RAM_AGC_RF_MAX__A,
+ p_agc_cfg->max_output_level);
if (status < 0)
goto error;
@@ -2312,7 +2256,7 @@
if (status < 0)
goto error;
data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
- if (state->m_RfAgcPol)
+ if (state->m_rf_agc_pol)
data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
else
data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
@@ -2326,7 +2270,8 @@
goto error;
/* Write value to output pin */
- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel);
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A,
+ p_agc_cfg->output_level);
if (status < 0)
goto error;
break;
@@ -2357,22 +2302,22 @@
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
-static int SetAgcIf(struct drxk_state *state,
- struct SCfgAgc *pAgcCfg, bool isDTV)
+static int set_agc_if(struct drxk_state *state,
+ struct s_cfg_agc *p_agc_cfg, bool is_dtv)
{
u16 data = 0;
int status = 0;
- struct SCfgAgc *pRfAgcSettings;
+ struct s_cfg_agc *p_rf_agc_settings;
dprintk(1, "\n");
- switch (pAgcCfg->ctrlMode) {
+ switch (p_agc_cfg->ctrl_mode) {
case DRXK_AGC_CTRL_AUTO:
/* Enable IF AGC DAC */
@@ -2392,7 +2337,7 @@
data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
/* Polarity */
- if (state->m_IfAgcPol)
+ if (state->m_if_agc_pol)
data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
else
data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
@@ -2405,7 +2350,7 @@
if (status < 0)
goto error;
data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
- data |= (~(pAgcCfg->speed <<
+ data |= (~(p_agc_cfg->speed <<
SCU_RAM_AGC_KI_RED_IAGC_RED__B)
& SCU_RAM_AGC_KI_RED_IAGC_RED__M);
@@ -2413,14 +2358,15 @@
if (status < 0)
goto error;
- if (IsQAM(state))
- pRfAgcSettings = &state->m_qamRfAgcCfg;
+ if (is_qam(state))
+ p_rf_agc_settings = &state->m_qam_rf_agc_cfg;
else
- pRfAgcSettings = &state->m_atvRfAgcCfg;
- if (pRfAgcSettings == NULL)
+ p_rf_agc_settings = &state->m_atv_rf_agc_cfg;
+ if (p_rf_agc_settings == NULL)
return -1;
/* Restore TOP */
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top);
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ p_rf_agc_settings->top);
if (status < 0)
goto error;
break;
@@ -2444,7 +2390,7 @@
data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
/* Polarity */
- if (state->m_IfAgcPol)
+ if (state->m_if_agc_pol)
data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
else
data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
@@ -2453,7 +2399,8 @@
goto error;
/* Write value to output pin */
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel);
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ p_agc_cfg->output_level);
if (status < 0)
goto error;
break;
@@ -2478,176 +2425,181 @@
if (status < 0)
goto error;
break;
- } /* switch (agcSettingsIf->ctrlMode) */
+ } /* switch (agcSettingsIf->ctrl_mode) */
/* always set the top to support
configurations without if-loop */
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top);
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, p_agc_cfg->top);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int GetQAMSignalToNoise(struct drxk_state *state,
- s32 *pSignalToNoise)
+static int get_qam_signal_to_noise(struct drxk_state *state,
+ s32 *p_signal_to_noise)
{
int status = 0;
- u16 qamSlErrPower = 0; /* accum. error between
+ u16 qam_sl_err_power = 0; /* accum. error between
raw and sliced symbols */
- u32 qamSlSigPower = 0; /* used for MER, depends of
+ u32 qam_sl_sig_power = 0; /* used for MER, depends of
QAM modulation */
- u32 qamSlMer = 0; /* QAM MER */
+ u32 qam_sl_mer = 0; /* QAM MER */
dprintk(1, "\n");
/* MER calculation */
/* get the register value needed for MER */
- status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower);
+ status = read16(state, QAM_SL_ERR_POWER__A, &qam_sl_err_power);
if (status < 0) {
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return -EINVAL;
}
switch (state->props.modulation) {
case QAM_16:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
break;
case QAM_32:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
break;
case QAM_64:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
break;
case QAM_128:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
break;
default:
case QAM_256:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
+ qam_sl_sig_power = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
break;
}
- if (qamSlErrPower > 0) {
- qamSlMer = Log10Times100(qamSlSigPower) -
- Log10Times100((u32) qamSlErrPower);
+ if (qam_sl_err_power > 0) {
+ qam_sl_mer = log10times100(qam_sl_sig_power) -
+ log10times100((u32) qam_sl_err_power);
}
- *pSignalToNoise = qamSlMer;
+ *p_signal_to_noise = qam_sl_mer;
return status;
}
-static int GetDVBTSignalToNoise(struct drxk_state *state,
- s32 *pSignalToNoise)
+static int get_dvbt_signal_to_noise(struct drxk_state *state,
+ s32 *p_signal_to_noise)
{
int status;
- u16 regData = 0;
- u32 EqRegTdSqrErrI = 0;
- u32 EqRegTdSqrErrQ = 0;
- u16 EqRegTdSqrErrExp = 0;
- u16 EqRegTdTpsPwrOfs = 0;
- u16 EqRegTdReqSmbCnt = 0;
- u32 tpsCnt = 0;
- u32 SqrErrIQ = 0;
+ u16 reg_data = 0;
+ u32 eq_reg_td_sqr_err_i = 0;
+ u32 eq_reg_td_sqr_err_q = 0;
+ u16 eq_reg_td_sqr_err_exp = 0;
+ u16 eq_reg_td_tps_pwr_ofs = 0;
+ u16 eq_reg_td_req_smb_cnt = 0;
+ u32 tps_cnt = 0;
+ u32 sqr_err_iq = 0;
u32 a = 0;
u32 b = 0;
u32 c = 0;
- u32 iMER = 0;
- u16 transmissionParams = 0;
+ u32 i_mer = 0;
+ u16 transmission_params = 0;
dprintk(1, "\n");
- status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs);
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A,
+ &eq_reg_td_tps_pwr_ofs);
if (status < 0)
goto error;
- status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt);
+ status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A,
+ &eq_reg_td_req_smb_cnt);
if (status < 0)
goto error;
- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp);
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A,
+ &eq_reg_td_sqr_err_exp);
if (status < 0)
goto error;
- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, ®Data);
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A,
+ ®_data);
if (status < 0)
goto error;
/* Extend SQR_ERR_I operational range */
- EqRegTdSqrErrI = (u32) regData;
- if ((EqRegTdSqrErrExp > 11) &&
- (EqRegTdSqrErrI < 0x00000FFFUL)) {
- EqRegTdSqrErrI += 0x00010000UL;
+ eq_reg_td_sqr_err_i = (u32) reg_data;
+ if ((eq_reg_td_sqr_err_exp > 11) &&
+ (eq_reg_td_sqr_err_i < 0x00000FFFUL)) {
+ eq_reg_td_sqr_err_i += 0x00010000UL;
}
- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, ®Data);
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, ®_data);
if (status < 0)
goto error;
/* Extend SQR_ERR_Q operational range */
- EqRegTdSqrErrQ = (u32) regData;
- if ((EqRegTdSqrErrExp > 11) &&
- (EqRegTdSqrErrQ < 0x00000FFFUL))
- EqRegTdSqrErrQ += 0x00010000UL;
+ eq_reg_td_sqr_err_q = (u32) reg_data;
+ if ((eq_reg_td_sqr_err_exp > 11) &&
+ (eq_reg_td_sqr_err_q < 0x00000FFFUL))
+ eq_reg_td_sqr_err_q += 0x00010000UL;
- status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams);
+ status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A,
+ &transmission_params);
if (status < 0)
goto error;
/* Check input data for MER */
/* MER calculation (in 0.1 dB) without math.h */
- if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0))
- iMER = 0;
- else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) {
+ if ((eq_reg_td_tps_pwr_ofs == 0) || (eq_reg_td_req_smb_cnt == 0))
+ i_mer = 0;
+ else if ((eq_reg_td_sqr_err_i + eq_reg_td_sqr_err_q) == 0) {
/* No error at all, this must be the HW reset value
* Apparently no first measurement yet
* Set MER to 0.0 */
- iMER = 0;
+ i_mer = 0;
} else {
- SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) <<
- EqRegTdSqrErrExp;
- if ((transmissionParams &
+ sqr_err_iq = (eq_reg_td_sqr_err_i + eq_reg_td_sqr_err_q) <<
+ eq_reg_td_sqr_err_exp;
+ if ((transmission_params &
OFDM_SC_RA_RAM_OP_PARAM_MODE__M)
== OFDM_SC_RA_RAM_OP_PARAM_MODE_2K)
- tpsCnt = 17;
+ tps_cnt = 17;
else
- tpsCnt = 68;
+ tps_cnt = 68;
/* IMER = 100 * log10 (x)
- where x = (EqRegTdTpsPwrOfs^2 *
- EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ
+ where x = (eq_reg_td_tps_pwr_ofs^2 *
+ eq_reg_td_req_smb_cnt * tps_cnt)/sqr_err_iq
=> IMER = a + b -c
- where a = 100 * log10 (EqRegTdTpsPwrOfs^2)
- b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt)
- c = 100 * log10 (SqrErrIQ)
+ where a = 100 * log10 (eq_reg_td_tps_pwr_ofs^2)
+ b = 100 * log10 (eq_reg_td_req_smb_cnt * tps_cnt)
+ c = 100 * log10 (sqr_err_iq)
*/
/* log(x) x = 9bits * 9bits->18 bits */
- a = Log10Times100(EqRegTdTpsPwrOfs *
- EqRegTdTpsPwrOfs);
+ a = log10times100(eq_reg_td_tps_pwr_ofs *
+ eq_reg_td_tps_pwr_ofs);
/* log(x) x = 16bits * 7bits->23 bits */
- b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt);
+ b = log10times100(eq_reg_td_req_smb_cnt * tps_cnt);
/* log(x) x = (16bits + 16bits) << 15 ->32 bits */
- c = Log10Times100(SqrErrIQ);
+ c = log10times100(sqr_err_iq);
- iMER = a + b - c;
+ i_mer = a + b - c;
}
- *pSignalToNoise = iMER;
+ *p_signal_to_noise = i_mer;
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise)
+static int get_signal_to_noise(struct drxk_state *state, s32 *p_signal_to_noise)
{
dprintk(1, "\n");
- *pSignalToNoise = 0;
- switch (state->m_OperationMode) {
+ *p_signal_to_noise = 0;
+ switch (state->m_operation_mode) {
case OM_DVBT:
- return GetDVBTSignalToNoise(state, pSignalToNoise);
+ return get_dvbt_signal_to_noise(state, p_signal_to_noise);
case OM_QAM_ITU_A:
case OM_QAM_ITU_C:
- return GetQAMSignalToNoise(state, pSignalToNoise);
+ return get_qam_signal_to_noise(state, p_signal_to_noise);
default:
break;
}
@@ -2655,7 +2607,7 @@
}
#if 0
-static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality)
+static int get_dvbt_quality(struct drxk_state *state, s32 *p_quality)
{
/* SNR Values for quasi errorfree reception rom Nordig 2.2 */
int status = 0;
@@ -2680,102 +2632,104 @@
225, /* 64-QAM 7/8 */
};
- *pQuality = 0;
+ *p_quality = 0;
do {
- s32 SignalToNoise = 0;
- u16 Constellation = 0;
- u16 CodeRate = 0;
- u32 SignalToNoiseRel;
- u32 BERQuality;
+ s32 signal_to_noise = 0;
+ u16 constellation = 0;
+ u16 code_rate = 0;
+ u32 signal_to_noise_rel;
+ u32 ber_quality;
- status = GetDVBTSignalToNoise(state, &SignalToNoise);
+ status = get_dvbt_signal_to_noise(state, &signal_to_noise);
if (status < 0)
break;
- status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation);
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A,
+ &constellation);
if (status < 0)
break;
- Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
+ constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
- status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate);
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A,
+ &code_rate);
if (status < 0)
break;
- CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
+ code_rate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
- if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
- CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
+ if (constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
+ code_rate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
break;
- SignalToNoiseRel = SignalToNoise -
- QE_SN[Constellation * 5 + CodeRate];
- BERQuality = 100;
+ signal_to_noise_rel = signal_to_noise -
+ QE_SN[constellation * 5 + code_rate];
+ ber_quality = 100;
- if (SignalToNoiseRel < -70)
- *pQuality = 0;
- else if (SignalToNoiseRel < 30)
- *pQuality = ((SignalToNoiseRel + 70) *
- BERQuality) / 100;
+ if (signal_to_noise_rel < -70)
+ *p_quality = 0;
+ else if (signal_to_noise_rel < 30)
+ *p_quality = ((signal_to_noise_rel + 70) *
+ ber_quality) / 100;
else
- *pQuality = BERQuality;
+ *p_quality = ber_quality;
} while (0);
return 0;
};
-static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality)
+static int get_dvbc_quality(struct drxk_state *state, s32 *p_quality)
{
int status = 0;
- *pQuality = 0;
+ *p_quality = 0;
dprintk(1, "\n");
do {
- u32 SignalToNoise = 0;
- u32 BERQuality = 100;
- u32 SignalToNoiseRel = 0;
+ u32 signal_to_noise = 0;
+ u32 ber_quality = 100;
+ u32 signal_to_noise_rel = 0;
- status = GetQAMSignalToNoise(state, &SignalToNoise);
+ status = get_qam_signal_to_noise(state, &signal_to_noise);
if (status < 0)
break;
switch (state->props.modulation) {
case QAM_16:
- SignalToNoiseRel = SignalToNoise - 200;
+ signal_to_noise_rel = signal_to_noise - 200;
break;
case QAM_32:
- SignalToNoiseRel = SignalToNoise - 230;
+ signal_to_noise_rel = signal_to_noise - 230;
break; /* Not in NorDig */
case QAM_64:
- SignalToNoiseRel = SignalToNoise - 260;
+ signal_to_noise_rel = signal_to_noise - 260;
break;
case QAM_128:
- SignalToNoiseRel = SignalToNoise - 290;
+ signal_to_noise_rel = signal_to_noise - 290;
break;
default:
case QAM_256:
- SignalToNoiseRel = SignalToNoise - 320;
+ signal_to_noise_rel = signal_to_noise - 320;
break;
}
- if (SignalToNoiseRel < -70)
- *pQuality = 0;
- else if (SignalToNoiseRel < 30)
- *pQuality = ((SignalToNoiseRel + 70) *
- BERQuality) / 100;
+ if (signal_to_noise_rel < -70)
+ *p_quality = 0;
+ else if (signal_to_noise_rel < 30)
+ *p_quality = ((signal_to_noise_rel + 70) *
+ ber_quality) / 100;
else
- *pQuality = BERQuality;
+ *p_quality = ber_quality;
} while (0);
return status;
}
-static int GetQuality(struct drxk_state *state, s32 *pQuality)
+static int get_quality(struct drxk_state *state, s32 *p_quality)
{
dprintk(1, "\n");
- switch (state->m_OperationMode) {
+ switch (state->m_operation_mode) {
case OM_DVBT:
- return GetDVBTQuality(state, pQuality);
+ return get_dvbt_quality(state, p_quality);
case OM_QAM_ITU_A:
- return GetDVBCQuality(state, pQuality);
+ return get_dvbc_quality(state, p_quality);
default:
break;
}
@@ -2797,65 +2751,68 @@
#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F)
#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF)
-static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
+static int ConfigureI2CBridge(struct drxk_state *state, bool b_enable_bridge)
{
int status = -EINVAL;
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return 0;
- if (state->m_DrxkState == DRXK_POWERED_DOWN)
+ if (state->m_drxk_state == DRXK_POWERED_DOWN)
goto error;
if (state->no_i2c_bridge)
return 0;
- status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A,
+ SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
if (status < 0)
goto error;
- if (bEnableBridge) {
- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
+ if (b_enable_bridge) {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A,
+ SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
if (status < 0)
goto error;
} else {
- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A,
+ SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
if (status < 0)
goto error;
}
- status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
+ status = hi_command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SetPreSaw(struct drxk_state *state,
- struct SCfgPreSaw *pPreSawCfg)
+static int set_pre_saw(struct drxk_state *state,
+ struct s_cfg_pre_saw *p_pre_saw_cfg)
{
int status = -EINVAL;
dprintk(1, "\n");
- if ((pPreSawCfg == NULL)
- || (pPreSawCfg->reference > IQM_AF_PDREF__M))
+ if ((p_pre_saw_cfg == NULL)
+ || (p_pre_saw_cfg->reference > IQM_AF_PDREF__M))
goto error;
- status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference);
+ status = write16(state, IQM_AF_PDREF__A, p_pre_saw_cfg->reference);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int BLDirectCmd(struct drxk_state *state, u32 targetAddr,
- u16 romOffset, u16 nrOfElements, u32 timeOut)
+static int bl_direct_cmd(struct drxk_state *state, u32 target_addr,
+ u16 rom_offset, u16 nr_of_elements, u32 time_out)
{
- u16 blStatus = 0;
- u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF);
- u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF);
+ u16 bl_status = 0;
+ u16 offset = (u16) ((target_addr >> 0) & 0x00FFFF);
+ u16 blockbank = (u16) ((target_addr >> 16) & 0x000FFF);
int status;
unsigned long end;
@@ -2871,44 +2828,44 @@
status = write16(state, SIO_BL_TGT_ADDR__A, offset);
if (status < 0)
goto error;
- status = write16(state, SIO_BL_SRC_ADDR__A, romOffset);
+ status = write16(state, SIO_BL_SRC_ADDR__A, rom_offset);
if (status < 0)
goto error;
- status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements);
+ status = write16(state, SIO_BL_SRC_LEN__A, nr_of_elements);
if (status < 0)
goto error;
status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
if (status < 0)
goto error;
- end = jiffies + msecs_to_jiffies(timeOut);
+ end = jiffies + msecs_to_jiffies(time_out);
do {
- status = read16(state, SIO_BL_STATUS__A, &blStatus);
+ status = read16(state, SIO_BL_STATUS__A, &bl_status);
if (status < 0)
goto error;
- } while ((blStatus == 0x1) && time_is_after_jiffies(end));
- if (blStatus == 0x1) {
- printk(KERN_ERR "drxk: SIO not ready\n");
+ } while ((bl_status == 0x1) && time_is_after_jiffies(end));
+ if (bl_status == 0x1) {
+ pr_err("SIO not ready\n");
status = -EINVAL;
goto error2;
}
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
error2:
mutex_unlock(&state->mutex);
return status;
}
-static int ADCSyncMeasurement(struct drxk_state *state, u16 *count)
+static int adc_sync_measurement(struct drxk_state *state, u16 *count)
{
u16 data = 0;
int status;
dprintk(1, "\n");
- /* Start measurement */
+ /* start measurement */
status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE);
if (status < 0)
goto error;
@@ -2935,42 +2892,42 @@
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int ADCSynchronization(struct drxk_state *state)
+static int adc_synchronization(struct drxk_state *state)
{
u16 count = 0;
int status;
dprintk(1, "\n");
- status = ADCSyncMeasurement(state, &count);
+ status = adc_sync_measurement(state, &count);
if (status < 0)
goto error;
if (count == 1) {
/* Try sampling on a diffrent edge */
- u16 clkNeg = 0;
+ u16 clk_neg = 0;
- status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
+ status = read16(state, IQM_AF_CLKNEG__A, &clk_neg);
if (status < 0)
goto error;
- if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+ if ((clk_neg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
- clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
- clkNeg |=
+ clk_neg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clk_neg |=
IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG;
} else {
- clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
- clkNeg |=
+ clk_neg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clk_neg |=
IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS;
}
- status = write16(state, IQM_AF_CLKNEG__A, clkNeg);
+ status = write16(state, IQM_AF_CLKNEG__A, clk_neg);
if (status < 0)
goto error;
- status = ADCSyncMeasurement(state, &count);
+ status = adc_sync_measurement(state, &count);
if (status < 0)
goto error;
}
@@ -2979,25 +2936,25 @@
status = -EINVAL;
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SetFrequencyShifter(struct drxk_state *state,
- u16 intermediateFreqkHz,
- s32 tunerFreqOffset, bool isDTV)
+static int set_frequency_shifter(struct drxk_state *state,
+ u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset, bool is_dtv)
{
- bool selectPosImage = false;
- u32 rfFreqResidual = tunerFreqOffset;
- u32 fmFrequencyShift = 0;
- bool tunerMirror = !state->m_bMirrorFreqSpect;
- u32 adcFreq;
- bool adcFlip;
+ bool select_pos_image = false;
+ u32 rf_freq_residual = tuner_freq_offset;
+ u32 fm_frequency_shift = 0;
+ bool tuner_mirror = !state->m_b_mirror_freq_spect;
+ u32 adc_freq;
+ bool adc_flip;
int status;
- u32 ifFreqActual;
- u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3);
- u32 frequencyShift;
- bool imageToSelect;
+ u32 if_freq_actual;
+ u32 sampling_frequency = (u32) (state->m_sys_clock_freq / 3);
+ u32 frequency_shift;
+ bool image_to_select;
dprintk(1, "\n");
@@ -3005,121 +2962,125 @@
Program frequency shifter
No need to account for mirroring on RF
*/
- if (isDTV) {
- if ((state->m_OperationMode == OM_QAM_ITU_A) ||
- (state->m_OperationMode == OM_QAM_ITU_C) ||
- (state->m_OperationMode == OM_DVBT))
- selectPosImage = true;
+ if (is_dtv) {
+ if ((state->m_operation_mode == OM_QAM_ITU_A) ||
+ (state->m_operation_mode == OM_QAM_ITU_C) ||
+ (state->m_operation_mode == OM_DVBT))
+ select_pos_image = true;
else
- selectPosImage = false;
+ select_pos_image = false;
}
- if (tunerMirror)
+ if (tuner_mirror)
/* tuner doesn't mirror */
- ifFreqActual = intermediateFreqkHz +
- rfFreqResidual + fmFrequencyShift;
+ if_freq_actual = intermediate_freqk_hz +
+ rf_freq_residual + fm_frequency_shift;
else
/* tuner mirrors */
- ifFreqActual = intermediateFreqkHz -
- rfFreqResidual - fmFrequencyShift;
- if (ifFreqActual > samplingFrequency / 2) {
+ if_freq_actual = intermediate_freqk_hz -
+ rf_freq_residual - fm_frequency_shift;
+ if (if_freq_actual > sampling_frequency / 2) {
/* adc mirrors */
- adcFreq = samplingFrequency - ifFreqActual;
- adcFlip = true;
+ adc_freq = sampling_frequency - if_freq_actual;
+ adc_flip = true;
} else {
/* adc doesn't mirror */
- adcFreq = ifFreqActual;
- adcFlip = false;
+ adc_freq = if_freq_actual;
+ adc_flip = false;
}
- frequencyShift = adcFreq;
- imageToSelect = state->m_rfmirror ^ tunerMirror ^
- adcFlip ^ selectPosImage;
- state->m_IqmFsRateOfs =
- Frac28a((frequencyShift), samplingFrequency);
+ frequency_shift = adc_freq;
+ image_to_select = state->m_rfmirror ^ tuner_mirror ^
+ adc_flip ^ select_pos_image;
+ state->m_iqm_fs_rate_ofs =
+ Frac28a((frequency_shift), sampling_frequency);
- if (imageToSelect)
- state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1;
+ if (image_to_select)
+ state->m_iqm_fs_rate_ofs = ~state->m_iqm_fs_rate_ofs + 1;
/* Program frequency shifter with tuner offset compensation */
- /* frequencyShift += tunerFreqOffset; TODO */
+ /* frequency_shift += tuner_freq_offset; TODO */
status = write32(state, IQM_FS_RATE_OFS_LO__A,
- state->m_IqmFsRateOfs);
+ state->m_iqm_fs_rate_ofs);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int InitAGC(struct drxk_state *state, bool isDTV)
+static int init_agc(struct drxk_state *state, bool is_dtv)
{
- u16 ingainTgt = 0;
- u16 ingainTgtMin = 0;
- u16 ingainTgtMax = 0;
- u16 clpCyclen = 0;
- u16 clpSumMin = 0;
- u16 clpDirTo = 0;
- u16 snsSumMin = 0;
- u16 snsSumMax = 0;
- u16 clpSumMax = 0;
- u16 snsDirTo = 0;
- u16 kiInnergainMin = 0;
- u16 ifIaccuHiTgt = 0;
- u16 ifIaccuHiTgtMin = 0;
- u16 ifIaccuHiTgtMax = 0;
+ u16 ingain_tgt = 0;
+ u16 ingain_tgt_min = 0;
+ u16 ingain_tgt_max = 0;
+ u16 clp_cyclen = 0;
+ u16 clp_sum_min = 0;
+ u16 clp_dir_to = 0;
+ u16 sns_sum_min = 0;
+ u16 sns_sum_max = 0;
+ u16 clp_sum_max = 0;
+ u16 sns_dir_to = 0;
+ u16 ki_innergain_min = 0;
+ u16 if_iaccu_hi_tgt = 0;
+ u16 if_iaccu_hi_tgt_min = 0;
+ u16 if_iaccu_hi_tgt_max = 0;
u16 data = 0;
- u16 fastClpCtrlDelay = 0;
- u16 clpCtrlMode = 0;
+ u16 fast_clp_ctrl_delay = 0;
+ u16 clp_ctrl_mode = 0;
int status = 0;
dprintk(1, "\n");
/* Common settings */
- snsSumMax = 1023;
- ifIaccuHiTgtMin = 2047;
- clpCyclen = 500;
- clpSumMax = 1023;
+ sns_sum_max = 1023;
+ if_iaccu_hi_tgt_min = 2047;
+ clp_cyclen = 500;
+ clp_sum_max = 1023;
/* AGCInit() not available for DVBT; init done in microcode */
- if (!IsQAM(state)) {
- printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode);
+ if (!is_qam(state)) {
+ pr_err("%s: mode %d is not DVB-C\n",
+ __func__, state->m_operation_mode);
return -EINVAL;
}
/* FIXME: Analog TV AGC require different settings */
/* Standard specific settings */
- clpSumMin = 8;
- clpDirTo = (u16) -9;
- clpCtrlMode = 0;
- snsSumMin = 8;
- snsDirTo = (u16) -9;
- kiInnergainMin = (u16) -1030;
- ifIaccuHiTgtMax = 0x2380;
- ifIaccuHiTgt = 0x2380;
- ingainTgtMin = 0x0511;
- ingainTgt = 0x0511;
- ingainTgtMax = 5119;
- fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay;
+ clp_sum_min = 8;
+ clp_dir_to = (u16) -9;
+ clp_ctrl_mode = 0;
+ sns_sum_min = 8;
+ sns_dir_to = (u16) -9;
+ ki_innergain_min = (u16) -1030;
+ if_iaccu_hi_tgt_max = 0x2380;
+ if_iaccu_hi_tgt = 0x2380;
+ ingain_tgt_min = 0x0511;
+ ingain_tgt = 0x0511;
+ ingain_tgt_max = 5119;
+ fast_clp_ctrl_delay = state->m_qam_if_agc_cfg.fast_clip_ctrl_delay;
- status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay);
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A,
+ fast_clp_ctrl_delay);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode);
+ status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clp_ctrl_mode);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt);
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingain_tgt);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin);
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingain_tgt_min);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax);
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingain_tgt_max);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin);
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A,
+ if_iaccu_hi_tgt_min);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax);
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A,
+ if_iaccu_hi_tgt_max);
if (status < 0)
goto error;
status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0);
@@ -3134,20 +3095,22 @@
status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax);
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clp_sum_max);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax);
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, sns_sum_max);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin);
+ status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A,
+ ki_innergain_min);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt);
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A,
+ if_iaccu_hi_tgt);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen);
+ status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clp_cyclen);
if (status < 0)
goto error;
@@ -3164,16 +3127,16 @@
status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin);
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clp_sum_min);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin);
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, sns_sum_min);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo);
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clp_dir_to);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo);
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, sns_dir_to);
if (status < 0)
goto error;
status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff);
@@ -3233,38 +3196,39 @@
status = write16(state, SCU_RAM_AGC_KI__A, data);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr)
+static int dvbtqam_get_acc_pkt_err(struct drxk_state *state, u16 *packet_err)
{
int status;
dprintk(1, "\n");
- if (packetErr == NULL)
+ if (packet_err == NULL)
status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
else
- status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr);
+ status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A,
+ packet_err);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int DVBTScCommand(struct drxk_state *state,
+static int dvbt_sc_command(struct drxk_state *state,
u16 cmd, u16 subcmd,
u16 param0, u16 param1, u16 param2,
u16 param3, u16 param4)
{
- u16 curCmd = 0;
- u16 errCode = 0;
- u16 retryCnt = 0;
- u16 scExec = 0;
+ u16 cur_cmd = 0;
+ u16 err_code = 0;
+ u16 retry_cnt = 0;
+ u16 sc_exec = 0;
int status;
dprintk(1, "\n");
- status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec);
- if (scExec != 1) {
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &sc_exec);
+ if (sc_exec != 1) {
/* SC is not running */
status = -EINVAL;
}
@@ -3272,13 +3236,13 @@
goto error;
/* Wait until sc is ready to receive command */
- retryCnt = 0;
+ retry_cnt = 0;
do {
- msleep(1);
- status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
- retryCnt++;
- } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
- if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+ usleep_range(1000, 2000);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd);
+ retry_cnt++;
+ } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES));
+ if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0))
goto error;
/* Write sub-command */
@@ -3324,18 +3288,18 @@
goto error;
/* Wait until sc is ready processing command */
- retryCnt = 0;
+ retry_cnt = 0;
do {
- msleep(1);
- status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
- retryCnt++;
- } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
- if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+ usleep_range(1000, 2000);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &cur_cmd);
+ retry_cnt++;
+ } while ((cur_cmd != 0) && (retry_cnt < DRXK_MAX_RETRIES));
+ if (retry_cnt >= DRXK_MAX_RETRIES && (status < 0))
goto error;
/* Check for illegal cmd */
- status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode);
- if (errCode == 0xFFFF) {
+ status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &err_code);
+ if (err_code == 0xFFFF) {
/* illegal command */
status = -EINVAL;
}
@@ -3367,23 +3331,23 @@
} /* switch (cmd->cmd) */
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int PowerUpDVBT(struct drxk_state *state)
+static int power_up_dvbt(struct drxk_state *state)
{
- enum DRXPowerMode powerMode = DRX_POWER_UP;
+ enum drx_power_mode power_mode = DRX_POWER_UP;
int status;
dprintk(1, "\n");
- status = CtrlPowerMode(state, &powerMode);
+ status = ctrl_power_mode(state, &power_mode);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled)
+static int dvbt_ctrl_set_inc_enable(struct drxk_state *state, bool *enabled)
{
int status;
@@ -3393,12 +3357,12 @@
else
status = write16(state, IQM_CF_BYPASSDET__A, 1);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
#define DEFAULT_FR_THRES_8K 4000
-static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled)
+static int dvbt_ctrl_set_fr_enable(struct drxk_state *state, bool *enabled)
{
int status;
@@ -3413,13 +3377,13 @@
status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0);
}
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int DVBTCtrlSetEchoThreshold(struct drxk_state *state,
- struct DRXKCfgDvbtEchoThres_t *echoThres)
+static int dvbt_ctrl_set_echo_threshold(struct drxk_state *state,
+ struct drxk_cfg_dvbt_echo_thres_t *echo_thres)
{
u16 data = 0;
int status;
@@ -3429,16 +3393,16 @@
if (status < 0)
goto error;
- switch (echoThres->fftMode) {
+ switch (echo_thres->fft_mode) {
case DRX_FFTMODE_2K:
data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M;
- data |= ((echoThres->threshold <<
+ data |= ((echo_thres->threshold <<
OFDM_SC_RA_RAM_ECHO_THRES_2K__B)
& (OFDM_SC_RA_RAM_ECHO_THRES_2K__M));
break;
case DRX_FFTMODE_8K:
data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M;
- data |= ((echoThres->threshold <<
+ data |= ((echo_thres->threshold <<
OFDM_SC_RA_RAM_ECHO_THRES_8K__B)
& (OFDM_SC_RA_RAM_ECHO_THRES_8K__M));
break;
@@ -3449,12 +3413,12 @@
status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int DVBTCtrlSetSqiSpeed(struct drxk_state *state,
- enum DRXKCfgDvbtSqiSpeed *speed)
+static int dvbt_ctrl_set_sqi_speed(struct drxk_state *state,
+ enum drxk_cfg_dvbt_sqi_speed *speed)
{
int status = -EINVAL;
@@ -3472,7 +3436,7 @@
(u16) *speed);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -3486,32 +3450,33 @@
* Called in DVBTSetStandard
*
*/
-static int DVBTActivatePresets(struct drxk_state *state)
+static int dvbt_activate_presets(struct drxk_state *state)
{
int status;
bool setincenable = false;
bool setfrenable = true;
- struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K };
- struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K };
+ struct drxk_cfg_dvbt_echo_thres_t echo_thres2k = { 0, DRX_FFTMODE_2K };
+ struct drxk_cfg_dvbt_echo_thres_t echo_thres8k = { 0, DRX_FFTMODE_8K };
dprintk(1, "\n");
- status = DVBTCtrlSetIncEnable(state, &setincenable);
+ status = dvbt_ctrl_set_inc_enable(state, &setincenable);
if (status < 0)
goto error;
- status = DVBTCtrlSetFrEnable(state, &setfrenable);
+ status = dvbt_ctrl_set_fr_enable(state, &setfrenable);
if (status < 0)
goto error;
- status = DVBTCtrlSetEchoThreshold(state, &echoThres2k);
+ status = dvbt_ctrl_set_echo_threshold(state, &echo_thres2k);
if (status < 0)
goto error;
- status = DVBTCtrlSetEchoThreshold(state, &echoThres8k);
+ status = dvbt_ctrl_set_echo_threshold(state, &echo_thres8k);
if (status < 0)
goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax);
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A,
+ state->m_dvbt_if_agc_cfg.ingain_tgt_max);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -3525,25 +3490,30 @@
* For ROM code channel filter taps are loaded from the bootloader. For microcode
* the DVB-T taps from the drxk_filters.h are used.
*/
-static int SetDVBTStandard(struct drxk_state *state,
- enum OperationMode oMode)
+static int set_dvbt_standard(struct drxk_state *state,
+ enum operation_mode o_mode)
{
- u16 cmdResult = 0;
+ u16 cmd_result = 0;
u16 data = 0;
int status;
dprintk(1, "\n");
- PowerUpDVBT(state);
+ power_up_dvbt(state);
/* added antenna switch */
- SwitchAntennaToDVBT(state);
+ switch_antenna_to_dvbt(state);
/* send OFDM reset command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
/* send OFDM setenv command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult);
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
@@ -3575,7 +3545,7 @@
status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
if (status < 0)
goto error;
- status = SetIqmAf(state, true);
+ status = set_iqm_af(state, true);
if (status < 0)
goto error;
@@ -3597,7 +3567,7 @@
status = write16(state, IQM_RC_STRETCH__A, 16);
if (status < 0)
goto error;
- status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */
+ status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */
if (status < 0)
goto error;
status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */
@@ -3618,7 +3588,8 @@
if (status < 0)
goto error;
- status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ status = bl_chain_cmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT,
+ DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
if (status < 0)
goto error;
@@ -3637,10 +3608,10 @@
goto error;
/* IQM will not be reset from here, sync ADC and update/init AGC */
- status = ADCSynchronization(state);
+ status = adc_synchronization(state);
if (status < 0)
goto error;
- status = SetPreSaw(state, &state->m_dvbtPreSawCfg);
+ status = set_pre_saw(state, &state->m_dvbt_pre_saw_cfg);
if (status < 0)
goto error;
@@ -3649,10 +3620,10 @@
if (status < 0)
goto error;
- status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true);
+ status = set_agc_rf(state, &state->m_dvbt_rf_agc_cfg, true);
if (status < 0)
goto error;
- status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true);
+ status = set_agc_if(state, &state->m_dvbt_if_agc_cfg, true);
if (status < 0)
goto error;
@@ -3670,9 +3641,10 @@
if (status < 0)
goto error;
- if (!state->m_DRXK_A3_ROM_CODE) {
- /* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay */
- status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay);
+ if (!state->m_drxk_a3_rom_code) {
+ /* AGCInit() is not done for DVBT, so set agcfast_clip_ctrl_delay */
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A,
+ state->m_dvbt_if_agc_cfg.fast_clip_ctrl_delay);
if (status < 0)
goto error;
}
@@ -3707,41 +3679,43 @@
goto error;
/* Setup MPEG bus */
- status = MPEGTSDtoSetup(state, OM_DVBT);
+ status = mpegts_dto_setup(state, OM_DVBT);
if (status < 0)
goto error;
/* Set DVBT Presets */
- status = DVBTActivatePresets(state);
+ status = dvbt_activate_presets(state);
if (status < 0)
goto error;
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
/*============================================================================*/
/**
-* \brief Start dvbt demodulating for channel.
+* \brief start dvbt demodulating for channel.
* \param demod instance of demodulator.
* \return DRXStatus_t.
*/
-static int DVBTStart(struct drxk_state *state)
+static int dvbt_start(struct drxk_state *state)
{
u16 param1;
int status;
- /* DRXKOfdmScCmd_t scCmd; */
+ /* drxk_ofdm_sc_cmd_t scCmd; */
dprintk(1, "\n");
- /* Start correct processes to get in lock */
+ /* start correct processes to get in lock */
/* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */
param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN;
- status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0);
+ status = dvbt_sc_command(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0,
+ OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1,
+ 0, 0, 0);
if (status < 0)
goto error;
- /* Start FEC OC */
- status = MPEGTSStart(state);
+ /* start FEC OC */
+ status = mpegts_start(state);
if (status < 0)
goto error;
status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
@@ -3749,7 +3723,7 @@
goto error;
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -3762,20 +3736,23 @@
* \return DRXStatus_t.
* // original DVBTSetChannel()
*/
-static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset)
+static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset)
{
- u16 cmdResult = 0;
- u16 transmissionParams = 0;
- u16 operationMode = 0;
- u32 iqmRcRateOfs = 0;
+ u16 cmd_result = 0;
+ u16 transmission_params = 0;
+ u16 operation_mode = 0;
+ u32 iqm_rc_rate_ofs = 0;
u32 bandwidth = 0;
u16 param1;
int status;
- dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset);
+ dprintk(1, "IF =%d, TFO = %d\n",
+ intermediate_freqk_hz, tuner_freq_offset);
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
@@ -3798,19 +3775,19 @@
if (status < 0)
goto error;
- /*== Write channel settings to device =====================================*/
+ /*== Write channel settings to device ================================*/
/* mode */
switch (state->props.transmission_mode) {
case TRANSMISSION_MODE_AUTO:
default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
+ operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
/* fall through , try first guess DRX_FFTMODE_8K */
case TRANSMISSION_MODE_8K:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
break;
case TRANSMISSION_MODE_2K:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
break;
}
@@ -3818,19 +3795,19 @@
switch (state->props.guard_interval) {
default:
case GUARD_INTERVAL_AUTO:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
+ operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
/* fall through , try first guess DRX_GUARD_1DIV4 */
case GUARD_INTERVAL_1_4:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
break;
case GUARD_INTERVAL_1_32:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
break;
case GUARD_INTERVAL_1_16:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
break;
case GUARD_INTERVAL_1_8:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
break;
}
@@ -3839,18 +3816,18 @@
case HIERARCHY_AUTO:
case HIERARCHY_NONE:
default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
+ operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
/* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
- /* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
+ /* transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
/* break; */
case HIERARCHY_1:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
break;
case HIERARCHY_2:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
break;
case HIERARCHY_4:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
break;
}
@@ -3859,16 +3836,16 @@
switch (state->props.modulation) {
case QAM_AUTO:
default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
+ operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
/* fall through , try first guess DRX_CONSTELLATION_QAM64 */
case QAM_64:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
break;
case QPSK:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
break;
case QAM_16:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
break;
}
#if 0
@@ -3876,13 +3853,13 @@
/* Priority (only for hierarchical channels) */
switch (channel->priority) {
case DRX_PRIORITY_LOW:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
- WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
+ WR16(dev_addr, OFDM_EC_SB_PRIOR__A,
OFDM_EC_SB_PRIOR_LO);
break;
case DRX_PRIORITY_HIGH:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
- WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ WR16(dev_addr, OFDM_EC_SB_PRIOR__A,
OFDM_EC_SB_PRIOR_HI));
break;
case DRX_PRIORITY_UNKNOWN: /* fall through */
@@ -3892,7 +3869,7 @@
}
#else
/* Set Priorty high */
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
if (status < 0)
goto error;
@@ -3902,90 +3879,111 @@
switch (state->props.code_rate_HP) {
case FEC_AUTO:
default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
+ operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
/* fall through , try first guess DRX_CODERATE_2DIV3 */
case FEC_2_3:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
break;
case FEC_1_2:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
break;
case FEC_3_4:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
break;
case FEC_5_6:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
break;
case FEC_7_8:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
+ transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
break;
}
- /* SAW filter selection: normaly not necesarry, but if wanted
- the application can select a SAW filter via the driver by using UIOs */
+ /*
+ * SAW filter selection: normaly not necesarry, but if wanted
+ * the application can select a SAW filter via the driver by
+ * using UIOs
+ */
+
/* First determine real bandwidth (Hz) */
/* Also set delay for impulse noise cruncher */
- /* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed
- by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC
- functions */
+ /*
+ * Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is
+ * changed by SC for fix for some 8K,1/8 guard but is restored by
+ * InitEC and ResetEC functions
+ */
switch (state->props.bandwidth_hz) {
case 0:
state->props.bandwidth_hz = 8000000;
/* fall though */
case 8000000:
bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052);
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
+ 3052);
if (status < 0)
goto error;
/* cochannel protection for PAL 8 MHz */
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A,
+ 7);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A,
+ 7);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A,
+ 7);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A,
+ 1);
if (status < 0)
goto error;
break;
case 7000000:
bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ;
- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491);
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
+ 3491);
if (status < 0)
goto error;
/* cochannel protection for PAL 7 MHz */
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A,
+ 8);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A,
+ 8);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A,
+ 4);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A,
+ 1);
if (status < 0)
goto error;
break;
case 6000000:
bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ;
- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073);
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A,
+ 4073);
if (status < 0)
goto error;
/* cochannel protection for NTSC 6 MHz */
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A,
+ 19);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A,
+ 19);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A,
+ 14);
if (status < 0)
goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A,
+ 1);
if (status < 0)
goto error;
break;
@@ -3994,46 +3992,50 @@
goto error;
}
- if (iqmRcRateOfs == 0) {
+ if (iqm_rc_rate_ofs == 0) {
/* Now compute IQM_RC_RATE_OFS
(((SysFreq/BandWidth)/2)/2) -1) * 2^23)
=>
((SysFreq / BandWidth) * (2^21)) - (2^23)
*/
/* (SysFreq / BandWidth) * (2^28) */
- /* assert (MAX(sysClk)/MIN(bandwidth) < 16)
- => assert(MAX(sysClk) < 16*MIN(bandwidth))
- => assert(109714272 > 48000000) = true so Frac 28 can be used */
- iqmRcRateOfs = Frac28a((u32)
- ((state->m_sysClockFreq *
+ /*
+ * assert (MAX(sysClk)/MIN(bandwidth) < 16)
+ * => assert(MAX(sysClk) < 16*MIN(bandwidth))
+ * => assert(109714272 > 48000000) = true
+ * so Frac 28 can be used
+ */
+ iqm_rc_rate_ofs = Frac28a((u32)
+ ((state->m_sys_clock_freq *
1000) / 3), bandwidth);
- /* (SysFreq / BandWidth) * (2^21), rounding before truncating */
- if ((iqmRcRateOfs & 0x7fL) >= 0x40)
- iqmRcRateOfs += 0x80L;
- iqmRcRateOfs = iqmRcRateOfs >> 7;
+ /* (SysFreq / BandWidth) * (2^21), rounding before truncating */
+ if ((iqm_rc_rate_ofs & 0x7fL) >= 0x40)
+ iqm_rc_rate_ofs += 0x80L;
+ iqm_rc_rate_ofs = iqm_rc_rate_ofs >> 7;
/* ((SysFreq / BandWidth) * (2^21)) - (2^23) */
- iqmRcRateOfs = iqmRcRateOfs - (1 << 23);
+ iqm_rc_rate_ofs = iqm_rc_rate_ofs - (1 << 23);
}
- iqmRcRateOfs &=
+ iqm_rc_rate_ofs &=
((((u32) IQM_RC_RATE_OFS_HI__M) <<
IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M);
- status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate_ofs);
if (status < 0)
goto error;
/* Bandwidth setting done */
#if 0
- status = DVBTSetFrequencyShift(demod, channel, tunerOffset);
+ status = dvbt_set_frequency_shift(demod, channel, tuner_offset);
if (status < 0)
goto error;
#endif
- status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+ status = set_frequency_shifter(state, intermediate_freqk_hz,
+ tuner_freq_offset, true);
if (status < 0)
goto error;
- /*== Start SC, write channel settings to SC ===============================*/
+ /*== start SC, write channel settings to SC ==========================*/
/* Activate SCU to enable SCU commands */
status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
@@ -4049,7 +4051,9 @@
goto error;
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM
+ | SCU_RAM_COMMAND_CMD_DEMOD_START,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
@@ -4059,16 +4063,16 @@
OFDM_SC_RA_RAM_OP_AUTO_CONST__M |
OFDM_SC_RA_RAM_OP_AUTO_HIER__M |
OFDM_SC_RA_RAM_OP_AUTO_RATE__M);
- status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
- 0, transmissionParams, param1, 0, 0, 0);
+ status = dvbt_sc_command(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
+ 0, transmission_params, param1, 0, 0, 0);
if (status < 0)
goto error;
- if (!state->m_DRXK_A3_ROM_CODE)
- status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed);
+ if (!state->m_drxk_a3_rom_code)
+ status = dvbt_ctrl_set_sqi_speed(state, &state->m_sqi_speed);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -4083,7 +4087,7 @@
* \return DRXStatus_t.
*
*/
-static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus)
+static int get_dvbt_lock_status(struct drxk_state *state, u32 *p_lock_status)
{
int status;
const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M |
@@ -4091,58 +4095,58 @@
const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M);
const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M;
- u16 ScRaRamLock = 0;
- u16 ScCommExec = 0;
+ u16 sc_ra_ram_lock = 0;
+ u16 sc_comm_exec = 0;
dprintk(1, "\n");
- *pLockStatus = NOT_LOCKED;
+ *p_lock_status = NOT_LOCKED;
/* driver 0.9.0 */
/* Check if SC is running */
- status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec);
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &sc_comm_exec);
if (status < 0)
goto end;
- if (ScCommExec == OFDM_SC_COMM_EXEC_STOP)
+ if (sc_comm_exec == OFDM_SC_COMM_EXEC_STOP)
goto end;
- status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock);
+ status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &sc_ra_ram_lock);
if (status < 0)
goto end;
- if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask)
- *pLockStatus = MPEG_LOCK;
- else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask)
- *pLockStatus = FEC_LOCK;
- else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask)
- *pLockStatus = DEMOD_LOCK;
- else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
- *pLockStatus = NEVER_LOCK;
+ if ((sc_ra_ram_lock & mpeg_lock_mask) == mpeg_lock_mask)
+ *p_lock_status = MPEG_LOCK;
+ else if ((sc_ra_ram_lock & fec_lock_mask) == fec_lock_mask)
+ *p_lock_status = FEC_LOCK;
+ else if ((sc_ra_ram_lock & demod_lock_mask) == demod_lock_mask)
+ *p_lock_status = DEMOD_LOCK;
+ else if (sc_ra_ram_lock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
+ *p_lock_status = NEVER_LOCK;
end:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int PowerUpQAM(struct drxk_state *state)
+static int power_up_qam(struct drxk_state *state)
{
- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM;
int status;
dprintk(1, "\n");
- status = CtrlPowerMode(state, &powerMode);
+ status = ctrl_power_mode(state, &power_mode);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
/** Power Down QAM */
-static int PowerDownQAM(struct drxk_state *state)
+static int power_down_qam(struct drxk_state *state)
{
u16 data = 0;
- u16 cmdResult;
+ u16 cmd_result;
int status = 0;
dprintk(1, "\n");
@@ -4158,16 +4162,18 @@
status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
if (status < 0)
goto error;
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_STOP,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
}
/* powerdown AFE */
- status = SetIqmAf(state, false);
+ status = set_iqm_af(state, false);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -4185,20 +4191,20 @@
* The implementation does not check this.
*
*/
-static int SetQAMMeasurement(struct drxk_state *state,
- enum EDrxkConstellation modulation,
- u32 symbolRate)
+static int set_qam_measurement(struct drxk_state *state,
+ enum e_drxk_constellation modulation,
+ u32 symbol_rate)
{
- u32 fecBitsDesired = 0; /* BER accounting period */
- u32 fecRsPeriodTotal = 0; /* Total period */
- u16 fecRsPrescale = 0; /* ReedSolomon Measurement Prescale */
- u16 fecRsPeriod = 0; /* Value for corresponding I2C register */
+ u32 fec_bits_desired = 0; /* BER accounting period */
+ u32 fec_rs_period_total = 0; /* Total period */
+ u16 fec_rs_prescale = 0; /* ReedSolomon Measurement Prescale */
+ u16 fec_rs_period = 0; /* Value for corresponding I2C register */
int status = 0;
dprintk(1, "\n");
- fecRsPrescale = 1;
- /* fecBitsDesired = symbolRate [kHz] *
+ fec_rs_prescale = 1;
+ /* fec_bits_desired = symbol_rate [kHz] *
FrameLenght [ms] *
(modulation + 1) *
SyncLoss (== 1) *
@@ -4206,19 +4212,19 @@
*/
switch (modulation) {
case DRX_CONSTELLATION_QAM16:
- fecBitsDesired = 4 * symbolRate;
+ fec_bits_desired = 4 * symbol_rate;
break;
case DRX_CONSTELLATION_QAM32:
- fecBitsDesired = 5 * symbolRate;
+ fec_bits_desired = 5 * symbol_rate;
break;
case DRX_CONSTELLATION_QAM64:
- fecBitsDesired = 6 * symbolRate;
+ fec_bits_desired = 6 * symbol_rate;
break;
case DRX_CONSTELLATION_QAM128:
- fecBitsDesired = 7 * symbolRate;
+ fec_bits_desired = 7 * symbol_rate;
break;
case DRX_CONSTELLATION_QAM256:
- fecBitsDesired = 8 * symbolRate;
+ fec_bits_desired = 8 * symbol_rate;
break;
default:
status = -EINVAL;
@@ -4226,40 +4232,41 @@
if (status < 0)
goto error;
- fecBitsDesired /= 1000; /* symbolRate [Hz] -> symbolRate [kHz] */
- fecBitsDesired *= 500; /* meas. period [ms] */
+ fec_bits_desired /= 1000; /* symbol_rate [Hz] -> symbol_rate [kHz] */
+ fec_bits_desired *= 500; /* meas. period [ms] */
/* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */
- /* fecRsPeriodTotal = fecBitsDesired / 1632 */
- fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1; /* roughly ceil */
+ /* fec_rs_period_total = fec_bits_desired / 1632 */
+ fec_rs_period_total = (fec_bits_desired / 1632UL) + 1; /* roughly ceil */
- /* fecRsPeriodTotal = fecRsPrescale * fecRsPeriod */
- fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16);
- if (fecRsPrescale == 0) {
+ /* fec_rs_period_total = fec_rs_prescale * fec_rs_period */
+ fec_rs_prescale = 1 + (u16) (fec_rs_period_total >> 16);
+ if (fec_rs_prescale == 0) {
/* Divide by zero (though impossible) */
status = -EINVAL;
if (status < 0)
goto error;
}
- fecRsPeriod =
- ((u16) fecRsPeriodTotal +
- (fecRsPrescale >> 1)) / fecRsPrescale;
+ fec_rs_period =
+ ((u16) fec_rs_period_total +
+ (fec_rs_prescale >> 1)) / fec_rs_prescale;
/* write corresponding registers */
- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod);
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fec_rs_period);
if (status < 0)
goto error;
- status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale);
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A,
+ fec_rs_prescale);
if (status < 0)
goto error;
- status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod);
+ status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fec_rs_period);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SetQAM16(struct drxk_state *state)
+static int set_qam16(struct drxk_state *state)
{
int status = 0;
@@ -4315,7 +4322,8 @@
goto error;
/* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16);
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM16);
if (status < 0)
goto error;
@@ -4441,7 +4449,7 @@
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -4452,7 +4460,7 @@
* \param demod instance of demod.
* \return DRXStatus_t.
*/
-static int SetQAM32(struct drxk_state *state)
+static int set_qam32(struct drxk_state *state)
{
int status = 0;
@@ -4511,7 +4519,8 @@
/* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32);
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM32);
if (status < 0)
goto error;
@@ -4636,7 +4645,7 @@
status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -4647,7 +4656,7 @@
* \param demod instance of demod.
* \return DRXStatus_t.
*/
-static int SetQAM64(struct drxk_state *state)
+static int set_qam64(struct drxk_state *state)
{
int status = 0;
@@ -4704,7 +4713,8 @@
goto error;
/* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64);
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM64);
if (status < 0)
goto error;
@@ -4829,7 +4839,7 @@
status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -4841,7 +4851,7 @@
* \param demod: instance of demod.
* \return DRXStatus_t.
*/
-static int SetQAM128(struct drxk_state *state)
+static int set_qam128(struct drxk_state *state)
{
int status = 0;
@@ -4900,7 +4910,8 @@
/* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128);
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM128);
if (status < 0)
goto error;
@@ -5025,7 +5036,7 @@
status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -5037,7 +5048,7 @@
* \param demod: instance of demod.
* \return DRXStatus_t.
*/
-static int SetQAM256(struct drxk_state *state)
+static int set_qam256(struct drxk_state *state)
{
int status = 0;
@@ -5095,7 +5106,8 @@
/* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256);
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A,
+ DRXK_QAM_SL_SIG_POWER_QAM256);
if (status < 0)
goto error;
@@ -5220,7 +5232,7 @@
status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -5232,10 +5244,10 @@
* \param channel: pointer to channel data.
* \return DRXStatus_t.
*/
-static int QAMResetQAM(struct drxk_state *state)
+static int qam_reset_qam(struct drxk_state *state)
{
int status;
- u16 cmdResult;
+ u16 cmd_result;
dprintk(1, "\n");
/* Stop QAM comstate->m_exec */
@@ -5243,10 +5255,12 @@
if (status < 0)
goto error;
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_RESET,
+ 0, NULL, 1, &cmd_result);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -5258,18 +5272,18 @@
* \param channel: pointer to channel data.
* \return DRXStatus_t.
*/
-static int QAMSetSymbolrate(struct drxk_state *state)
+static int qam_set_symbolrate(struct drxk_state *state)
{
- u32 adcFrequency = 0;
- u32 symbFreq = 0;
- u32 iqmRcRate = 0;
+ u32 adc_frequency = 0;
+ u32 symb_freq = 0;
+ u32 iqm_rc_rate = 0;
u16 ratesel = 0;
- u32 lcSymbRate = 0;
+ u32 lc_symb_rate = 0;
int status;
dprintk(1, "\n");
/* Select & calculate correct IQM rate */
- adcFrequency = (state->m_sysClockFreq * 1000) / 3;
+ adc_frequency = (state->m_sys_clock_freq * 1000) / 3;
ratesel = 0;
/* printk(KERN_DEBUG "drxk: SR %d\n", state->props.symbol_rate); */
if (state->props.symbol_rate <= 1188750)
@@ -5285,38 +5299,38 @@
/*
IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23)
*/
- symbFreq = state->props.symbol_rate * (1 << ratesel);
- if (symbFreq == 0) {
+ symb_freq = state->props.symbol_rate * (1 << ratesel);
+ if (symb_freq == 0) {
/* Divide by zero */
status = -EINVAL;
goto error;
}
- iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) +
- (Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) -
+ iqm_rc_rate = (adc_frequency / symb_freq) * (1 << 21) +
+ (Frac28a((adc_frequency % symb_freq), symb_freq) >> 7) -
(1 << 23);
- status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqm_rc_rate);
if (status < 0)
goto error;
- state->m_iqmRcRate = iqmRcRate;
+ state->m_iqm_rc_rate = iqm_rc_rate;
/*
- LcSymbFreq = round (.125 * symbolrate / adcFreq * (1<<15))
+ LcSymbFreq = round (.125 * symbolrate / adc_freq * (1<<15))
*/
- symbFreq = state->props.symbol_rate;
- if (adcFrequency == 0) {
+ symb_freq = state->props.symbol_rate;
+ if (adc_frequency == 0) {
/* Divide by zero */
status = -EINVAL;
goto error;
}
- lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) +
- (Frac28a((symbFreq % adcFrequency), adcFrequency) >>
+ lc_symb_rate = (symb_freq / adc_frequency) * (1 << 12) +
+ (Frac28a((symb_freq % adc_frequency), adc_frequency) >>
16);
- if (lcSymbRate > 511)
- lcSymbRate = 511;
- status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate);
+ if (lc_symb_rate > 511)
+ lc_symb_rate = 511;
+ status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lc_symb_rate);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -5329,34 +5343,36 @@
* \return DRXStatus_t.
*/
-static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
+static int get_qam_lock_status(struct drxk_state *state, u32 *p_lock_status)
{
int status;
- u16 Result[2] = { 0, 0 };
+ u16 result[2] = { 0, 0 };
dprintk(1, "\n");
- *pLockStatus = NOT_LOCKED;
+ *p_lock_status = NOT_LOCKED;
status = scu_command(state,
SCU_RAM_COMMAND_STANDARD_QAM |
SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
- Result);
+ result);
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
- if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
+ if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
/* 0x0000 NOT LOCKED */
- } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
+ } else if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
/* 0x4000 DEMOD LOCKED */
- *pLockStatus = DEMOD_LOCK;
- } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
+ *p_lock_status = DEMOD_LOCK;
+ } else if (result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
/* 0x8000 DEMOD + FEC LOCKED (system lock) */
- *pLockStatus = MPEG_LOCK;
+ *p_lock_status = MPEG_LOCK;
} else {
/* 0xC000 NEVER LOCKED */
/* (system will never be able to lock to the signal) */
- /* TODO: check this, intermediate & standard specific lock states are not
- taken into account here */
- *pLockStatus = NEVER_LOCK;
+ /*
+ * TODO: check this, intermediate & standard specific lock
+ * states are not taken into account here
+ */
+ *p_lock_status = NEVER_LOCK;
}
return status;
}
@@ -5368,68 +5384,70 @@
#define QAM_LOCKRANGE__M 0x10
#define QAM_LOCKRANGE_NORMAL 0x10
-static int QAMDemodulatorCommand(struct drxk_state *state,
- int numberOfParameters)
+static int qam_demodulator_command(struct drxk_state *state,
+ int number_of_parameters)
{
int status;
- u16 cmdResult;
- u16 setParamParameters[4] = { 0, 0, 0, 0 };
+ u16 cmd_result;
+ u16 set_param_parameters[4] = { 0, 0, 0, 0 };
- setParamParameters[0] = state->m_Constellation; /* modulation */
- setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
+ set_param_parameters[0] = state->m_constellation; /* modulation */
+ set_param_parameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
- if (numberOfParameters == 2) {
- u16 setEnvParameters[1] = { 0 };
+ if (number_of_parameters == 2) {
+ u16 set_env_parameters[1] = { 0 };
- if (state->m_OperationMode == OM_QAM_ITU_C)
- setEnvParameters[0] = QAM_TOP_ANNEX_C;
+ if (state->m_operation_mode == OM_QAM_ITU_C)
+ set_env_parameters[0] = QAM_TOP_ANNEX_C;
else
- setEnvParameters[0] = QAM_TOP_ANNEX_A;
+ set_env_parameters[0] = QAM_TOP_ANNEX_A;
status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
- 1, setEnvParameters, 1, &cmdResult);
+ SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+ 1, set_env_parameters, 1, &cmd_result);
if (status < 0)
goto error;
status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
- numberOfParameters, setParamParameters,
- 1, &cmdResult);
- } else if (numberOfParameters == 4) {
- if (state->m_OperationMode == OM_QAM_ITU_C)
- setParamParameters[2] = QAM_TOP_ANNEX_C;
+ SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ number_of_parameters, set_param_parameters,
+ 1, &cmd_result);
+ } else if (number_of_parameters == 4) {
+ if (state->m_operation_mode == OM_QAM_ITU_C)
+ set_param_parameters[2] = QAM_TOP_ANNEX_C;
else
- setParamParameters[2] = QAM_TOP_ANNEX_A;
+ set_param_parameters[2] = QAM_TOP_ANNEX_A;
- setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
+ set_param_parameters[3] |= (QAM_MIRROR_AUTO_ON);
/* Env parameters */
/* check for LOCKRANGE Extented */
- /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
+ /* set_param_parameters[3] |= QAM_LOCKRANGE_NORMAL; */
status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
- numberOfParameters, setParamParameters,
- 1, &cmdResult);
+ SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ number_of_parameters, set_param_parameters,
+ 1, &cmd_result);
} else {
- printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter "
- "count %d\n", numberOfParameters);
+ pr_warn("Unknown QAM demodulator parameter count %d\n",
+ number_of_parameters);
status = -EINVAL;
}
error:
if (status < 0)
- printk(KERN_WARNING "drxk: Warning %d on %s\n",
- status, __func__);
+ pr_warn("Warning %d on %s\n", status, __func__);
return status;
}
-static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset)
+static int set_qam(struct drxk_state *state, u16 intermediate_freqk_hz,
+ s32 tuner_freq_offset)
{
int status;
- u16 cmdResult;
- int qamDemodParamCount = state->qam_demod_parameter_count;
+ u16 cmd_result;
+ int qam_demod_param_count = state->qam_demod_parameter_count;
dprintk(1, "\n");
/*
@@ -5444,7 +5462,7 @@
status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP);
if (status < 0)
goto error;
- status = QAMResetQAM(state);
+ status = qam_reset_qam(state);
if (status < 0)
goto error;
@@ -5453,27 +5471,27 @@
* -set params; resets IQM,QAM,FEC HW; initializes some
* SCU variables
*/
- status = QAMSetSymbolrate(state);
+ status = qam_set_symbolrate(state);
if (status < 0)
goto error;
/* Set params */
switch (state->props.modulation) {
case QAM_256:
- state->m_Constellation = DRX_CONSTELLATION_QAM256;
+ state->m_constellation = DRX_CONSTELLATION_QAM256;
break;
case QAM_AUTO:
case QAM_64:
- state->m_Constellation = DRX_CONSTELLATION_QAM64;
+ state->m_constellation = DRX_CONSTELLATION_QAM64;
break;
case QAM_16:
- state->m_Constellation = DRX_CONSTELLATION_QAM16;
+ state->m_constellation = DRX_CONSTELLATION_QAM16;
break;
case QAM_32:
- state->m_Constellation = DRX_CONSTELLATION_QAM32;
+ state->m_constellation = DRX_CONSTELLATION_QAM32;
break;
case QAM_128:
- state->m_Constellation = DRX_CONSTELLATION_QAM128;
+ state->m_constellation = DRX_CONSTELLATION_QAM128;
break;
default:
status = -EINVAL;
@@ -5486,8 +5504,8 @@
* the correct command. */
if (state->qam_demod_parameter_count == 4
|| !state->qam_demod_parameter_count) {
- qamDemodParamCount = 4;
- status = QAMDemodulatorCommand(state, qamDemodParamCount);
+ qam_demod_param_count = 4;
+ status = qam_demodulator_command(state, qam_demod_param_count);
}
/* Use the 2-parameter command if it was requested or if we're
@@ -5495,27 +5513,27 @@
* failed. */
if (state->qam_demod_parameter_count == 2
|| (!state->qam_demod_parameter_count && status < 0)) {
- qamDemodParamCount = 2;
- status = QAMDemodulatorCommand(state, qamDemodParamCount);
+ qam_demod_param_count = 2;
+ status = qam_demodulator_command(state, qam_demod_param_count);
}
if (status < 0) {
- dprintk(1, "Could not set demodulator parameters. Make "
- "sure qam_demod_parameter_count (%d) is correct for "
- "your firmware (%s).\n",
+ dprintk(1, "Could not set demodulator parameters.\n");
+ dprintk(1,
+ "Make sure qam_demod_parameter_count (%d) is correct for your firmware (%s).\n",
state->qam_demod_parameter_count,
state->microcode_name);
goto error;
} else if (!state->qam_demod_parameter_count) {
- dprintk(1, "Auto-probing the correct QAM demodulator command "
- "parameters was successful - using %d parameters.\n",
- qamDemodParamCount);
+ dprintk(1,
+ "Auto-probing the QAM command parameters was successful - using %d parameters.\n",
+ qam_demod_param_count);
/*
* One of our commands was successful. We don't need to
* auto-probe anymore, now that we got the correct command.
*/
- state->qam_demod_parameter_count = qamDemodParamCount;
+ state->qam_demod_parameter_count = qam_demod_param_count;
}
/*
@@ -5523,16 +5541,18 @@
* signal setup modulation independent registers
*/
#if 0
- status = SetFrequency(channel, tunerFreqOffset));
+ status = set_frequency(channel, tuner_freq_offset));
if (status < 0)
goto error;
#endif
- status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+ status = set_frequency_shifter(state, intermediate_freqk_hz,
+ tuner_freq_offset, true);
if (status < 0)
goto error;
/* Setup BER measurement */
- status = SetQAMMeasurement(state, state->m_Constellation, state->props.symbol_rate);
+ status = set_qam_measurement(state, state->m_constellation,
+ state->props.symbol_rate);
if (status < 0)
goto error;
@@ -5605,7 +5625,8 @@
goto error;
/* Mirroring, QAM-block starting point not inverted */
- status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS);
+ status = write16(state, QAM_SY_SP_INV__A,
+ QAM_SY_SP_INV_SPECTRUM_INV_DIS);
if (status < 0)
goto error;
@@ -5617,20 +5638,20 @@
/* STEP 4: modulation specific setup */
switch (state->props.modulation) {
case QAM_16:
- status = SetQAM16(state);
+ status = set_qam16(state);
break;
case QAM_32:
- status = SetQAM32(state);
+ status = set_qam32(state);
break;
case QAM_AUTO:
case QAM_64:
- status = SetQAM64(state);
+ status = set_qam64(state);
break;
case QAM_128:
- status = SetQAM128(state);
+ status = set_qam128(state);
break;
case QAM_256:
- status = SetQAM256(state);
+ status = set_qam256(state);
break;
default:
status = -EINVAL;
@@ -5647,12 +5668,12 @@
/* Re-configure MPEG output, requires knowledge of channel bitrate */
/* extAttr->currentChannel.modulation = channel->modulation; */
/* extAttr->currentChannel.symbolrate = channel->symbolrate; */
- status = MPEGTSDtoSetup(state, state->m_OperationMode);
+ status = mpegts_dto_setup(state, state->m_operation_mode);
if (status < 0)
goto error;
- /* Start processes */
- status = MPEGTSStart(state);
+ /* start processes */
+ status = mpegts_start(state);
if (status < 0)
goto error;
status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
@@ -5666,7 +5687,9 @@
goto error;
/* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM
+ | SCU_RAM_COMMAND_CMD_DEMOD_START,
+ 0, NULL, 1, &cmd_result);
if (status < 0)
goto error;
@@ -5675,12 +5698,12 @@
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SetQAMStandard(struct drxk_state *state,
- enum OperationMode oMode)
+static int set_qam_standard(struct drxk_state *state,
+ enum operation_mode o_mode)
{
int status;
#ifdef DRXK_QAM_TAPS
@@ -5692,14 +5715,14 @@
dprintk(1, "\n");
/* added antenna switch */
- SwitchAntennaToQAM(state);
+ switch_antenna_to_qam(state);
/* Ensure correct power-up mode */
- status = PowerUpQAM(state);
+ status = power_up_qam(state);
if (status < 0)
goto error;
/* Reset QAM block */
- status = QAMResetQAM(state);
+ status = qam_reset_qam(state);
if (status < 0)
goto error;
@@ -5714,15 +5737,24 @@
/* Upload IQM Channel Filter settings by
boot loader from ROM table */
- switch (oMode) {
+ switch (o_mode) {
case OM_QAM_ITU_A:
- status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ status = bl_chain_cmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A,
+ DRXK_BLCC_NR_ELEMENTS_TAPS,
+ DRXK_BLC_TIMEOUT);
break;
case OM_QAM_ITU_C:
- status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ status = bl_direct_cmd(state, IQM_CF_TAP_RE0__A,
+ DRXK_BL_ROM_OFFSET_TAPS_ITU_C,
+ DRXK_BLDC_NR_ELEMENTS_TAPS,
+ DRXK_BLC_TIMEOUT);
if (status < 0)
goto error;
- status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ status = bl_direct_cmd(state,
+ IQM_CF_TAP_IM0__A,
+ DRXK_BL_ROM_OFFSET_TAPS_ITU_C,
+ DRXK_BLDC_NR_ELEMENTS_TAPS,
+ DRXK_BLC_TIMEOUT);
break;
default:
status = -EINVAL;
@@ -5730,13 +5762,14 @@
if (status < 0)
goto error;
- status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B));
+ status = write16(state, IQM_CF_OUT_ENA__A, 1 << IQM_CF_OUT_ENA_QAM__B);
if (status < 0)
goto error;
status = write16(state, IQM_CF_SYMMETRIC__A, 0);
if (status < 0)
goto error;
- status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
+ status = write16(state, IQM_CF_MIDTAP__A,
+ ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
if (status < 0)
goto error;
@@ -5793,7 +5826,7 @@
goto error;
/* turn on IQMAF. Must be done before setAgc**() */
- status = SetIqmAf(state, true);
+ status = set_iqm_af(state, true);
if (status < 0)
goto error;
status = write16(state, IQM_AF_START_LOCK__A, 0x01);
@@ -5801,7 +5834,7 @@
goto error;
/* IQM will not be reset from here, sync ADC and update/init AGC */
- status = ADCSynchronization(state);
+ status = adc_synchronization(state);
if (status < 0)
goto error;
@@ -5818,18 +5851,18 @@
/* No more resets of the IQM, current standard correctly set =>
now AGCs can be configured. */
- status = InitAGC(state, true);
+ status = init_agc(state, true);
if (status < 0)
goto error;
- status = SetPreSaw(state, &(state->m_qamPreSawCfg));
+ status = set_pre_saw(state, &(state->m_qam_pre_saw_cfg));
if (status < 0)
goto error;
/* Configure AGC's */
- status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true);
+ status = set_agc_rf(state, &(state->m_qam_rf_agc_cfg), true);
if (status < 0)
goto error;
- status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true);
+ status = set_agc_if(state, &(state->m_qam_if_agc_cfg), true);
if (status < 0)
goto error;
@@ -5837,18 +5870,19 @@
status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int WriteGPIO(struct drxk_state *state)
+static int write_gpio(struct drxk_state *state)
{
int status;
u16 value = 0;
dprintk(1, "\n");
/* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
@@ -5857,10 +5891,11 @@
if (status < 0)
goto error;
- if (state->m_hasSAWSW) {
- if (state->UIO_mask & 0x0001) { /* UIO-1 */
+ if (state->m_has_sawsw) {
+ if (state->uio_mask & 0x0001) { /* UIO-1 */
/* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ status = write16(state, SIO_PDR_SMA_TX_CFG__A,
+ state->m_gpio_cfg);
if (status < 0)
goto error;
@@ -5868,7 +5903,7 @@
status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
if (status < 0)
goto error;
- if ((state->m_GPIO & 0x0001) == 0)
+ if ((state->m_gpio & 0x0001) == 0)
value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
else
value |= 0x8000; /* write one to 15th bit - 1st UIO */
@@ -5877,9 +5912,10 @@
if (status < 0)
goto error;
}
- if (state->UIO_mask & 0x0002) { /* UIO-2 */
+ if (state->uio_mask & 0x0002) { /* UIO-2 */
/* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg);
+ status = write16(state, SIO_PDR_SMA_RX_CFG__A,
+ state->m_gpio_cfg);
if (status < 0)
goto error;
@@ -5887,7 +5923,7 @@
status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
if (status < 0)
goto error;
- if ((state->m_GPIO & 0x0002) == 0)
+ if ((state->m_gpio & 0x0002) == 0)
value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */
else
value |= 0x4000; /* write one to 14th bit - 2st UIO */
@@ -5896,9 +5932,10 @@
if (status < 0)
goto error;
}
- if (state->UIO_mask & 0x0004) { /* UIO-3 */
+ if (state->uio_mask & 0x0004) { /* UIO-3 */
/* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg);
+ status = write16(state, SIO_PDR_GPIO_CFG__A,
+ state->m_gpio_cfg);
if (status < 0)
goto error;
@@ -5906,7 +5943,7 @@
status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
if (status < 0)
goto error;
- if ((state->m_GPIO & 0x0004) == 0)
+ if ((state->m_gpio & 0x0004) == 0)
value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
else
value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
@@ -5920,11 +5957,11 @@
status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SwitchAntennaToQAM(struct drxk_state *state)
+static int switch_antenna_to_qam(struct drxk_state *state)
{
int status = 0;
bool gpio_state;
@@ -5934,22 +5971,22 @@
if (!state->antenna_gpio)
return 0;
- gpio_state = state->m_GPIO & state->antenna_gpio;
+ gpio_state = state->m_gpio & state->antenna_gpio;
if (state->antenna_dvbt ^ gpio_state) {
/* Antenna is on DVB-T mode. Switch */
if (state->antenna_dvbt)
- state->m_GPIO &= ~state->antenna_gpio;
+ state->m_gpio &= ~state->antenna_gpio;
else
- state->m_GPIO |= state->antenna_gpio;
- status = WriteGPIO(state);
+ state->m_gpio |= state->antenna_gpio;
+ status = write_gpio(state);
}
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int SwitchAntennaToDVBT(struct drxk_state *state)
+static int switch_antenna_to_dvbt(struct drxk_state *state)
{
int status = 0;
bool gpio_state;
@@ -5959,23 +5996,23 @@
if (!state->antenna_gpio)
return 0;
- gpio_state = state->m_GPIO & state->antenna_gpio;
+ gpio_state = state->m_gpio & state->antenna_gpio;
if (!(state->antenna_dvbt ^ gpio_state)) {
/* Antenna is on DVB-C mode. Switch */
if (state->antenna_dvbt)
- state->m_GPIO |= state->antenna_gpio;
+ state->m_gpio |= state->antenna_gpio;
else
- state->m_GPIO &= ~state->antenna_gpio;
- status = WriteGPIO(state);
+ state->m_gpio &= ~state->antenna_gpio;
+ status = write_gpio(state);
}
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
-static int PowerDownDevice(struct drxk_state *state)
+static int power_down_device(struct drxk_state *state)
{
/* Power down to requested mode */
/* Backup some register settings */
@@ -5986,28 +6023,29 @@
int status;
dprintk(1, "\n");
- if (state->m_bPDownOpenBridge) {
+ if (state->m_b_p_down_open_bridge) {
/* Open I2C bridge before power down of DRXK */
status = ConfigureI2CBridge(state, true);
if (status < 0)
goto error;
}
/* driver 0.9.0 */
- status = DVBTEnableOFDMTokenRing(state, false);
+ status = dvbt_enable_ofdm_token_ring(state, false);
if (status < 0)
goto error;
- status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK);
+ status = write16(state, SIO_CC_PWD_MODE__A,
+ SIO_CC_PWD_MODE_LEVEL_CLOCK);
if (status < 0)
goto error;
status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
if (status < 0)
goto error;
- state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
- status = HI_CfgCommand(state);
+ state->m_hi_cfg_ctrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = hi_cfg_command(state);
error:
if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
return status;
}
@@ -6015,50 +6053,56 @@
static int init_drxk(struct drxk_state *state)
{
int status = 0, n = 0;
- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
- u16 driverVersion;
+ enum drx_power_mode power_mode = DRXK_POWER_DOWN_OFDM;
+ u16 driver_version;
dprintk(1, "\n");
- if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
+ if ((state->m_drxk_state == DRXK_UNINITIALIZED)) {
drxk_i2c_lock(state);
- status = PowerUpDevice(state);
+ status = power_up_device(state);
if (status < 0)
goto error;
- status = DRXX_Open(state);
+ status = drxx_open(state);
if (status < 0)
goto error;
/* Soft reset of OFDM-, sys- and osc-clockdomain */
- status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M);
+ status = write16(state, SIO_CC_SOFT_RST__A,
+ SIO_CC_SOFT_RST_OFDM__M
+ | SIO_CC_SOFT_RST_SYS__M
+ | SIO_CC_SOFT_RST_OSC__M);
if (status < 0)
goto error;
status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
if (status < 0)
goto error;
- /* TODO is this needed, if yes how much delay in worst case scenario */
- msleep(1);
- state->m_DRXK_A3_PATCH_CODE = true;
- status = GetDeviceCapabilities(state);
+ /*
+ * TODO is this needed? If yes, how much delay in
+ * worst case scenario
+ */
+ usleep_range(1000, 2000);
+ state->m_drxk_a3_patch_code = true;
+ status = get_device_capabilities(state);
if (status < 0)
goto error;
/* Bridge delay, uses oscilator clock */
/* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */
/* SDA brdige delay */
- state->m_HICfgBridgeDelay =
- (u16) ((state->m_oscClockFreq / 1000) *
+ state->m_hi_cfg_bridge_delay =
+ (u16) ((state->m_osc_clock_freq / 1000) *
HI_I2C_BRIDGE_DELAY) / 1000;
/* Clipping */
- if (state->m_HICfgBridgeDelay >
+ if (state->m_hi_cfg_bridge_delay >
SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) {
- state->m_HICfgBridgeDelay =
+ state->m_hi_cfg_bridge_delay =
SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
}
/* SCL bridge delay, same as SDA for now */
- state->m_HICfgBridgeDelay +=
- state->m_HICfgBridgeDelay <<
+ state->m_hi_cfg_bridge_delay +=
+ state->m_hi_cfg_bridge_delay <<
SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B;
- status = InitHI(state);
+ status = init_hi(state);
if (status < 0)
goto error;
/* disable various processes */
@@ -6067,13 +6111,14 @@
&& !(state->m_DRXK_A2_ROM_CODE))
#endif
{
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ status = write16(state, SCU_RAM_GPIO__A,
+ SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
}
/* disable MPEG port */
- status = MPEGTSDisable(state);
+ status = mpegts_disable(state);
if (status < 0)
goto error;
@@ -6086,27 +6131,30 @@
goto error;
/* enable token-ring bus through OFDM block for possible ucode upload */
- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A,
+ SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
if (status < 0)
goto error;
/* include boot loader section */
- status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE);
+ status = write16(state, SIO_BL_COMM_EXEC__A,
+ SIO_BL_COMM_EXEC_ACTIVE);
if (status < 0)
goto error;
- status = BLChainCmd(state, 0, 6, 100);
+ status = bl_chain_cmd(state, 0, 6, 100);
if (status < 0)
goto error;
if (state->fw) {
- status = DownloadMicrocode(state, state->fw->data,
+ status = download_microcode(state, state->fw->data,
state->fw->size);
if (status < 0)
goto error;
}
/* disable token-ring bus through OFDM block for possible ucode upload */
- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A,
+ SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
if (status < 0)
goto error;
@@ -6114,14 +6162,14 @@
status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
if (status < 0)
goto error;
- status = DRXX_Open(state);
+ status = drxx_open(state);
if (status < 0)
goto error;
/* added for test */
msleep(30);
- powerMode = DRXK_POWER_DOWN_OFDM;
- status = CtrlPowerMode(state, &powerMode);
+ power_mode = DRXK_POWER_DOWN_OFDM;
+ status = ctrl_power_mode(state, &power_mode);
if (status < 0)
goto error;
@@ -6131,33 +6179,38 @@
Not using SCU command interface for SCU register access since no
microcode may be present.
*/
- driverVersion =
+ driver_version =
(((DRXK_VERSION_MAJOR / 100) % 10) << 12) +
(((DRXK_VERSION_MAJOR / 10) % 10) << 8) +
((DRXK_VERSION_MAJOR % 10) << 4) +
(DRXK_VERSION_MINOR % 10);
- status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion);
+ status = write16(state, SCU_RAM_DRIVER_VER_HI__A,
+ driver_version);
if (status < 0)
goto error;
- driverVersion =
+ driver_version =
(((DRXK_VERSION_PATCH / 1000) % 10) << 12) +
(((DRXK_VERSION_PATCH / 100) % 10) << 8) +
(((DRXK_VERSION_PATCH / 10) % 10) << 4) +
(DRXK_VERSION_PATCH % 10);
- status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion);
+ status = write16(state, SCU_RAM_DRIVER_VER_LO__A,
+ driver_version);
if (status < 0)
goto error;
- printk(KERN_INFO "DRXK driver version %d.%d.%d\n",
+ pr_info("DRXK driver version %d.%d.%d\n",
DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR,
DRXK_VERSION_PATCH);
- /* Dirty fix of default values for ROM/PATCH microcode
- Dirty because this fix makes it impossible to setup suitable values
- before calling DRX_Open. This solution requires changes to RF AGC speed
- to be done via the CTRL function after calling DRX_Open */
+ /*
+ * Dirty fix of default values for ROM/PATCH microcode
+ * Dirty because this fix makes it impossible to setup
+ * suitable values before calling DRX_Open. This solution
+ * requires changes to RF AGC speed to be done via the CTRL
+ * function after calling DRX_Open
+ */
- /* m_dvbtRfAgcCfg.speed = 3; */
+ /* m_dvbt_rf_agc_cfg.speed = 3; */
/* Reset driver debug flags to 0 */
status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0);
@@ -6170,42 +6223,42 @@
if (status < 0)
goto error;
/* MPEGTS functions are still the same */
- status = MPEGTSDtoInit(state);
+ status = mpegts_dto_init(state);
if (status < 0)
goto error;
- status = MPEGTSStop(state);
+ status = mpegts_stop(state);
if (status < 0)
goto error;
- status = MPEGTSConfigurePolarity(state);
+ status = mpegts_configure_polarity(state);
if (status < 0)
goto error;
- status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput);
+ status = mpegts_configure_pins(state, state->m_enable_mpeg_output);
if (status < 0)
goto error;
/* added: configure GPIO */
- status = WriteGPIO(state);
+ status = write_gpio(state);
if (status < 0)
goto error;
- state->m_DrxkState = DRXK_STOPPED;
+ state->m_drxk_state = DRXK_STOPPED;
- if (state->m_bPowerDown) {
- status = PowerDownDevice(state);
+ if (state->m_b_power_down) {
+ status = power_down_device(state);
if (status < 0)
goto error;
- state->m_DrxkState = DRXK_POWERED_DOWN;
+ state->m_drxk_state = DRXK_POWERED_DOWN;
} else
- state->m_DrxkState = DRXK_STOPPED;
+ state->m_drxk_state = DRXK_STOPPED;
/* Initialize the supported delivery systems */
n = 0;
- if (state->m_hasDVBC) {
+ if (state->m_has_dvbc) {
state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
strlcat(state->frontend.ops.info.name, " DVB-C",
sizeof(state->frontend.ops.info.name));
}
- if (state->m_hasDVBT) {
+ if (state->m_has_dvbt) {
state->frontend.ops.delsys[n++] = SYS_DVBT;
strlcat(state->frontend.ops.info.name, " DVB-T",
sizeof(state->frontend.ops.info.name));
@@ -6214,9 +6267,9 @@
}
error:
if (status < 0) {
- state->m_DrxkState = DRXK_NO_DEV;
+ state->m_drxk_state = DRXK_NO_DEV;
drxk_i2c_unlock(state);
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ pr_err("Error %d on %s\n", status, __func__);
}
return status;
@@ -6229,11 +6282,9 @@
dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded");
if (!fw) {
- printk(KERN_ERR
- "drxk: Could not load firmware file %s.\n",
+ pr_err("Could not load firmware file %s.\n",
state->microcode_name);
- printk(KERN_INFO
- "drxk: Copy %s to your hotplug directory!\n",
+ pr_info("Copy %s to your hotplug directory!\n",
state->microcode_name);
state->microcode_name = NULL;
@@ -6270,12 +6321,12 @@
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return 0;
- ShutDown(state);
+ shut_down(state);
return 0;
}
@@ -6285,7 +6336,7 @@
dprintk(1, ": %s\n", enable ? "enable" : "disable");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
return ConfigureI2CBridge(state, enable ? true : false);
@@ -6300,15 +6351,14 @@
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return -EAGAIN;
if (!fe->ops.tuner_ops.get_if_frequency) {
- printk(KERN_ERR
- "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
+ pr_err("Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
return -EINVAL;
}
@@ -6323,22 +6373,23 @@
state->props = *p;
if (old_delsys != delsys) {
- ShutDown(state);
+ shut_down(state);
switch (delsys) {
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
- if (!state->m_hasDVBC)
+ if (!state->m_has_dvbc)
return -EINVAL;
- state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ? true : false;
+ state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ?
+ true : false;
if (state->m_itut_annex_c)
- SetOperationMode(state, OM_QAM_ITU_C);
+ setoperation_mode(state, OM_QAM_ITU_C);
else
- SetOperationMode(state, OM_QAM_ITU_A);
+ setoperation_mode(state, OM_QAM_ITU_A);
break;
case SYS_DVBT:
- if (!state->m_hasDVBT)
+ if (!state->m_has_dvbt)
return -EINVAL;
- SetOperationMode(state, OM_DVBT);
+ setoperation_mode(state, OM_DVBT);
break;
default:
return -EINVAL;
@@ -6346,7 +6397,7 @@
}
fe->ops.tuner_ops.get_if_frequency(fe, &IF);
- Start(state, 0, IF);
+ start(state, 0, IF);
/* After set_frontend, stats aren't avaliable */
p->strength.stat[0].scale = FE_SCALE_RELATIVE;
@@ -6366,31 +6417,31 @@
static int get_strength(struct drxk_state *state, u64 *strength)
{
int status;
- struct SCfgAgc rfAgc, ifAgc;
- u32 totalGain = 0;
+ struct s_cfg_agc rf_agc, if_agc;
+ u32 total_gain = 0;
u32 atten = 0;
- u32 agcRange = 0;
+ u32 agc_range = 0;
u16 scu_lvl = 0;
u16 scu_coc = 0;
/* FIXME: those are part of the tuner presets */
- u16 tunerRfGain = 50; /* Default value on az6007 driver */
- u16 tunerIfGain = 40; /* Default value on az6007 driver */
+ u16 tuner_rf_gain = 50; /* Default value on az6007 driver */
+ u16 tuner_if_gain = 40; /* Default value on az6007 driver */
*strength = 0;
- if (IsDVBT(state)) {
- rfAgc = state->m_dvbtRfAgcCfg;
- ifAgc = state->m_dvbtIfAgcCfg;
- } else if (IsQAM(state)) {
- rfAgc = state->m_qamRfAgcCfg;
- ifAgc = state->m_qamIfAgcCfg;
+ if (is_dvbt(state)) {
+ rf_agc = state->m_dvbt_rf_agc_cfg;
+ if_agc = state->m_dvbt_if_agc_cfg;
+ } else if (is_qam(state)) {
+ rf_agc = state->m_qam_rf_agc_cfg;
+ if_agc = state->m_qam_if_agc_cfg;
} else {
- rfAgc = state->m_atvRfAgcCfg;
- ifAgc = state->m_atvIfAgcCfg;
+ rf_agc = state->m_atv_rf_agc_cfg;
+ if_agc = state->m_atv_if_agc_cfg;
}
- if (rfAgc.ctrlMode == DRXK_AGC_CTRL_AUTO) {
- /* SCU outputLevel */
+ if (rf_agc.ctrl_mode == DRXK_AGC_CTRL_AUTO) {
+ /* SCU output_level */
status = read16(state, SCU_RAM_AGC_RF_IACCU_HI__A, &scu_lvl);
if (status < 0)
return status;
@@ -6401,54 +6452,54 @@
return status;
if (((u32) scu_lvl + (u32) scu_coc) < 0xffff)
- rfAgc.outputLevel = scu_lvl + scu_coc;
+ rf_agc.output_level = scu_lvl + scu_coc;
else
- rfAgc.outputLevel = 0xffff;
+ rf_agc.output_level = 0xffff;
/* Take RF gain into account */
- totalGain += tunerRfGain;
+ total_gain += tuner_rf_gain;
/* clip output value */
- if (rfAgc.outputLevel < rfAgc.minOutputLevel)
- rfAgc.outputLevel = rfAgc.minOutputLevel;
- if (rfAgc.outputLevel > rfAgc.maxOutputLevel)
- rfAgc.outputLevel = rfAgc.maxOutputLevel;
+ if (rf_agc.output_level < rf_agc.min_output_level)
+ rf_agc.output_level = rf_agc.min_output_level;
+ if (rf_agc.output_level > rf_agc.max_output_level)
+ rf_agc.output_level = rf_agc.max_output_level;
- agcRange = (u32) (rfAgc.maxOutputLevel - rfAgc.minOutputLevel);
- if (agcRange > 0) {
+ agc_range = (u32) (rf_agc.max_output_level - rf_agc.min_output_level);
+ if (agc_range > 0) {
atten += 100UL *
- ((u32)(tunerRfGain)) *
- ((u32)(rfAgc.outputLevel - rfAgc.minOutputLevel))
- / agcRange;
+ ((u32)(tuner_rf_gain)) *
+ ((u32)(rf_agc.output_level - rf_agc.min_output_level))
+ / agc_range;
}
}
- if (ifAgc.ctrlMode == DRXK_AGC_CTRL_AUTO) {
+ if (if_agc.ctrl_mode == DRXK_AGC_CTRL_AUTO) {
status = read16(state, SCU_RAM_AGC_IF_IACCU_HI__A,
- &ifAgc.outputLevel);
+ &if_agc.output_level);
if (status < 0)
return status;
status = read16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A,
- &ifAgc.top);
+ &if_agc.top);
if (status < 0)
return status;
/* Take IF gain into account */
- totalGain += (u32) tunerIfGain;
+ total_gain += (u32) tuner_if_gain;
/* clip output value */
- if (ifAgc.outputLevel < ifAgc.minOutputLevel)
- ifAgc.outputLevel = ifAgc.minOutputLevel;
- if (ifAgc.outputLevel > ifAgc.maxOutputLevel)
- ifAgc.outputLevel = ifAgc.maxOutputLevel;
+ if (if_agc.output_level < if_agc.min_output_level)
+ if_agc.output_level = if_agc.min_output_level;
+ if (if_agc.output_level > if_agc.max_output_level)
+ if_agc.output_level = if_agc.max_output_level;
- agcRange = (u32) (ifAgc.maxOutputLevel - ifAgc.minOutputLevel);
- if (agcRange > 0) {
+ agc_range = (u32)(if_agc.max_output_level - if_agc.min_output_level);
+ if (agc_range > 0) {
atten += 100UL *
- ((u32)(tunerIfGain)) *
- ((u32)(ifAgc.outputLevel - ifAgc.minOutputLevel))
- / agcRange;
+ ((u32)(tuner_if_gain)) *
+ ((u32)(if_agc.output_level - if_agc.min_output_level))
+ / agc_range;
}
}
@@ -6456,8 +6507,8 @@
* Convert to 0..65535 scale.
* If it can't be measured (AGC is disabled), just show 100%.
*/
- if (totalGain > 0)
- *strength = (65535UL * atten / totalGain / 100);
+ if (total_gain > 0)
+ *strength = (65535UL * atten / total_gain / 100);
else
*strength = 65535;
@@ -6480,14 +6531,14 @@
u32 pkt_error_count;
s32 cnr;
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return -EAGAIN;
/* get status */
state->fe_status = 0;
- GetLockStatus(state, &stat);
+ get_lock_status(state, &stat);
if (stat == MPEG_LOCK)
state->fe_status |= 0x1f;
if (stat == FEC_LOCK)
@@ -6503,7 +6554,7 @@
if (stat >= DEMOD_LOCK) {
- GetSignalToNoise(state, &cnr);
+ get_signal_to_noise(state, &cnr);
c->cnr.stat[0].svalue = cnr * 100;
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
} else {
@@ -6524,9 +6575,11 @@
/* BER measurement is valid if at least FEC lock is achieved */
- /* OFDM_EC_VD_REQ_SMB_CNT__A and/or OFDM_EC_VD_REQ_BIT_CNT can be written
- to set nr of symbols or bits over which
- to measure EC_VD_REG_ERR_BIT_CNT__A . See CtrlSetCfg(). */
+ /*
+ * OFDM_EC_VD_REQ_SMB_CNT__A and/or OFDM_EC_VD_REQ_BIT_CNT can be
+ * written to set nr of symbols or bits over which to measure
+ * EC_VD_REG_ERR_BIT_CNT__A . See CtrlSetCfg().
+ */
/* Read registers for post/preViterbi BER calculation */
status = read16(state, OFDM_EC_VD_ERR_BIT_CNT__A, ®16);
@@ -6610,9 +6663,9 @@
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return -EAGAIN;
*strength = c->strength.stat[0].uvalue;
@@ -6626,12 +6679,12 @@
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return -EAGAIN;
- GetSignalToNoise(state, &snr2);
+ get_signal_to_noise(state, &snr2);
/* No negative SNR, clip to zero */
if (snr2 < 0)
@@ -6647,27 +6700,27 @@
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return -EAGAIN;
- DVBTQAMGetAccPktErr(state, &err);
+ dvbtqam_get_acc_pkt_err(state, &err);
*ucblocks = (u32) err;
return 0;
}
-static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
- *sets)
+static int drxk_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *sets)
{
struct drxk_state *state = fe->demodulator_priv;
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
dprintk(1, "\n");
- if (state->m_DrxkState == DRXK_NO_DEV)
+ if (state->m_drxk_state == DRXK_NO_DEV)
return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ if (state->m_drxk_state == DRXK_UNINITIALIZED)
return -EAGAIN;
switch (p->delivery_system) {
@@ -6737,36 +6790,36 @@
state->no_i2c_bridge = config->no_i2c_bridge;
state->antenna_gpio = config->antenna_gpio;
state->antenna_dvbt = config->antenna_dvbt;
- state->m_ChunkSize = config->chunk_size;
+ state->m_chunk_size = config->chunk_size;
state->enable_merr_cfg = config->enable_merr_cfg;
if (config->dynamic_clk) {
- state->m_DVBTStaticCLK = 0;
- state->m_DVBCStaticCLK = 0;
+ state->m_dvbt_static_clk = 0;
+ state->m_dvbc_static_clk = 0;
} else {
- state->m_DVBTStaticCLK = 1;
- state->m_DVBCStaticCLK = 1;
+ state->m_dvbt_static_clk = 1;
+ state->m_dvbc_static_clk = 1;
}
if (config->mpeg_out_clk_strength)
- state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07;
+ state->m_ts_clockk_strength = config->mpeg_out_clk_strength & 0x07;
else
- state->m_TSClockkStrength = 0x06;
+ state->m_ts_clockk_strength = 0x06;
if (config->parallel_ts)
- state->m_enableParallel = true;
+ state->m_enable_parallel = true;
else
- state->m_enableParallel = false;
+ state->m_enable_parallel = false;
/* NOTE: as more UIO bits will be used, add them to the mask */
- state->UIO_mask = config->antenna_gpio;
+ state->uio_mask = config->antenna_gpio;
/* Default gpio to DVB-C */
if (!state->antenna_dvbt && state->antenna_gpio)
- state->m_GPIO |= state->antenna_gpio;
+ state->m_gpio |= state->antenna_gpio;
else
- state->m_GPIO &= ~state->antenna_gpio;
+ state->m_gpio &= ~state->antenna_gpio;
mutex_init(&state->mutex);
@@ -6792,8 +6845,7 @@
GFP_KERNEL,
state, load_firmware_cb);
if (status < 0) {
- printk(KERN_ERR
- "drxk: failed to request a firmware\n");
+ pr_err("failed to request a firmware\n");
return NULL;
}
}
@@ -6821,11 +6873,11 @@
p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
- printk(KERN_INFO "drxk: frontend initialized.\n");
+ pr_info("frontend initialized.\n");
return &state->frontend;
error:
- printk(KERN_ERR "drxk: not found\n");
+ pr_err("not found\n");
kfree(state);
return NULL;
}
diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h
index b8424f1..bae9c71 100644
--- a/drivers/media/dvb-frontends/drxk_hard.h
+++ b/drivers/media/dvb-frontends/drxk_hard.h
@@ -46,7 +46,7 @@
#define IQM_RC_ADJ_SEL_B_QAM 0x1
#define IQM_RC_ADJ_SEL_B_VSB 0x2
-enum OperationMode {
+enum operation_mode {
OM_NONE,
OM_QAM_ITU_A,
OM_QAM_ITU_B,
@@ -54,7 +54,7 @@
OM_DVBT
};
-enum DRXPowerMode {
+enum drx_power_mode {
DRX_POWER_UP = 0,
DRX_POWER_MODE_1,
DRX_POWER_MODE_2,
@@ -77,24 +77,29 @@
};
-/** /brief Intermediate power mode for DRXK, power down OFDM clock domain */
+/* Intermediate power mode for DRXK, power down OFDM clock domain */
#ifndef DRXK_POWER_DOWN_OFDM
#define DRXK_POWER_DOWN_OFDM DRX_POWER_MODE_1
#endif
-/** /brief Intermediate power mode for DRXK, power down core (sysclk) */
+/* Intermediate power mode for DRXK, power down core (sysclk) */
#ifndef DRXK_POWER_DOWN_CORE
#define DRXK_POWER_DOWN_CORE DRX_POWER_MODE_9
#endif
-/** /brief Intermediate power mode for DRXK, power down pll (only osc runs) */
+/* Intermediate power mode for DRXK, power down pll (only osc runs) */
#ifndef DRXK_POWER_DOWN_PLL
#define DRXK_POWER_DOWN_PLL DRX_POWER_MODE_10
#endif
-enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF };
-enum EDrxkState {
+enum agc_ctrl_mode {
+ DRXK_AGC_CTRL_AUTO = 0,
+ DRXK_AGC_CTRL_USER,
+ DRXK_AGC_CTRL_OFF
+};
+
+enum e_drxk_state {
DRXK_UNINITIALIZED = 0,
DRXK_STOPPED,
DRXK_DTV_STARTED,
@@ -103,7 +108,7 @@
DRXK_NO_DEV /* If drxk init failed */
};
-enum EDrxkCoefArrayIndex {
+enum e_drxk_coef_array_index {
DRXK_COEF_IDX_MN = 0,
DRXK_COEF_IDX_FM ,
DRXK_COEF_IDX_L ,
@@ -113,13 +118,13 @@
DRXK_COEF_IDX_I ,
DRXK_COEF_IDX_MAX
};
-enum EDrxkSifAttenuation {
+enum e_drxk_sif_attenuation {
DRXK_SIF_ATTENUATION_0DB,
DRXK_SIF_ATTENUATION_3DB,
DRXK_SIF_ATTENUATION_6DB,
DRXK_SIF_ATTENUATION_9DB
};
-enum EDrxkConstellation {
+enum e_drxk_constellation {
DRX_CONSTELLATION_BPSK = 0,
DRX_CONSTELLATION_QPSK,
DRX_CONSTELLATION_PSK8,
@@ -133,7 +138,7 @@
DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN,
DRX_CONSTELLATION_AUTO = DRX_AUTO
};
-enum EDrxkInterleaveMode {
+enum e_drxk_interleave_mode {
DRXK_QAM_I12_J17 = 16,
DRXK_QAM_I_UNKNOWN = DRX_UNKNOWN
};
@@ -144,14 +149,14 @@
DRXK_SPIN_UNKNOWN
};
-enum DRXKCfgDvbtSqiSpeed {
+enum drxk_cfg_dvbt_sqi_speed {
DRXK_DVBT_SQI_SPEED_FAST = 0,
DRXK_DVBT_SQI_SPEED_MEDIUM,
DRXK_DVBT_SQI_SPEED_SLOW,
DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN
} ;
-enum DRXFftmode_t {
+enum drx_fftmode_t {
DRX_FFTMODE_2K = 0,
DRX_FFTMODE_4K,
DRX_FFTMODE_8K,
@@ -159,47 +164,47 @@
DRX_FFTMODE_AUTO = DRX_AUTO
};
-enum DRXMPEGStrWidth_t {
+enum drxmpeg_str_width_t {
DRX_MPEG_STR_WIDTH_1,
DRX_MPEG_STR_WIDTH_8
};
-enum DRXQamLockRange_t {
+enum drx_qam_lock_range_t {
DRX_QAM_LOCKRANGE_NORMAL,
DRX_QAM_LOCKRANGE_EXTENDED
};
-struct DRXKCfgDvbtEchoThres_t {
+struct drxk_cfg_dvbt_echo_thres_t {
u16 threshold;
- enum DRXFftmode_t fftMode;
+ enum drx_fftmode_t fft_mode;
} ;
-struct SCfgAgc {
- enum AGC_CTRL_MODE ctrlMode; /* off, user, auto */
- u16 outputLevel; /* range dependent on AGC */
- u16 minOutputLevel; /* range dependent on AGC */
- u16 maxOutputLevel; /* range dependent on AGC */
+struct s_cfg_agc {
+ enum agc_ctrl_mode ctrl_mode; /* off, user, auto */
+ u16 output_level; /* range dependent on AGC */
+ u16 min_output_level; /* range dependent on AGC */
+ u16 max_output_level; /* range dependent on AGC */
u16 speed; /* range dependent on AGC */
u16 top; /* rf-agc take over point */
- u16 cutOffCurrent; /* rf-agc is accelerated if output current
+ u16 cut_off_current; /* rf-agc is accelerated if output current
is below cut-off current */
- u16 IngainTgtMax;
- u16 FastClipCtrlDelay;
+ u16 ingain_tgt_max;
+ u16 fast_clip_ctrl_delay;
};
-struct SCfgPreSaw {
+struct s_cfg_pre_saw {
u16 reference; /* pre SAW reference value, range 0 .. 31 */
- bool usePreSaw; /* TRUE algorithms must use pre SAW sense */
+ bool use_pre_saw; /* TRUE algorithms must use pre SAW sense */
};
-struct DRXKOfdmScCmd_t {
- u16 cmd; /**< Command number */
- u16 subcmd; /**< Sub-command parameter*/
- u16 param0; /**< General purpous param */
- u16 param1; /**< General purpous param */
- u16 param2; /**< General purpous param */
- u16 param3; /**< General purpous param */
- u16 param4; /**< General purpous param */
+struct drxk_ofdm_sc_cmd_t {
+ u16 cmd; /* Command number */
+ u16 subcmd; /* Sub-command parameter*/
+ u16 param0; /* General purpous param */
+ u16 param1; /* General purpous param */
+ u16 param2; /* General purpous param */
+ u16 param3; /* General purpous param */
+ u16 param4; /* General purpous param */
};
struct drxk_state {
@@ -213,121 +218,121 @@
struct mutex mutex;
- u32 m_Instance; /**< Channel 1,2,3 or 4 */
+ u32 m_instance; /* Channel 1,2,3 or 4 */
- int m_ChunkSize;
- u8 Chunk[256];
+ int m_chunk_size;
+ u8 chunk[256];
- bool m_hasLNA;
- bool m_hasDVBT;
- bool m_hasDVBC;
- bool m_hasAudio;
- bool m_hasATV;
- bool m_hasOOB;
- bool m_hasSAWSW; /**< TRUE if mat_tx is available */
- bool m_hasGPIO1; /**< TRUE if mat_rx is available */
- bool m_hasGPIO2; /**< TRUE if GPIO is available */
- bool m_hasIRQN; /**< TRUE if IRQN is available */
- u16 m_oscClockFreq;
- u16 m_HICfgTimingDiv;
- u16 m_HICfgBridgeDelay;
- u16 m_HICfgWakeUpKey;
- u16 m_HICfgTimeout;
- u16 m_HICfgCtrl;
- s32 m_sysClockFreq; /**< system clock frequency in kHz */
+ bool m_has_lna;
+ bool m_has_dvbt;
+ bool m_has_dvbc;
+ bool m_has_audio;
+ bool m_has_atv;
+ bool m_has_oob;
+ bool m_has_sawsw; /* TRUE if mat_tx is available */
+ bool m_has_gpio1; /* TRUE if mat_rx is available */
+ bool m_has_gpio2; /* TRUE if GPIO is available */
+ bool m_has_irqn; /* TRUE if IRQN is available */
+ u16 m_osc_clock_freq;
+ u16 m_hi_cfg_timing_div;
+ u16 m_hi_cfg_bridge_delay;
+ u16 m_hi_cfg_wake_up_key;
+ u16 m_hi_cfg_timeout;
+ u16 m_hi_cfg_ctrl;
+ s32 m_sys_clock_freq; /* system clock frequency in kHz */
- enum EDrxkState m_DrxkState; /**< State of Drxk (init,stopped,started) */
- enum OperationMode m_OperationMode; /**< digital standards */
- struct SCfgAgc m_vsbRfAgcCfg; /**< settings for VSB RF-AGC */
- struct SCfgAgc m_vsbIfAgcCfg; /**< settings for VSB IF-AGC */
- u16 m_vsbPgaCfg; /**< settings for VSB PGA */
- struct SCfgPreSaw m_vsbPreSawCfg; /**< settings for pre SAW sense */
- s32 m_Quality83percent; /**< MER level (*0.1 dB) for 83% quality indication */
- s32 m_Quality93percent; /**< MER level (*0.1 dB) for 93% quality indication */
- bool m_smartAntInverted;
- bool m_bDebugEnableBridge;
- bool m_bPDownOpenBridge; /**< only open DRXK bridge before power-down once it has been accessed */
- bool m_bPowerDown; /**< Power down when not used */
+ enum e_drxk_state m_drxk_state; /* State of Drxk (init,stopped,started) */
+ enum operation_mode m_operation_mode; /* digital standards */
+ struct s_cfg_agc m_vsb_rf_agc_cfg; /* settings for VSB RF-AGC */
+ struct s_cfg_agc m_vsb_if_agc_cfg; /* settings for VSB IF-AGC */
+ u16 m_vsb_pga_cfg; /* settings for VSB PGA */
+ struct s_cfg_pre_saw m_vsb_pre_saw_cfg; /* settings for pre SAW sense */
+ s32 m_Quality83percent; /* MER level (*0.1 dB) for 83% quality indication */
+ s32 m_Quality93percent; /* MER level (*0.1 dB) for 93% quality indication */
+ bool m_smart_ant_inverted;
+ bool m_b_debug_enable_bridge;
+ bool m_b_p_down_open_bridge; /* only open DRXK bridge before power-down once it has been accessed */
+ bool m_b_power_down; /* Power down when not used */
- u32 m_IqmFsRateOfs; /**< frequency shift as written to DRXK register (28bit fixpoint) */
+ u32 m_iqm_fs_rate_ofs; /* frequency shift as written to DRXK register (28bit fixpoint) */
- bool m_enableMPEGOutput; /**< If TRUE, enable MPEG output */
- bool m_insertRSByte; /**< If TRUE, insert RS byte */
- bool m_enableParallel; /**< If TRUE, parallel out otherwise serial */
- bool m_invertDATA; /**< If TRUE, invert DATA signals */
- bool m_invertERR; /**< If TRUE, invert ERR signal */
- bool m_invertSTR; /**< If TRUE, invert STR signals */
- bool m_invertVAL; /**< If TRUE, invert VAL signals */
- bool m_invertCLK; /**< If TRUE, invert CLK signals */
- bool m_DVBCStaticCLK;
- bool m_DVBTStaticCLK; /**< If TRUE, static MPEG clockrate will
+ bool m_enable_mpeg_output; /* If TRUE, enable MPEG output */
+ bool m_insert_rs_byte; /* If TRUE, insert RS byte */
+ bool m_enable_parallel; /* If TRUE, parallel out otherwise serial */
+ bool m_invert_data; /* If TRUE, invert DATA signals */
+ bool m_invert_err; /* If TRUE, invert ERR signal */
+ bool m_invert_str; /* If TRUE, invert STR signals */
+ bool m_invert_val; /* If TRUE, invert VAL signals */
+ bool m_invert_clk; /* If TRUE, invert CLK signals */
+ bool m_dvbc_static_clk;
+ bool m_dvbt_static_clk; /* If TRUE, static MPEG clockrate will
be used, otherwise clockrate will
adapt to the bitrate of the TS */
- u32 m_DVBTBitrate;
- u32 m_DVBCBitrate;
+ u32 m_dvbt_bitrate;
+ u32 m_dvbc_bitrate;
- u8 m_TSDataStrength;
- u8 m_TSClockkStrength;
+ u8 m_ts_data_strength;
+ u8 m_ts_clockk_strength;
bool m_itut_annex_c; /* If true, uses ITU-T DVB-C Annex C, instead of Annex A */
- enum DRXMPEGStrWidth_t m_widthSTR; /**< MPEG start width */
- u32 m_mpegTsStaticBitrate; /**< Maximum bitrate in b/s in case
+ enum drxmpeg_str_width_t m_width_str; /* MPEG start width */
+ u32 m_mpeg_ts_static_bitrate; /* Maximum bitrate in b/s in case
static clockrate is selected */
- /* LARGE_INTEGER m_StartTime; */ /**< Contains the time of the last demod start */
- s32 m_MpegLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */
- s32 m_DemodLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */
+ /* LARGE_INTEGER m_startTime; */ /* Contains the time of the last demod start */
+ s32 m_mpeg_lock_time_out; /* WaitForLockStatus Timeout (counts from start time) */
+ s32 m_demod_lock_time_out; /* WaitForLockStatus Timeout (counts from start time) */
- bool m_disableTEIhandling;
+ bool m_disable_te_ihandling;
- bool m_RfAgcPol;
- bool m_IfAgcPol;
+ bool m_rf_agc_pol;
+ bool m_if_agc_pol;
- struct SCfgAgc m_atvRfAgcCfg; /**< settings for ATV RF-AGC */
- struct SCfgAgc m_atvIfAgcCfg; /**< settings for ATV IF-AGC */
- struct SCfgPreSaw m_atvPreSawCfg; /**< settings for ATV pre SAW sense */
- bool m_phaseCorrectionBypass;
- s16 m_atvTopVidPeak;
- u16 m_atvTopNoiseTh;
- enum EDrxkSifAttenuation m_sifAttenuation;
- bool m_enableCVBSOutput;
- bool m_enableSIFOutput;
- bool m_bMirrorFreqSpect;
- enum EDrxkConstellation m_Constellation; /**< Constellation type of the channel */
- u32 m_CurrSymbolRate; /**< Current QAM symbol rate */
- struct SCfgAgc m_qamRfAgcCfg; /**< settings for QAM RF-AGC */
- struct SCfgAgc m_qamIfAgcCfg; /**< settings for QAM IF-AGC */
- u16 m_qamPgaCfg; /**< settings for QAM PGA */
- struct SCfgPreSaw m_qamPreSawCfg; /**< settings for QAM pre SAW sense */
- enum EDrxkInterleaveMode m_qamInterleaveMode; /**< QAM Interleave mode */
- u16 m_fecRsPlen;
- u16 m_fecRsPrescale;
+ struct s_cfg_agc m_atv_rf_agc_cfg; /* settings for ATV RF-AGC */
+ struct s_cfg_agc m_atv_if_agc_cfg; /* settings for ATV IF-AGC */
+ struct s_cfg_pre_saw m_atv_pre_saw_cfg; /* settings for ATV pre SAW sense */
+ bool m_phase_correction_bypass;
+ s16 m_atv_top_vid_peak;
+ u16 m_atv_top_noise_th;
+ enum e_drxk_sif_attenuation m_sif_attenuation;
+ bool m_enable_cvbs_output;
+ bool m_enable_sif_output;
+ bool m_b_mirror_freq_spect;
+ enum e_drxk_constellation m_constellation; /* constellation type of the channel */
+ u32 m_curr_symbol_rate; /* Current QAM symbol rate */
+ struct s_cfg_agc m_qam_rf_agc_cfg; /* settings for QAM RF-AGC */
+ struct s_cfg_agc m_qam_if_agc_cfg; /* settings for QAM IF-AGC */
+ u16 m_qam_pga_cfg; /* settings for QAM PGA */
+ struct s_cfg_pre_saw m_qam_pre_saw_cfg; /* settings for QAM pre SAW sense */
+ enum e_drxk_interleave_mode m_qam_interleave_mode; /* QAM Interleave mode */
+ u16 m_fec_rs_plen;
+ u16 m_fec_rs_prescale;
- enum DRXKCfgDvbtSqiSpeed m_sqiSpeed;
+ enum drxk_cfg_dvbt_sqi_speed m_sqi_speed;
- u16 m_GPIO;
- u16 m_GPIOCfg;
+ u16 m_gpio;
+ u16 m_gpio_cfg;
- struct SCfgAgc m_dvbtRfAgcCfg; /**< settings for QAM RF-AGC */
- struct SCfgAgc m_dvbtIfAgcCfg; /**< settings for QAM IF-AGC */
- struct SCfgPreSaw m_dvbtPreSawCfg; /**< settings for QAM pre SAW sense */
+ struct s_cfg_agc m_dvbt_rf_agc_cfg; /* settings for QAM RF-AGC */
+ struct s_cfg_agc m_dvbt_if_agc_cfg; /* settings for QAM IF-AGC */
+ struct s_cfg_pre_saw m_dvbt_pre_saw_cfg; /* settings for QAM pre SAW sense */
- u16 m_agcFastClipCtrlDelay;
- bool m_adcCompPassed;
+ u16 m_agcfast_clip_ctrl_delay;
+ bool m_adc_comp_passed;
u16 m_adcCompCoef[64];
- u16 m_adcState;
+ u16 m_adc_state;
u8 *m_microcode;
int m_microcode_length;
- bool m_DRXK_A3_ROM_CODE;
- bool m_DRXK_A3_PATCH_CODE;
+ bool m_drxk_a3_rom_code;
+ bool m_drxk_a3_patch_code;
bool m_rfmirror;
- u8 m_deviceSpin;
- u32 m_iqmRcRate;
+ u8 m_device_spin;
+ u32 m_iqm_rc_rate;
- enum DRXPowerMode m_currentPowerMode;
+ enum drx_power_mode m_current_power_mode;
/* when true, avoids other devices to use the I2C bus */
bool drxk_i2c_exclusive_lock;
@@ -337,7 +342,7 @@
* at struct drxk_config.
*/
- u16 UIO_mask; /* Bits used by UIO */
+ u16 uio_mask; /* Bits used by UIO */
bool enable_merr_cfg;
bool single_master;
diff --git a/drivers/media/dvb-frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c
index 117a569..93596e0 100644
--- a/drivers/media/dvb-frontends/stb0899_algo.c
+++ b/drivers/media/dvb-frontends/stb0899_algo.c
@@ -226,8 +226,8 @@
next_loop--;
if (next_loop) {
- STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
- STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq));
stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
}
internal->direction = -internal->direction; /* Change zigzag direction */
@@ -235,7 +235,7 @@
if (internal->status == TIMINGOK) {
stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
- internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]);
dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq);
}
@@ -306,8 +306,8 @@
STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
stb0899_write_reg(state, STB0899_CFD, reg);
- STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
- STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq));
stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
}
}
@@ -317,7 +317,7 @@
if (internal->status == CARRIEROK) {
stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
- internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]);
dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq);
} else {
internal->derot_freq = last_derot_freq;
@@ -412,8 +412,8 @@
STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
stb0899_write_reg(state, STB0899_CFD, reg);
- STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
- STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(internal->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(internal->inversion * derot_freq));
stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
stb0899_check_carrier(state);
@@ -425,7 +425,15 @@
if (internal->status == DATAOK) {
stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
- internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+
+ /* store autodetected IQ swapping as default for DVB-S2 tuning */
+ reg = stb0899_read_reg(state, STB0899_IQSWAP);
+ if (STB0899_GETFIELD(SYM, reg))
+ internal->inversion = IQ_SWAP_ON;
+ else
+ internal->inversion = IQ_SWAP_OFF;
+
+ internal->derot_freq = internal->inversion * MAKEWORD16(cfr[0], cfr[1]);
dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq);
}
@@ -444,7 +452,7 @@
int range_offst, tp_freq;
range_offst = internal->srch_range / 2000;
- tp_freq = internal->freq + (internal->derot_freq * internal->mclk) / 1000;
+ tp_freq = internal->freq - (internal->derot_freq * internal->mclk) / 1000;
if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) {
internal->status = RANGEOK;
@@ -638,7 +646,7 @@
"RANGE OK ! derot freq=%d, mclk=%d",
internal->derot_freq, internal->mclk);
- internal->freq = params->freq + ((internal->derot_freq * internal->mclk) / 1000);
+ internal->freq = params->freq - ((internal->derot_freq * internal->mclk) / 1000);
reg = stb0899_read_reg(state, STB0899_PLPARM);
internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg);
dprintk(state->verbose, FE_DEBUG, 1,
@@ -1373,9 +1381,6 @@
case IQ_SWAP_ON:
STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1);
break;
- case IQ_SWAP_AUTO: /* use last successful search first */
- STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1);
- break;
}
stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
stb0899_dvbs2_reacquire(state);
@@ -1405,41 +1410,39 @@
}
if (internal->status != DVBS2_FEC_LOCK) {
- if (internal->inversion == IQ_SWAP_AUTO) {
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
- iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg);
- /* IQ Spectrum Inversion */
- STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum);
- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
- /* start acquistion process */
- stb0899_dvbs2_reacquire(state);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg);
+ /* IQ Spectrum Inversion */
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
+ /* start acquistion process */
+ stb0899_dvbs2_reacquire(state);
- /* Wait for demod lock (UWP and CSM) */
- internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime);
- if (internal->status == DVBS2_DEMOD_LOCK) {
- i = 0;
- /* Demod Locked, check FEC */
- internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
- /*try thrice for false locks, (UWP and CSM Locked but no FEC) */
- while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
- /* Read the frequency offset*/
- offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+ /* Wait for demod lock (UWP and CSM) */
+ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime);
+ if (internal->status == DVBS2_DEMOD_LOCK) {
+ i = 0;
+ /* Demod Locked, check FEC */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+ /*try thrice for false locks, (UWP and CSM Locked but no FEC) */
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ /* Read the frequency offset*/
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
- /* Set the Nominal frequency to the found frequency offset for the next reacquire*/
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
- STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq);
- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
- stb0899_dvbs2_reacquire(state);
- internal->status = stb0899_dvbs2_get_fec_status(state, searchTime);
- i++;
- }
+ stb0899_dvbs2_reacquire(state);
+ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime);
+ i++;
}
-/*
- if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED)
- pParams->IQLocked = !iqSpectrum;
-*/
}
+/*
+ if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED)
+ pParams->IQLocked = !iqSpectrum;
+*/
}
if (internal->status == DVBS2_FEC_LOCK) {
dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !");
@@ -1487,13 +1490,21 @@
/* Store signal parameters */
offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+ /* sign extend 30 bit value before using it in calculations */
+ if (offsetfreq & (1 << 29))
+ offsetfreq |= -1 << 30;
+
offsetfreq = offsetfreq / ((1 << 30) / 1000);
offsetfreq *= (internal->master_clk / 1000000);
+
+ /* store current inversion for next run */
reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
if (STB0899_GETFIELD(SPECTRUM_INVERT, reg))
- offsetfreq *= -1;
+ internal->inversion = IQ_SWAP_ON;
+ else
+ internal->inversion = IQ_SWAP_OFF;
- internal->freq = internal->freq - offsetfreq;
+ internal->freq = internal->freq + offsetfreq;
internal->srate = stb0899_dvbs2_get_srate(state);
reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
index cc278b3..3dd5714 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.c
+++ b/drivers/media/dvb-frontends/stb0899_drv.c
@@ -1618,19 +1618,18 @@
struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c)
{
struct stb0899_state *state = NULL;
- enum stb0899_inversion inversion;
state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL);
if (state == NULL)
goto error;
- inversion = config->inversion;
state->verbose = &verbose;
state->config = config;
state->i2c = i2c;
state->frontend.ops = stb0899_ops;
state->frontend.demodulator_priv = state;
- state->internal.inversion = inversion;
+ /* use configured inversion as default -- we'll later autodetect inversion */
+ state->internal.inversion = config->inversion;
stb0899_wakeup(&state->frontend);
if (stb0899_get_dev_id(state) == -ENODEV) {
diff --git a/drivers/media/dvb-frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h
index 8d26ff6..139264d 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.h
+++ b/drivers/media/dvb-frontends/stb0899_drv.h
@@ -45,9 +45,8 @@
};
enum stb0899_inversion {
- IQ_SWAP_OFF = 0,
- IQ_SWAP_ON,
- IQ_SWAP_AUTO
+ IQ_SWAP_OFF = +1, /* inversion affects the sign of e. g. */
+ IQ_SWAP_ON = -1, /* the derotator frequency register */
};
#define STB0899_GPIO00 0xf140
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index f981d50..b2cd8ca 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -245,6 +245,15 @@
To compile this driver as a module, choose M here: the
module will be called ks0127.
+config VIDEO_ML86V7667
+ tristate "OKI ML86V7667 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the OKI Semiconductor ML86V7667 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ml86v7667.
+
config VIDEO_SAA7110
tristate "Philips SAA7110 video decoder"
depends on VIDEO_V4L2 && I2C
@@ -425,6 +434,15 @@
help
Video output driver for AKM AK8813 and AK8814 TV encoders
+config VIDEO_THS8200
+ tristate "Texas Instruments THS8200 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments THS8200 video encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ths8200.
+
comment "Camera sensor devices"
config VIDEO_APTINA_PLL
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 720f42d..dc20653 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -34,6 +34,7 @@
obj-$(CONFIG_VIDEO_BT866) += bt866.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
obj-$(CONFIG_VIDEO_THS7303) += ths7303.o
+obj-$(CONFIG_VIDEO_THS8200) += ths8200.o
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
@@ -70,3 +71,4 @@
obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index 58344b6..ba4364d 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -32,7 +32,6 @@
#include <linux/workqueue.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/ad9389b.h>
@@ -343,12 +342,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = ad9389b_rd(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
@@ -356,24 +349,11 @@
static int ad9389b_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
ad9389b_wr(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
#endif
-static int ad9389b_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_AD9389B, 0);
-}
-
static int ad9389b_log_status(struct v4l2_subdev *sd)
{
struct ad9389b_state *state = get_ad9389b_state(sd);
@@ -600,7 +580,6 @@
static const struct v4l2_subdev_core_ops ad9389b_core_ops = {
.log_status = ad9389b_log_status,
- .g_chip_ident = ad9389b_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ad9389b_g_register,
.s_register = ad9389b_s_register,
@@ -1188,15 +1167,14 @@
v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n",
client->addr << 1);
- state = kzalloc(sizeof(struct ad9389b_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
/* Platform data */
if (pdata == NULL) {
v4l_err(client, "No platform data!\n");
- err = -ENODEV;
- goto err_free;
+ return -ENODEV;
}
memcpy(&state->pdata, pdata, sizeof(state->pdata));
@@ -1251,12 +1229,14 @@
state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1));
if (state->edid_i2c_client == NULL) {
v4l2_err(sd, "failed to register edid i2c client\n");
+ err = -ENOMEM;
goto err_entity;
}
state->work_queue = create_singlethread_workqueue(sd->name);
if (state->work_queue == NULL) {
v4l2_err(sd, "could not create workqueue\n");
+ err = -ENOMEM;
goto err_unreg;
}
@@ -1276,8 +1256,6 @@
media_entity_cleanup(&sd->entity);
err_hdl:
v4l2_ctrl_handler_free(&state->hdl);
-err_free:
- kfree(state);
return err;
}
@@ -1302,15 +1280,14 @@
v4l2_device_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- kfree(get_ad9389b_state(sd));
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_device_id ad9389b_id[] = {
- { "ad9389b", V4L2_IDENT_AD9389B },
- { "ad9889b", V4L2_IDENT_AD9389B },
+ { "ad9389b", 0 },
+ { "ad9889b", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad9389b_id);
diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c
index ef75abe..873fe19 100644
--- a/drivers/media/i2c/adp1653.c
+++ b/drivers/media/i2c/adp1653.c
@@ -417,7 +417,7 @@
if (client->dev.platform_data == NULL)
return -ENODEV;
- flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
if (flash == NULL)
return -ENOMEM;
@@ -443,7 +443,6 @@
free_and_quit:
v4l2_ctrl_handler_free(&flash->ctrls);
- kfree(flash);
return ret;
}
@@ -455,7 +454,7 @@
v4l2_device_unregister_subdev(&flash->subdev);
v4l2_ctrl_handler_free(&flash->ctrls);
media_entity_cleanup(&flash->subdev.entity);
- kfree(flash);
+
return 0;
}
diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c
index 6bc01fb..04bb297 100644
--- a/drivers/media/i2c/adv7170.c
+++ b/drivers/media/i2c/adv7170.c
@@ -36,7 +36,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
MODULE_AUTHOR("Maxim Yevtyushkin");
@@ -317,19 +316,8 @@
return ret;
}
-static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0);
-}
-
/* ----------------------------------------------------------------------- */
-static const struct v4l2_subdev_core_ops adv7170_core_ops = {
- .g_chip_ident = adv7170_g_chip_ident,
-};
-
static const struct v4l2_subdev_video_ops adv7170_video_ops = {
.s_std_output = adv7170_s_std_output,
.s_routing = adv7170_s_routing,
@@ -339,7 +327,6 @@
};
static const struct v4l2_subdev_ops adv7170_ops = {
- .core = &adv7170_core_ops,
.video = &adv7170_video_ops,
};
@@ -359,7 +346,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL);
+ encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
@@ -384,7 +371,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_adv7170(sd));
return 0;
}
diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c
index c7640fa..b88f3b3 100644
--- a/drivers/media/i2c/adv7175.c
+++ b/drivers/media/i2c/adv7175.c
@@ -32,7 +32,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
MODULE_AUTHOR("Dave Perks");
@@ -355,13 +354,6 @@
return ret;
}
-static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0);
-}
-
static int adv7175_s_power(struct v4l2_subdev *sd, int on)
{
if (on)
@@ -375,7 +367,6 @@
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops adv7175_core_ops = {
- .g_chip_ident = adv7175_g_chip_ident,
.init = adv7175_init,
.s_power = adv7175_s_power,
};
@@ -409,7 +400,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL);
+ encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
@@ -434,7 +425,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_adv7175(sd));
return 0;
}
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index afd561a..d7d99f1 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1,6 +1,8 @@
/*
* adv7180.c Analog Devices ADV7180 video decoder driver
* Copyright (c) 2009 Intel Corporation
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ * Copyright (C) 2013 Renesas Solutions Corp.
*
* 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
@@ -27,7 +29,6 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/mutex.h>
#define ADV7180_INPUT_CONTROL_REG 0x00
@@ -272,14 +273,6 @@
return ret;
}
-static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
-}
-
static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7180_state *state = to_state(sd);
@@ -397,14 +390,57 @@
v4l2_ctrl_handler_free(&state->ctrl_hdl);
}
+static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV8_2X8;
+
+ return 0;
+}
+
+static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7180_state *state = to_state(sd);
+
+ fmt->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->width = 720;
+ fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
+
+ return 0;
+}
+
+static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ /*
+ * The ADV7180 sensor supports BT.601/656 output modes.
+ * The BT.656 is default and not yet configurable by s/w.
+ */
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_BT656;
+
+ return 0;
+}
+
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
+ .enum_mbus_fmt = adv7180_enum_mbus_fmt,
+ .try_mbus_fmt = adv7180_mbus_fmt,
+ .g_mbus_fmt = adv7180_mbus_fmt,
+ .s_mbus_fmt = adv7180_mbus_fmt,
+ .g_mbus_config = adv7180_g_mbus_config,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
- .g_chip_ident = adv7180_g_chip_ident,
.s_std = adv7180_s_std,
};
@@ -555,7 +591,7 @@
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr, client->adapter->name);
- state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL) {
ret = -ENOMEM;
goto err;
@@ -582,7 +618,6 @@
err_unreg_subdev:
mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
- kfree(state);
err:
printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret);
return ret;
@@ -607,7 +642,6 @@
mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
return 0;
}
@@ -616,9 +650,10 @@
{},
};
-#ifdef CONFIG_PM
-static int adv7180_suspend(struct i2c_client *client, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int adv7180_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
int ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
@@ -628,8 +663,9 @@
return 0;
}
-static int adv7180_resume(struct i2c_client *client)
+static int adv7180_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7180_state *state = to_state(sd);
int ret;
@@ -643,6 +679,12 @@
return ret;
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
+#define ADV7180_PM_OPS (&adv7180_pm_ops)
+
+#else
+#define ADV7180_PM_OPS NULL
#endif
MODULE_DEVICE_TABLE(i2c, adv7180_id);
@@ -651,13 +693,10 @@
.driver = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
+ .pm = ADV7180_PM_OPS,
},
.probe = adv7180_probe,
.remove = adv7180_remove,
-#ifdef CONFIG_PM
- .suspend = adv7180_suspend,
- .resume = adv7180_resume,
-#endif
.id_table = adv7180_id,
};
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index 56a1fa4..6f738d8 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -28,7 +28,6 @@
#include <linux/videodev2.h>
#include <media/adv7183.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -375,28 +374,28 @@
reg = adv7183_read(sd, ADV7183_STATUS_1);
switch ((reg >> 0x4) & 0x7) {
case 0:
- *std = V4L2_STD_NTSC;
+ *std &= V4L2_STD_NTSC;
break;
case 1:
- *std = V4L2_STD_NTSC_443;
+ *std &= V4L2_STD_NTSC_443;
break;
case 2:
- *std = V4L2_STD_PAL_M;
+ *std &= V4L2_STD_PAL_M;
break;
case 3:
- *std = V4L2_STD_PAL_60;
+ *std &= V4L2_STD_PAL_60;
break;
case 4:
- *std = V4L2_STD_PAL;
+ *std &= V4L2_STD_PAL;
break;
case 5:
- *std = V4L2_STD_SECAM;
+ *std &= V4L2_STD_SECAM;
break;
case 6:
- *std = V4L2_STD_PAL_Nc;
+ *std &= V4L2_STD_PAL_Nc;
break;
case 7:
- *std = V4L2_STD_SECAM;
+ *std &= V4L2_STD_SECAM;
break;
default:
*std = V4L2_STD_UNKNOWN;
@@ -474,34 +473,16 @@
struct adv7183 *decoder = to_adv7183(sd);
if (enable)
- gpio_direction_output(decoder->oe_pin, 0);
+ gpio_set_value(decoder->oe_pin, 0);
else
- gpio_direction_output(decoder->oe_pin, 1);
+ gpio_set_value(decoder->oe_pin, 1);
udelay(1);
return 0;
}
-static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- int rev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* 0x11 for adv7183, 0x13 for adv7183b */
- rev = adv7183_read(sd, ADV7183_IDENT);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = adv7183_read(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
@@ -509,12 +490,6 @@
static int adv7183_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
@@ -529,7 +504,6 @@
.g_std = adv7183_g_std,
.s_std = adv7183_s_std,
.reset = adv7183_reset,
- .g_chip_ident = adv7183_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = adv7183_g_register,
.s_register = adv7183_s_register,
@@ -573,23 +547,24 @@
if (pin_array == NULL)
return -EINVAL;
- decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
+ decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL)
return -ENOMEM;
decoder->reset_pin = pin_array[0];
decoder->oe_pin = pin_array[1];
- if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
+ if (devm_gpio_request_one(&client->dev, decoder->reset_pin,
+ GPIOF_OUT_INIT_LOW, "ADV7183 Reset")) {
v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
- ret = -EBUSY;
- goto err_free_decoder;
+ return -EBUSY;
}
- if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
+ if (devm_gpio_request_one(&client->dev, decoder->oe_pin,
+ GPIOF_OUT_INIT_HIGH,
+ "ADV7183 Output Enable")) {
v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
- ret = -EBUSY;
- goto err_free_reset;
+ return -EBUSY;
}
sd = &decoder->sd;
@@ -611,7 +586,7 @@
ret = hdl->error;
v4l2_ctrl_handler_free(hdl);
- goto err_free_oe;
+ return ret;
}
/* v4l2 doesn't support an autodetect standard, pick PAL as default */
@@ -619,12 +594,10 @@
decoder->input = ADV7183_COMPOSITE4;
decoder->output = ADV7183_8BIT_OUT;
- gpio_direction_output(decoder->oe_pin, 1);
/* reset chip */
- gpio_direction_output(decoder->reset_pin, 0);
/* reset pulse width at least 5ms */
mdelay(10);
- gpio_direction_output(decoder->reset_pin, 1);
+ gpio_set_value(decoder->reset_pin, 1);
/* wait 5ms before any further i2c writes are performed */
mdelay(5);
@@ -638,29 +611,18 @@
ret = v4l2_ctrl_handler_setup(hdl);
if (ret) {
v4l2_ctrl_handler_free(hdl);
- goto err_free_oe;
+ return ret;
}
return 0;
-err_free_oe:
- gpio_free(decoder->oe_pin);
-err_free_reset:
- gpio_free(decoder->reset_pin);
-err_free_decoder:
- kfree(decoder);
- return ret;
}
static int adv7183_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct adv7183 *decoder = to_adv7183(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- gpio_free(decoder->oe_pin);
- gpio_free(decoder->reset_pin);
- kfree(decoder);
return 0;
}
diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c
index 9fc2b98..7606218 100644
--- a/drivers/media/i2c/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
@@ -28,7 +28,6 @@
#include <media/adv7343.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include "adv7343_regs.h"
@@ -311,21 +310,12 @@
return -EINVAL;
}
-static int adv7343_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0);
-}
-
static const struct v4l2_ctrl_ops adv7343_ctrl_ops = {
.s_ctrl = adv7343_s_ctrl,
};
static const struct v4l2_subdev_core_ops adv7343_core_ops = {
.log_status = adv7343_log_status,
- .g_chip_ident = adv7343_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c
index 3dc6098..558f191 100644
--- a/drivers/media/i2c/adv7393.c
+++ b/drivers/media/i2c/adv7393.c
@@ -33,7 +33,6 @@
#include <media/adv7393.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include "adv7393_regs.h"
@@ -301,21 +300,12 @@
return -EINVAL;
}
-static int adv7393_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0);
-}
-
static const struct v4l2_ctrl_ops adv7393_ctrl_ops = {
.s_ctrl = adv7393_s_ctrl,
};
static const struct v4l2_subdev_core_ops adv7393_core_ops = {
.log_status = adv7393_log_status,
- .g_chip_ident = adv7393_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -410,7 +400,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
@@ -444,16 +434,13 @@
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
v4l2_ctrl_handler_setup(&state->hdl);
err = adv7393_initialize(&state->sd);
- if (err) {
+ if (err)
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
- }
return err;
}
@@ -464,7 +451,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 31a63c9..1d675b5 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -38,7 +38,6 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <media/adv7604.h>
static int debug;
@@ -643,12 +642,6 @@
static int adv7604_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->size = 1;
switch (reg->reg >> 8) {
case 0:
@@ -701,12 +694,6 @@
static int adv7604_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
switch (reg->reg >> 8) {
case 0:
io_write(sd, reg->reg & 0xff, reg->val & 0xff);
@@ -984,14 +971,6 @@
return -EINVAL;
}
-static int adv7604_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7604, 0);
-}
-
/* ----------------------------------------------------------------------- */
static inline bool no_power(struct v4l2_subdev *sd)
@@ -1787,7 +1766,6 @@
.s_ctrl = v4l2_subdev_s_ctrl,
.queryctrl = v4l2_subdev_queryctrl,
.querymenu = v4l2_subdev_querymenu,
- .g_chip_ident = adv7604_g_chip_ident,
.interrupt_service_routine = adv7604_isr,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = adv7604_g_register,
@@ -1968,7 +1946,7 @@
v4l_dbg(1, debug, client, "detecting adv7604 client on address 0x%x\n",
client->addr << 1);
- state = kzalloc(sizeof(struct adv7604_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (!state) {
v4l_err(client, "Could not allocate adv7604_state memory!\n");
return -ENOMEM;
@@ -1977,8 +1955,7 @@
/* platform data */
if (!pdata) {
v4l_err(client, "No platform data!\n");
- err = -ENODEV;
- goto err_state;
+ return -ENODEV;
}
memcpy(&state->pdata, pdata, sizeof(state->pdata));
@@ -1991,8 +1968,7 @@
if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) {
v4l2_info(sd, "not an adv7604 on address 0x%x\n",
client->addr << 1);
- err = -ENODEV;
- goto err_state;
+ return -ENODEV;
}
/* control handlers */
@@ -2093,8 +2069,6 @@
adv7604_unregister_clients(state);
err_hdl:
v4l2_ctrl_handler_free(hdl);
-err_state:
- kfree(state);
return err;
}
@@ -2111,7 +2085,6 @@
media_entity_cleanup(&sd->entity);
adv7604_unregister_clients(to_state(sd));
v4l2_ctrl_handler_free(sd->ctrl_handler);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c
index fd47465..c14e667 100644
--- a/drivers/media/i2c/ak881x.c
+++ b/drivers/media/i2c/ak881x.c
@@ -16,7 +16,6 @@
#include <linux/module.h>
#include <media/ak881x.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -33,7 +32,6 @@
struct v4l2_subdev subdev;
struct ak881x_pdata *pdata;
unsigned int lines;
- int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */
char revision; /* DEVICE_REVISION content */
};
@@ -62,36 +60,16 @@
return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
}
-static int ak881x_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ak881x *ak881x = to_ak881x(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = ak881x->id;
- id->revision = ak881x->revision;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ak881x_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
+ if (reg->reg > 0x26)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
+ reg->size = 1;
reg->val = reg_read(client, reg->reg);
if (reg->val > 0xffff)
@@ -105,12 +83,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
+ if (reg->reg > 0x26)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
@@ -229,7 +204,6 @@
}
static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {
- .g_chip_ident = ak881x_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ak881x_g_register,
.s_register = ak881x_s_register,
@@ -264,7 +238,7 @@
return -EIO;
}
- ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL);
+ ak881x = devm_kzalloc(&client->dev, sizeof(*ak881x), GFP_KERNEL);
if (!ak881x)
return -ENOMEM;
@@ -274,15 +248,11 @@
switch (data) {
case 0x13:
- ak881x->id = V4L2_IDENT_AK8813;
- break;
case 0x14:
- ak881x->id = V4L2_IDENT_AK8814;
break;
default:
dev_err(&client->dev,
"No ak881x chip detected, register read %x\n", data);
- kfree(ak881x);
return -ENODEV;
}
@@ -331,7 +301,6 @@
struct ak881x *ak881x = to_ak881x(client);
v4l2_device_unregister_subdev(&ak881x->subdev);
- kfree(ak881x);
return 0;
}
diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c
index 58d523f..301084b 100644
--- a/drivers/media/i2c/as3645a.c
+++ b/drivers/media/i2c/as3645a.c
@@ -813,7 +813,7 @@
if (client->dev.platform_data == NULL)
return -ENODEV;
- flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
if (flash == NULL)
return -ENOMEM;
@@ -838,10 +838,8 @@
flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
done:
- if (ret < 0) {
+ if (ret < 0)
v4l2_ctrl_handler_free(&flash->ctrls);
- kfree(flash);
- }
return ret;
}
@@ -855,7 +853,6 @@
v4l2_ctrl_handler_free(&flash->ctrls);
media_entity_cleanup(&flash->subdev.entity);
mutex_destroy(&flash->power_lock);
- kfree(flash);
return 0;
}
diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c
index 377bf05..369cf6f 100644
--- a/drivers/media/i2c/bt819.c
+++ b/drivers/media/i2c/bt819.c
@@ -36,7 +36,6 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/bt819.h>
@@ -57,7 +56,6 @@
unsigned char reg[32];
v4l2_std_id norm;
- int ident;
int input;
int enable;
};
@@ -217,15 +215,17 @@
struct bt819 *decoder = to_bt819(sd);
int status = bt819_read(decoder, 0x00);
int res = V4L2_IN_ST_NO_SIGNAL;
- v4l2_std_id std;
+ v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
if ((status & 0x80))
res = 0;
+ else
+ std = V4L2_STD_UNKNOWN;
if ((status & 0x10))
- std = V4L2_STD_PAL;
+ std &= V4L2_STD_PAL;
else
- std = V4L2_STD_NTSC;
+ std &= V4L2_STD_NTSC;
if (pstd)
*pstd = std;
if (pstatus)
@@ -373,14 +373,6 @@
return 0;
}
-static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct bt819 *decoder = to_bt819(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops bt819_ctrl_ops = {
@@ -388,7 +380,6 @@
};
static const struct v4l2_subdev_core_ops bt819_core_ops = {
- .g_chip_ident = bt819_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -425,7 +416,7 @@
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL);
+ decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL)
return -ENOMEM;
sd = &decoder->sd;
@@ -435,15 +426,12 @@
switch (ver & 0xf0) {
case 0x70:
name = "bt819a";
- decoder->ident = V4L2_IDENT_BT819A;
break;
case 0x60:
name = "bt817a";
- decoder->ident = V4L2_IDENT_BT817A;
break;
case 0x20:
name = "bt815a";
- decoder->ident = V4L2_IDENT_BT815A;
break;
default:
v4l2_dbg(1, debug, sd,
@@ -476,7 +464,6 @@
int err = decoder->hdl.error;
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
return err;
}
v4l2_ctrl_handler_setup(&decoder->hdl);
@@ -490,7 +477,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
return 0;
}
diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c
index 7e5bd36..7fc163d 100644
--- a/drivers/media/i2c/bt856.c
+++ b/drivers/media/i2c/bt856.c
@@ -36,7 +36,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
@@ -177,17 +176,9 @@
return 0;
}
-static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops bt856_core_ops = {
- .g_chip_ident = bt856_g_chip_ident,
.init = bt856_init,
};
@@ -216,7 +207,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL);
+ encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
@@ -250,7 +241,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_bt856(sd));
return 0;
}
diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c
index 905320b..a8bf10f 100644
--- a/drivers/media/i2c/bt866.c
+++ b/drivers/media/i2c/bt866.c
@@ -36,7 +36,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Brooktree-866 video encoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
@@ -175,26 +174,14 @@
bt866_write(client, 0xdc, val);
#endif
-static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0);
-}
-
/* ----------------------------------------------------------------------- */
-static const struct v4l2_subdev_core_ops bt866_core_ops = {
- .g_chip_ident = bt866_g_chip_ident,
-};
-
static const struct v4l2_subdev_video_ops bt866_video_ops = {
.s_std_output = bt866_s_std_output,
.s_routing = bt866_s_routing,
};
static const struct v4l2_subdev_ops bt866_ops = {
- .core = &bt866_core_ops,
.video = &bt866_video_ops,
};
@@ -207,7 +194,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- encoder = kzalloc(sizeof(*encoder), GFP_KERNEL);
+ encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
sd = &encoder->sd;
@@ -220,7 +207,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_bt866(sd));
return 0;
}
diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c
index 1d2f7c8..34b76a9 100644
--- a/drivers/media/i2c/cs5345.c
+++ b/drivers/media/i2c/cs5345.c
@@ -24,7 +24,6 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
@@ -99,12 +98,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->size = 1;
reg->val = cs5345_read(sd, reg->reg & 0x1f);
return 0;
@@ -112,24 +105,11 @@
static int cs5345_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff);
return 0;
}
#endif
-static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0);
-}
-
static int cs5345_log_status(struct v4l2_subdev *sd)
{
u8 v = cs5345_read(sd, 0x09) & 7;
@@ -152,7 +132,6 @@
static const struct v4l2_subdev_core_ops cs5345_core_ops = {
.log_status = cs5345_log_status,
- .g_chip_ident = cs5345_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -190,7 +169,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -206,7 +185,6 @@
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
/* set volume/mute */
@@ -227,7 +205,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c
index b293912..27400c1 100644
--- a/drivers/media/i2c/cs53l32a.c
+++ b/drivers/media/i2c/cs53l32a.c
@@ -28,7 +28,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
@@ -104,14 +103,6 @@
return -EINVAL;
}
-static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client,
- chip, V4L2_IDENT_CS53l32A, 0);
-}
-
static int cs53l32a_log_status(struct v4l2_subdev *sd)
{
struct cs53l32a_state *state = to_state(sd);
@@ -130,7 +121,6 @@
static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
.log_status = cs53l32a_log_status,
- .g_chip_ident = cs53l32a_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -175,7 +165,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -197,7 +187,6 @@
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
@@ -228,7 +217,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 12fb9b2..2e3771d 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -45,7 +45,6 @@
#include <linux/delay.h>
#include <linux/math64.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
#include <media/cx25840.h>
#include "cx25840-core.h"
@@ -498,7 +497,7 @@
/* Sys PLL */
switch (state->id) {
- case V4L2_IDENT_CX23888_AV:
+ case CX23888_AV:
/*
* 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz
* 572.73 MHz before post divide
@@ -511,7 +510,7 @@
cx25840_write4(client, 0x42c, 0x42600000);
cx25840_write4(client, 0x44c, 0x161f1000);
break;
- case V4L2_IDENT_CX23887_AV:
+ case CX23887_AV:
/*
* 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz
* 572.73 MHz before post divide
@@ -519,7 +518,7 @@
cx25840_write4(client, 0x11c, 0x01d1744c);
cx25840_write4(client, 0x118, 0x00000416);
break;
- case V4L2_IDENT_CX23885_AV:
+ case CX23885_AV:
default:
/*
* 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz
@@ -546,7 +545,7 @@
/* HVR1850 */
switch (state->id) {
- case V4L2_IDENT_CX23888_AV:
+ case CX23888_AV:
/* 888/HVR1250 specific */
cx25840_write4(client, 0x10c, 0x13333333);
cx25840_write4(client, 0x108, 0x00000515);
@@ -570,7 +569,7 @@
* 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz
*/
switch (state->id) {
- case V4L2_IDENT_CX23888_AV:
+ case CX23888_AV:
/*
* 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz
* 368.64 MHz before post divide
@@ -580,7 +579,7 @@
cx25840_write4(client, 0x114, 0x017dbf48);
cx25840_write4(client, 0x110, 0x000a030e);
break;
- case V4L2_IDENT_CX23887_AV:
+ case CX23887_AV:
/*
* 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz
* 368.64 MHz before post divide
@@ -589,7 +588,7 @@
cx25840_write4(client, 0x114, 0x017dbf48);
cx25840_write4(client, 0x110, 0x000a030e);
break;
- case V4L2_IDENT_CX23885_AV:
+ case CX23885_AV:
default:
/*
* 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz
@@ -1662,10 +1661,6 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->size = 1;
reg->val = cx25840_read(client, reg->reg & 0x0fff);
return 0;
@@ -1675,10 +1670,6 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
return 0;
}
@@ -1938,14 +1929,6 @@
return 0;
}
-static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct cx25840_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
-}
-
static int cx25840_log_status(struct v4l2_subdev *sd)
{
struct cx25840_state *state = to_state(sd);
@@ -5051,7 +5034,6 @@
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.log_status = cx25840_log_status,
- .g_chip_ident = cx25840_g_chip_ident,
.g_ctrl = v4l2_subdev_g_ctrl,
.s_ctrl = v4l2_subdev_s_ctrl,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -5128,18 +5110,18 @@
ret = cx25840_read4(client, 0x300);
if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) {
/* No DIF */
- ret = V4L2_IDENT_CX23885_AV;
+ ret = CX23885_AV;
} else {
/* CX23887 has a broken DIF, but the registers
* appear valid (but unused), good enough to detect. */
- ret = V4L2_IDENT_CX23887_AV;
+ ret = CX23887_AV;
}
} else if (cx25840_read4(client, 0x300) & 0x0fffffff) {
/* DIF PLL Freq Word reg exists; chip must be a CX23888 */
- ret = V4L2_IDENT_CX23888_AV;
+ ret = CX23888_AV;
} else {
v4l_err(client, "Unable to detect h/w, assuming cx23887\n");
- ret = V4L2_IDENT_CX23887_AV;
+ ret = CX23887_AV;
}
/* Back into digital power down */
@@ -5153,7 +5135,7 @@
struct cx25840_state *state;
struct v4l2_subdev *sd;
int default_volume;
- u32 id = V4L2_IDENT_NONE;
+ u32 id;
u16 device_id;
/* Check if the adapter supports the needed features */
@@ -5169,14 +5151,14 @@
/* The high byte of the device ID should be
* 0x83 for the cx2583x and 0x84 for the cx2584x */
if ((device_id & 0xff00) == 0x8300) {
- id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ id = CX25836 + ((device_id >> 4) & 0xf) - 6;
} else if ((device_id & 0xff00) == 0x8400) {
- id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ id = CX25840 + ((device_id >> 4) & 0xf);
} else if (device_id == 0x0000) {
id = get_cx2388x_ident(client);
} else if ((device_id & 0xfff0) == 0x5A30) {
/* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
- id = V4L2_IDENT_CX2310X_AV;
+ id = CX2310X_AV;
} else if ((device_id & 0xff) == (device_id >> 8)) {
v4l_err(client,
"likely a confused/unresponsive cx2388[578] A/V decoder"
@@ -5190,7 +5172,7 @@
return -ENODEV;
}
- state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
@@ -5198,26 +5180,26 @@
v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
switch (id) {
- case V4L2_IDENT_CX23885_AV:
+ case CX23885_AV:
v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
break;
- case V4L2_IDENT_CX23887_AV:
+ case CX23887_AV:
v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
break;
- case V4L2_IDENT_CX23888_AV:
+ case CX23888_AV:
v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
break;
- case V4L2_IDENT_CX2310X_AV:
+ case CX2310X_AV:
v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n",
device_id, client->addr << 1, client->adapter->name);
break;
- case V4L2_IDENT_CX25840:
- case V4L2_IDENT_CX25841:
- case V4L2_IDENT_CX25842:
- case V4L2_IDENT_CX25843:
+ case CX25840:
+ case CX25841:
+ case CX25842:
+ case CX25843:
/* Note: revision '(device_id & 0x0f) == 2' was never built. The
marking skips from 0x1 == 22 to 0x3 == 23. */
v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
@@ -5226,8 +5208,8 @@
: (device_id & 0x0f),
client->addr << 1, client->adapter->name);
break;
- case V4L2_IDENT_CX25836:
- case V4L2_IDENT_CX25837:
+ case CX25836:
+ case CX25837:
default:
v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n",
(device_id & 0xfff0) >> 4, device_id & 0x0f,
@@ -5292,7 +5274,6 @@
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
if (!is_cx2583x(state))
@@ -5317,7 +5298,6 @@
cx25840_ir_remove(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h
index bd4ada2..37bc042 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.h
+++ b/drivers/media/i2c/cx25840/cx25840-core.h
@@ -23,12 +23,24 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <linux/i2c.h>
struct cx25840_ir_state;
+enum cx25840_model {
+ CX23885_AV,
+ CX23887_AV,
+ CX23888_AV,
+ CX2310X_AV,
+ CX25840,
+ CX25841,
+ CX25842,
+ CX25843,
+ CX25836,
+ CX25837,
+};
+
struct cx25840_state {
struct i2c_client *c;
struct v4l2_subdev sd;
@@ -46,7 +58,7 @@
u32 audclk_freq;
int audmode;
int vbi_line_offset;
- u32 id;
+ enum cx25840_model id;
u32 rev;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
@@ -66,35 +78,35 @@
static inline bool is_cx2583x(struct cx25840_state *state)
{
- return state->id == V4L2_IDENT_CX25836 ||
- state->id == V4L2_IDENT_CX25837;
+ return state->id == CX25836 ||
+ state->id == CX25837;
}
static inline bool is_cx231xx(struct cx25840_state *state)
{
- return state->id == V4L2_IDENT_CX2310X_AV;
+ return state->id == CX2310X_AV;
}
static inline bool is_cx2388x(struct cx25840_state *state)
{
- return state->id == V4L2_IDENT_CX23885_AV ||
- state->id == V4L2_IDENT_CX23887_AV ||
- state->id == V4L2_IDENT_CX23888_AV;
+ return state->id == CX23885_AV ||
+ state->id == CX23887_AV ||
+ state->id == CX23888_AV;
}
static inline bool is_cx23885(struct cx25840_state *state)
{
- return state->id == V4L2_IDENT_CX23885_AV;
+ return state->id == CX23885_AV;
}
static inline bool is_cx23887(struct cx25840_state *state)
{
- return state->id == V4L2_IDENT_CX23887_AV;
+ return state->id == CX23887_AV;
}
static inline bool is_cx23888(struct cx25840_state *state)
{
- return state->id == V4L2_IDENT_CX23888_AV;
+ return state->id == CX23888_AV;
}
/* ----------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c
index 9ae977b..e6588ee 100644
--- a/drivers/media/i2c/cx25840/cx25840-ir.c
+++ b/drivers/media/i2c/cx25840/cx25840-ir.c
@@ -1230,16 +1230,14 @@
if (!(is_cx23885(state) || is_cx23887(state)))
return 0;
- ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL);
+ ir_state = devm_kzalloc(&state->c->dev, sizeof(*ir_state), GFP_KERNEL);
if (ir_state == NULL)
return -ENOMEM;
spin_lock_init(&ir_state->rx_kfifo_lock);
if (kfifo_alloc(&ir_state->rx_kfifo,
- CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
- kfree(ir_state);
+ CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL))
return -ENOMEM;
- }
ir_state->c = state->c;
state->ir_state = ir_state;
@@ -1273,7 +1271,6 @@
cx25840_ir_tx_shutdown(sd);
kfifo_free(&ir_state->rx_kfifo);
- kfree(ir_state);
state->ir_state = NULL;
return 0;
}
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 8e2f79c..82bf567 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -295,7 +295,7 @@
unsigned short addr = client->addr;
int err;
- ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL);
+ ir = devm_kzalloc(&client->dev, sizeof(*ir), GFP_KERNEL);
if (!ir)
return -ENOMEM;
@@ -398,10 +398,8 @@
* internally
*/
rc = rc_allocate_device();
- if (!rc) {
- err = -ENOMEM;
- goto err_out_free;
- }
+ if (!rc)
+ return -ENOMEM;
}
ir->rc = rc;
@@ -454,7 +452,6 @@
err_out_free:
/* Only frees rc if it were allocated internally */
rc_free_device(rc);
- kfree(ir);
return err;
}
@@ -470,7 +467,6 @@
rc_unregister_device(ir->rc);
/* free memory */
- kfree(ir);
return 0;
}
diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c
index 04a6efa..c3e94ae 100644
--- a/drivers/media/i2c/ks0127.c
+++ b/drivers/media/i2c/ks0127.c
@@ -42,7 +42,6 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include "ks0127.h"
MODULE_DESCRIPTION("KS0127 video decoder driver");
@@ -200,7 +199,6 @@
struct ks0127 {
struct v4l2_subdev sd;
v4l2_std_id norm;
- int ident;
u8 regs[256];
};
@@ -371,12 +369,9 @@
****************************************************************************/
static void ks0127_init(struct v4l2_subdev *sd)
{
- struct ks0127 *ks = to_ks0127(sd);
u8 *table = reg_defaults;
int i;
- ks->ident = V4L2_IDENT_KS0127;
-
v4l2_dbg(1, debug, sd, "reset\n");
msleep(1);
@@ -397,7 +392,6 @@
if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) {
- ks->ident = V4L2_IDENT_KS0122S;
v4l2_dbg(1, debug, sd, "ks0122s found\n");
return;
}
@@ -408,7 +402,6 @@
break;
case 9:
- ks->ident = V4L2_IDENT_KS0127B;
v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n");
break;
@@ -616,17 +609,24 @@
{
int stat = V4L2_IN_ST_NO_SIGNAL;
u8 status;
- v4l2_std_id std = V4L2_STD_ALL;
+ v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
status = ks0127_read(sd, KS_STAT);
if (!(status & 0x20)) /* NOVID not set */
stat = 0;
- if (!(status & 0x01)) /* CLOCK set */
+ if (!(status & 0x01)) { /* CLOCK set */
stat |= V4L2_IN_ST_NO_COLOR;
- if ((status & 0x08)) /* PALDET set */
- std = V4L2_STD_PAL;
+ std = V4L2_STD_UNKNOWN;
+ } else {
+ if ((status & 0x08)) /* PALDET set */
+ std &= V4L2_STD_PAL;
+ else
+ std &= V4L2_STD_NTSC;
+ }
+ if ((status & 0x10)) /* PALDET set */
+ std &= V4L2_STD_525_60;
else
- std = V4L2_STD_NTSC;
+ std &= V4L2_STD_625_50;
if (pstd)
*pstd = std;
if (pstatus)
@@ -646,18 +646,9 @@
return ks0127_status(sd, status, NULL);
}
-static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ks0127 *ks = to_ks0127(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops ks0127_core_ops = {
- .g_chip_ident = ks0127_g_chip_ident,
.s_std = ks0127_s_std,
};
@@ -685,7 +676,7 @@
client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board",
client->addr << 1, client->adapter->name);
- ks = kzalloc(sizeof(*ks), GFP_KERNEL);
+ ks = devm_kzalloc(&client->dev, sizeof(*ks), GFP_KERNEL);
if (ks == NULL)
return -ENOMEM;
sd = &ks->sd;
@@ -708,7 +699,6 @@
v4l2_device_unregister_subdev(sd);
ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */
ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */
- kfree(to_ks0127(sd));
return 0;
}
diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c
index 39f50fd..bf47635 100644
--- a/drivers/media/i2c/m52790.c
+++ b/drivers/media/i2c/m52790.c
@@ -29,7 +29,6 @@
#include <linux/videodev2.h>
#include <media/m52790.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch");
MODULE_AUTHOR("Hans Verkuil");
@@ -83,12 +82,7 @@
static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
struct m52790_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
if (reg->reg != 0)
return -EINVAL;
reg->size = 1;
@@ -99,12 +93,7 @@
static int m52790_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
struct m52790_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
if (reg->reg != 0)
return -EINVAL;
state->input = reg->val & 0x0303;
@@ -114,13 +103,6 @@
}
#endif
-static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0);
-}
-
static int m52790_log_status(struct v4l2_subdev *sd)
{
struct m52790_state *state = to_state(sd);
@@ -136,7 +118,6 @@
static const struct v4l2_subdev_core_ops m52790_core_ops = {
.log_status = m52790_log_status,
- .g_chip_ident = m52790_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = m52790_g_register,
.s_register = m52790_s_register,
@@ -174,7 +155,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
@@ -191,7 +172,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
index 0b899cb..8d870b7 100644
--- a/drivers/media/i2c/m5mols/m5mols_core.c
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -930,6 +930,7 @@
const struct i2c_device_id *id)
{
const struct m5mols_platform_data *pdata = client->dev.platform_data;
+ unsigned long gpio_flags;
struct m5mols_info *info;
struct v4l2_subdev *sd;
int ret;
@@ -949,24 +950,27 @@
return -EINVAL;
}
- info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->pdata = pdata;
info->set_power = pdata->set_power;
- ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+ gpio_flags = pdata->reset_polarity
+ ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_reset, gpio_flags,
+ "M5MOLS_NRST");
if (ret) {
dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
- goto out_free;
+ return ret;
}
- gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
- ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+ ret = devm_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;
+ return ret;
}
sd = &info->sd;
@@ -978,17 +982,17 @@
info->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
if (ret < 0)
- goto out_reg;
+ return ret;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
init_waitqueue_head(&info->irq_waitq);
mutex_init(&info->lock);
- ret = request_irq(client->irq, m5mols_irq_handler,
- IRQF_TRIGGER_RISING, MODULE_NAME, sd);
+ ret = devm_request_irq(&client->dev, client->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;
+ goto error;
}
info->res_type = M5MOLS_RESTYPE_MONITOR;
info->ffmt[0] = m5mols_default_ffmt[0];
@@ -996,7 +1000,7 @@
ret = m5mols_sensor_power(info, true);
if (ret)
- goto out_irq;
+ goto error;
ret = m5mols_fw_start(sd);
if (!ret)
@@ -1005,32 +1009,19 @@
ret = m5mols_sensor_power(info, false);
if (!ret)
return 0;
-out_irq:
- free_irq(client->irq, sd);
-out_me:
+error:
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 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);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- free_irq(client->irq, sd);
-
- regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
- gpio_free(info->pdata->gpio_reset);
media_entity_cleanup(&sd->entity);
- kfree(info);
+
return 0;
}
diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c
new file mode 100644
index 0000000..efdc873
--- /dev/null
+++ b/drivers/media/i2c/ml86v7667.c
@@ -0,0 +1,431 @@
+/*
+ * OKI Semiconductor ML86V7667 video decoder driver
+ *
+ * Author: Vladimir Barinov <source@cogentembedded.com>
+ * Copyright (C) 2013 Cogent Embedded, Inc.
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+
+#define DRV_NAME "ml86v7667"
+
+/* Subaddresses */
+#define MRA_REG 0x00 /* Mode Register A */
+#define MRC_REG 0x02 /* Mode Register C */
+#define LUMC_REG 0x0C /* Luminance Control */
+#define CLC_REG 0x10 /* Contrast level control */
+#define SSEPL_REG 0x11 /* Sync separation level */
+#define CHRCA_REG 0x12 /* Chrominance Control A */
+#define ACCC_REG 0x14 /* ACC Loop filter & Chrominance control */
+#define ACCRC_REG 0x15 /* ACC Reference level control */
+#define HUE_REG 0x16 /* Hue control */
+#define ADC2_REG 0x1F /* ADC Register 2 */
+#define PLLR1_REG 0x20 /* PLL Register 1 */
+#define STATUS_REG 0x2C /* STATUS Register */
+
+/* Mode Register A register bits */
+#define MRA_OUTPUT_MODE_MASK (3 << 6)
+#define MRA_ITUR_BT601 (1 << 6)
+#define MRA_ITUR_BT656 (0 << 6)
+#define MRA_INPUT_MODE_MASK (7 << 3)
+#define MRA_PAL_BT601 (4 << 3)
+#define MRA_NTSC_BT601 (0 << 3)
+#define MRA_REGISTER_MODE (1 << 0)
+
+/* Mode Register C register bits */
+#define MRC_AUTOSELECT (1 << 7)
+
+/* Luminance Control register bits */
+#define LUMC_ONOFF_SHIFT 7
+#define LUMC_ONOFF_MASK (1 << 7)
+
+/* Contrast level control register bits */
+#define CLC_CONTRAST_ONOFF (1 << 7)
+#define CLC_CONTRAST_MASK 0x0F
+
+/* Sync separation level register bits */
+#define SSEPL_LUMINANCE_ONOFF (1 << 7)
+#define SSEPL_LUMINANCE_MASK 0x7F
+
+/* Chrominance Control A register bits */
+#define CHRCA_MODE_SHIFT 6
+#define CHRCA_MODE_MASK (1 << 6)
+
+/* ACC Loop filter & Chrominance control register bits */
+#define ACCC_CHROMA_CR_SHIFT 3
+#define ACCC_CHROMA_CR_MASK (7 << 3)
+#define ACCC_CHROMA_CB_SHIFT 0
+#define ACCC_CHROMA_CB_MASK (7 << 0)
+
+/* ACC Reference level control register bits */
+#define ACCRC_CHROMA_MASK 0xfc
+#define ACCRC_CHROMA_SHIFT 2
+
+/* ADC Register 2 register bits */
+#define ADC2_CLAMP_VOLTAGE_MASK (7 << 1)
+#define ADC2_CLAMP_VOLTAGE(n) ((n & 7) << 1)
+
+/* PLL Register 1 register bits */
+#define PLLR1_FIXED_CLOCK (1 << 7)
+
+/* STATUS Register register bits */
+#define STATUS_HLOCK_DETECT (1 << 3)
+#define STATUS_NTSCPAL (1 << 2)
+
+struct ml86v7667_priv {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ v4l2_std_id std;
+};
+
+static inline struct ml86v7667_priv *to_ml86v7667(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct ml86v7667_priv, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ml86v7667_priv, hdl)->sd;
+}
+
+static int ml86v7667_mask_set(struct i2c_client *client, const u8 reg,
+ const u8 mask, const u8 data)
+{
+ int val = i2c_smbus_read_byte_data(client, reg);
+ if (val < 0)
+ return val;
+
+ val = (val & ~mask) | (data & mask);
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int ml86v7667_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ret = ml86v7667_mask_set(client, SSEPL_REG,
+ SSEPL_LUMINANCE_MASK, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ ret = ml86v7667_mask_set(client, CLC_REG,
+ CLC_CONTRAST_MASK, ctrl->val);
+ break;
+ case V4L2_CID_CHROMA_GAIN:
+ ret = ml86v7667_mask_set(client, ACCRC_REG, ACCRC_CHROMA_MASK,
+ ctrl->val << ACCRC_CHROMA_SHIFT);
+ break;
+ case V4L2_CID_HUE:
+ ret = ml86v7667_mask_set(client, HUE_REG, ~0, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ ret = ml86v7667_mask_set(client, ACCC_REG,
+ ACCC_CHROMA_CR_MASK,
+ ctrl->val << ACCC_CHROMA_CR_SHIFT);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ ret = ml86v7667_mask_set(client, ACCC_REG,
+ ACCC_CHROMA_CB_MASK,
+ ctrl->val << ACCC_CHROMA_CB_SHIFT);
+ break;
+ case V4L2_CID_SHARPNESS:
+ ret = ml86v7667_mask_set(client, LUMC_REG,
+ LUMC_ONOFF_MASK,
+ ctrl->val << LUMC_ONOFF_SHIFT);
+ break;
+ case V4L2_CID_COLOR_KILLER:
+ ret = ml86v7667_mask_set(client, CHRCA_REG,
+ CHRCA_MODE_MASK,
+ ctrl->val << CHRCA_MODE_SHIFT);
+ break;
+ }
+
+ return 0;
+}
+
+static int ml86v7667_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int status;
+
+ status = i2c_smbus_read_byte_data(client, STATUS_REG);
+ if (status < 0)
+ return status;
+
+ if (status & STATUS_HLOCK_DETECT)
+ *std &= status & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60;
+ else
+ *std = V4L2_STD_UNKNOWN;
+
+ return 0;
+}
+
+static int ml86v7667_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int status_reg;
+
+ status_reg = i2c_smbus_read_byte_data(client, STATUS_REG);
+ if (status_reg < 0)
+ return status_reg;
+
+ *status = status_reg & STATUS_HLOCK_DETECT ? 0 : V4L2_IN_ST_NO_SIGNAL;
+
+ return 0;
+}
+
+static int ml86v7667_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV8_2X8;
+
+ return 0;
+}
+
+static int ml86v7667_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct ml86v7667_priv *priv = to_ml86v7667(sd);
+
+ fmt->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->width = 720;
+ fmt->height = priv->std & V4L2_STD_525_60 ? 480 : 576;
+
+ return 0;
+}
+
+static int ml86v7667_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_BT656;
+
+ return 0;
+}
+
+static int ml86v7667_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct ml86v7667_priv *priv = to_ml86v7667(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+ int ret;
+ u8 mode;
+
+ /* PAL/NTSC ITU-R BT.601 input mode */
+ mode = std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601;
+ ret = ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, mode);
+ if (ret < 0)
+ return ret;
+
+ priv->std = std;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ml86v7667_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, (u8)reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ reg->size = sizeof(u8);
+
+ return 0;
+}
+
+static int ml86v7667_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, (u8)reg->reg, (u8)reg->val);
+}
+#endif
+
+static const struct v4l2_ctrl_ops ml86v7667_ctrl_ops = {
+ .s_ctrl = ml86v7667_s_ctrl,
+};
+
+static struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
+ .querystd = ml86v7667_querystd,
+ .g_input_status = ml86v7667_g_input_status,
+ .enum_mbus_fmt = ml86v7667_enum_mbus_fmt,
+ .try_mbus_fmt = ml86v7667_mbus_fmt,
+ .g_mbus_fmt = ml86v7667_mbus_fmt,
+ .s_mbus_fmt = ml86v7667_mbus_fmt,
+ .g_mbus_config = ml86v7667_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = {
+ .s_std = ml86v7667_s_std,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ml86v7667_g_register,
+ .s_register = ml86v7667_s_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ml86v7667_subdev_ops = {
+ .core = &ml86v7667_subdev_core_ops,
+ .video = &ml86v7667_subdev_video_ops,
+};
+
+static int ml86v7667_init(struct ml86v7667_priv *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+ int val;
+ int ret;
+
+ /* BT.656-4 output mode, register mode */
+ ret = ml86v7667_mask_set(client, MRA_REG,
+ MRA_OUTPUT_MODE_MASK | MRA_REGISTER_MODE,
+ MRA_ITUR_BT656 | MRA_REGISTER_MODE);
+
+ /* PLL circuit fixed clock, 32MHz */
+ ret |= ml86v7667_mask_set(client, PLLR1_REG, PLLR1_FIXED_CLOCK,
+ PLLR1_FIXED_CLOCK);
+
+ /* ADC2 clamping voltage maximum */
+ ret |= ml86v7667_mask_set(client, ADC2_REG, ADC2_CLAMP_VOLTAGE_MASK,
+ ADC2_CLAMP_VOLTAGE(7));
+
+ /* enable luminance function */
+ ret |= ml86v7667_mask_set(client, SSEPL_REG, SSEPL_LUMINANCE_ONOFF,
+ SSEPL_LUMINANCE_ONOFF);
+
+ /* enable contrast function */
+ ret |= ml86v7667_mask_set(client, CLC_REG, CLC_CONTRAST_ONOFF, 0);
+
+ /*
+ * PAL/NTSC autodetection is enabled after reset,
+ * set the autodetected std in manual std mode and
+ * disable autodetection
+ */
+ val = i2c_smbus_read_byte_data(client, STATUS_REG);
+ if (val < 0)
+ return val;
+
+ priv->std = val & STATUS_NTSCPAL ? V4L2_STD_625_50 : V4L2_STD_525_60;
+ ret |= ml86v7667_mask_set(client, MRC_REG, MRC_AUTOSELECT, 0);
+
+ val = priv->std & V4L2_STD_525_60 ? MRA_NTSC_BT601 : MRA_PAL_BT601;
+ ret |= ml86v7667_mask_set(client, MRA_REG, MRA_INPUT_MODE_MASK, val);
+
+ return ret;
+}
+
+static int ml86v7667_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ml86v7667_priv *priv;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&priv->sd, client, &ml86v7667_subdev_ops);
+
+ v4l2_ctrl_handler_init(&priv->hdl, 8);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -64, 63, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_CONTRAST, -8, 7, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_CHROMA_GAIN, -32, 31, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_RED_BALANCE, -4, 3, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, -4, 3, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ml86v7667_ctrl_ops,
+ V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
+ priv->sd.ctrl_handler = &priv->hdl;
+
+ ret = priv->hdl.error;
+ if (ret)
+ goto cleanup;
+
+ v4l2_ctrl_handler_setup(&priv->hdl);
+
+ ret = ml86v7667_init(priv);
+ if (ret)
+ goto cleanup;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr, client->adapter->name);
+ return 0;
+
+cleanup:
+ v4l2_ctrl_handler_free(&priv->hdl);
+ v4l2_device_unregister_subdev(&priv->sd);
+ v4l_err(client, "failed to probe @ 0x%02x (%s)\n",
+ client->addr, client->adapter->name);
+ return ret;
+}
+
+static int ml86v7667_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ml86v7667_priv *priv = to_ml86v7667(sd);
+
+ v4l2_ctrl_handler_free(&priv->hdl);
+ v4l2_device_unregister_subdev(&priv->sd);
+
+ return 0;
+}
+
+static const struct i2c_device_id ml86v7667_id[] = {
+ {DRV_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ml86v7667_id);
+
+static struct i2c_driver ml86v7667_i2c_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ml86v7667_probe,
+ .remove = ml86v7667_remove,
+ .id_table = ml86v7667_id,
+};
+
+module_i2c_driver(ml86v7667_i2c_driver);
+
+MODULE_DESCRIPTION("OKI Semiconductor ML86V7667 video decoder driver");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index 54a9dd3..8190fec 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -570,15 +570,6 @@
return 0;
}
-static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->ident,
- (state->rev1 << 16) | state->rev2);
-}
-
static int msp_log_status(struct v4l2_subdev *sd)
{
struct msp_state *state = to_state(sd);
@@ -651,7 +642,6 @@
static const struct v4l2_subdev_core_ops msp_core_ops = {
.log_status = msp_log_status,
- .g_chip_ident = msp_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -707,7 +697,7 @@
return -ENODEV;
}
- state = kzalloc(sizeof(*state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
@@ -732,7 +722,6 @@
if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
v4l_dbg(1, msp_debug, client,
"not an msp3400 (cannot read chip version)\n");
- kfree(state);
return -ENODEV;
}
@@ -827,7 +816,6 @@
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(state);
return err;
}
@@ -889,7 +877,6 @@
msp_reset(client);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c
index 8edb3d8..846b15f 100644
--- a/drivers/media/i2c/mt9m032.c
+++ b/drivers/media/i2c/mt9m032.c
@@ -554,10 +554,8 @@
struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
int val;
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
val = mt9m032_read(client, reg->reg);
if (val < 0)
@@ -575,12 +573,9 @@
struct mt9m032 *sensor = to_mt9m032(sd);
struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
return mt9m032_write(client, reg->reg, reg->val);
}
#endif
@@ -730,7 +725,7 @@
if (!client->dev.platform_data)
return -ENODEV;
- sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (sensor == NULL)
return -ENOMEM;
@@ -860,7 +855,6 @@
v4l2_ctrl_handler_free(&sensor->ctrls);
error_sensor:
mutex_destroy(&sensor->lock);
- kfree(sensor);
return ret;
}
@@ -873,7 +867,6 @@
v4l2_ctrl_handler_free(&sensor->ctrls);
media_entity_cleanup(&subdev->entity);
mutex_destroy(&sensor->lock);
- kfree(sensor);
return 0;
}
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 28cf95b..4734836 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -16,18 +16,19 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>
-#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/mt9p031.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
#include "aptina-pll.h"
@@ -124,9 +125,7 @@
int power_count;
struct clk *clk;
- struct regulator *vaa;
- struct regulator *vdd;
- struct regulator *vdd_io;
+ struct regulator_bulk_data regulators[3];
enum mt9p031_model model;
struct aptina_pll pll;
@@ -271,23 +270,26 @@
static int mt9p031_power_on(struct mt9p031 *mt9p031)
{
+ int ret;
+
/* Ensure RESET_BAR is low */
- if (mt9p031->reset != -1) {
+ if (gpio_is_valid(mt9p031->reset)) {
gpio_set_value(mt9p031->reset, 0);
usleep_range(1000, 2000);
}
/* Bring up the supplies */
- regulator_enable(mt9p031->vdd);
- regulator_enable(mt9p031->vdd_io);
- regulator_enable(mt9p031->vaa);
+ ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators),
+ mt9p031->regulators);
+ if (ret < 0)
+ return ret;
/* Emable clock */
if (mt9p031->clk)
clk_prepare_enable(mt9p031->clk);
/* Now RESET_BAR must be high */
- if (mt9p031->reset != -1) {
+ if (gpio_is_valid(mt9p031->reset)) {
gpio_set_value(mt9p031->reset, 1);
usleep_range(1000, 2000);
}
@@ -297,14 +299,13 @@
static void mt9p031_power_off(struct mt9p031 *mt9p031)
{
- if (mt9p031->reset != -1) {
+ if (gpio_is_valid(mt9p031->reset)) {
gpio_set_value(mt9p031->reset, 0);
usleep_range(1000, 2000);
}
- regulator_disable(mt9p031->vaa);
- regulator_disable(mt9p031->vdd_io);
- regulator_disable(mt9p031->vdd);
+ regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators),
+ mt9p031->regulators);
if (mt9p031->clk)
clk_disable_unprepare(mt9p031->clk);
@@ -849,18 +850,18 @@
/* Read out the chip version register */
data = mt9p031_read(client, MT9P031_CHIP_VERSION);
+ mt9p031_power_off(mt9p031);
+
if (data != MT9P031_CHIP_VERSION_VALUE) {
dev_err(&client->dev, "MT9P031 not detected, wrong version "
"0x%04x\n", data);
return -ENODEV;
}
- mt9p031_power_off(mt9p031);
-
dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
client->addr);
- return ret;
+ return 0;
}
static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
@@ -928,10 +929,36 @@
* Driver initialization and probing
*/
+static struct mt9p031_platform_data *
+mt9p031_get_pdata(struct i2c_client *client)
+{
+ struct mt9p031_platform_data *pdata;
+ struct device_node *np;
+
+ if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
+ return client->dev.platform_data;
+
+ np = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
+ if (!np)
+ return NULL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ goto done;
+
+ pdata->reset = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
+ of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq);
+ of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq);
+
+done:
+ of_node_put(np);
+ return pdata;
+}
+
static int mt9p031_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
- struct mt9p031_platform_data *pdata = client->dev.platform_data;
+ struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct mt9p031 *mt9p031;
unsigned int i;
@@ -958,14 +985,14 @@
mt9p031->model = did->driver_data;
mt9p031->reset = -1;
- mt9p031->vaa = devm_regulator_get(&client->dev, "vaa");
- mt9p031->vdd = devm_regulator_get(&client->dev, "vdd");
- mt9p031->vdd_io = devm_regulator_get(&client->dev, "vdd_io");
+ mt9p031->regulators[0].supply = "vdd";
+ mt9p031->regulators[1].supply = "vdd_io";
+ mt9p031->regulators[2].supply = "vaa";
- if (IS_ERR(mt9p031->vaa) || IS_ERR(mt9p031->vdd) ||
- IS_ERR(mt9p031->vdd_io)) {
+ ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators);
+ if (ret < 0) {
dev_err(&client->dev, "Unable to get regulators\n");
- return -ENODEV;
+ return ret;
}
v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6);
@@ -1031,7 +1058,7 @@
mt9p031->format.field = V4L2_FIELD_NONE;
mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
- if (pdata->reset != -1) {
+ if (gpio_is_valid(pdata->reset)) {
ret = devm_gpio_request_one(&client->dev, pdata->reset,
GPIOF_OUT_INIT_LOW, "mt9p031_rst");
if (ret < 0)
@@ -1070,8 +1097,18 @@
};
MODULE_DEVICE_TABLE(i2c, mt9p031_id);
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id mt9p031_of_match[] = {
+ { .compatible = "aptina,mt9p031", },
+ { .compatible = "aptina,mt9p031m", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mt9p031_of_match);
+#endif
+
static struct i2c_driver mt9p031_i2c_driver = {
.driver = {
+ .of_match_table = of_match_ptr(mt9p031_of_match),
.name = "mt9p031",
},
.probe = mt9p031_probe,
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index 2e189d8..7964634 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -740,7 +740,7 @@
if (ret < 0)
return ret;
- mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL);
+ mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL);
if (!mt9t001)
return -ENOMEM;
@@ -801,7 +801,6 @@
if (ret < 0) {
v4l2_ctrl_handler_free(&mt9t001->ctrls);
media_entity_cleanup(&mt9t001->subdev.entity);
- kfree(mt9t001);
}
return ret;
@@ -815,7 +814,6 @@
v4l2_ctrl_handler_free(&mt9t001->ctrls);
v4l2_device_unregister_subdev(subdev);
media_entity_cleanup(&subdev->entity);
- kfree(mt9t001);
return 0;
}
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index 3f415fd..f74698c 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <asm/div64.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/mt9v011.h>
@@ -407,13 +406,6 @@
static int mt9v011_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
reg->val = mt9v011_read(sd, reg->reg & 0xff);
reg->size = 2;
@@ -423,31 +415,12 @@
static int mt9v011_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff);
return 0;
}
#endif
-static int mt9v011_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- u16 version;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011,
- version);
-}
-
static int mt9v011_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9v011 *core =
@@ -489,7 +462,6 @@
static const struct v4l2_subdev_core_ops mt9v011_core_ops = {
.reset = mt9v011_reset,
- .g_chip_ident = mt9v011_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v011_g_register,
.s_register = mt9v011_s_register,
@@ -526,7 +498,7 @@
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
- core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);
+ core = devm_kzalloc(&c->dev, sizeof(struct mt9v011), GFP_KERNEL);
if (!core)
return -ENOMEM;
@@ -539,7 +511,6 @@
(version != MT9V011_REV_B_VERSION)) {
v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",
version);
- kfree(core);
return -EINVAL;
}
@@ -562,7 +533,6 @@
v4l2_err(sd, "control initialization error %d\n", ret);
v4l2_ctrl_handler_free(&core->ctrls);
- kfree(core);
return ret;
}
core->sd.ctrl_handler = &core->ctrls;
@@ -598,7 +568,7 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&core->ctrls);
- kfree(to_mt9v011(sd));
+
return 0;
}
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 3f356cb..60c6f67 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -744,7 +744,7 @@
return -EIO;
}
- mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL);
+ mt9v032 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL);
if (!mt9v032)
return -ENOMEM;
@@ -830,8 +830,9 @@
mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0);
+
if (ret < 0)
- kfree(mt9v032);
+ v4l2_ctrl_handler_free(&mt9v032->ctrls);
return ret;
}
@@ -841,9 +842,10 @@
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ v4l2_ctrl_handler_free(&mt9v032->ctrls);
v4l2_device_unregister_subdev(subdev);
media_entity_cleanup(&subdev->entity);
- kfree(mt9v032);
+
return 0;
}
diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c
index 8554b47..271d0b7 100644
--- a/drivers/media/i2c/noon010pc30.c
+++ b/drivers/media/i2c/noon010pc30.c
@@ -19,7 +19,6 @@
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <media/noon010pc30.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/v4l2-ctrls.h>
@@ -712,7 +711,7 @@
return -EIO;
}
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -746,57 +745,50 @@
info->curr_win = &noon010_sizes[0];
if (gpio_is_valid(pdata->gpio_nreset)) {
- ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST");
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_nreset,
+ GPIOF_OUT_INIT_LOW,
+ "NOON010PC30 NRST");
if (ret) {
dev_err(&client->dev, "GPIO request error: %d\n", ret);
goto np_err;
}
info->gpio_nreset = pdata->gpio_nreset;
- gpio_direction_output(info->gpio_nreset, 0);
gpio_export(info->gpio_nreset, 0);
}
if (gpio_is_valid(pdata->gpio_nstby)) {
- ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY");
+ ret = devm_gpio_request_one(&client->dev, pdata->gpio_nstby,
+ GPIOF_OUT_INIT_LOW,
+ "NOON010PC30 NSTBY");
if (ret) {
dev_err(&client->dev, "GPIO request error: %d\n", ret);
- goto np_gpio_err;
+ goto np_err;
}
info->gpio_nstby = pdata->gpio_nstby;
- gpio_direction_output(info->gpio_nstby, 0);
gpio_export(info->gpio_nstby, 0);
}
for (i = 0; i < NOON010_NUM_SUPPLIES; i++)
info->supply[i].supply = noon010_supply_name[i];
- ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES,
+ ret = devm_regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES,
info->supply);
if (ret)
- goto np_reg_err;
+ goto np_err;
info->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
if (ret < 0)
- goto np_me_err;
+ goto np_err;
ret = noon010_detect(client, info);
if (!ret)
return 0;
-np_me_err:
- regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
-np_reg_err:
- if (gpio_is_valid(info->gpio_nstby))
- gpio_free(info->gpio_nstby);
-np_gpio_err:
- if (gpio_is_valid(info->gpio_nreset))
- gpio_free(info->gpio_nreset);
np_err:
v4l2_ctrl_handler_free(&info->hdl);
v4l2_device_unregister_subdev(sd);
- kfree(info);
return ret;
}
@@ -807,17 +799,8 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&info->hdl);
-
- regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply);
-
- if (gpio_is_valid(info->gpio_nreset))
- gpio_free(info->gpio_nreset);
-
- if (gpio_is_valid(info->gpio_nstby))
- gpio_free(info->gpio_nstby);
-
media_entity_cleanup(&sd->entity);
- kfree(info);
+
return 0;
}
diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c
index b0cc927..faa64ba 100644
--- a/drivers/media/i2c/ov7640.c
+++ b/drivers/media/i2c/ov7640.c
@@ -20,7 +20,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/slab.h>
MODULE_DESCRIPTION("OmniVision ov7640 sensor driver");
@@ -59,7 +58,7 @@
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &ov7640_ops);
@@ -71,7 +70,6 @@
if (write_regs(client, initial_registers) < 0) {
v4l_err(client, "error initializing OV7640\n");
- kfree(sd);
return -ENODEV;
}
@@ -84,7 +82,7 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
+
return 0;
}
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 617ad3f..e8a1ce2 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -17,7 +17,6 @@
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mediabus.h>
#include <media/ov7670.h>
@@ -1462,25 +1461,12 @@
.g_volatile_ctrl = ov7670_g_volatile_ctrl,
};
-static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char val = 0;
int ret;
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
ret = ov7670_read(sd, reg->reg & 0xff, &val);
reg->val = val;
reg->size = 1;
@@ -1489,12 +1475,6 @@
static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
@@ -1503,7 +1483,6 @@
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops ov7670_core_ops = {
- .g_chip_ident = ov7670_g_chip_ident,
.reset = ov7670_reset,
.init = ov7670_init,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1552,7 +1531,7 @@
struct ov7670_info *info;
int ret;
- info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
sd = &info->sd;
@@ -1590,7 +1569,6 @@
v4l_dbg(1, debug, client,
"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
client->addr << 1, client->adapter->name);
- kfree(info);
return ret;
}
v4l_info(client, "chip found @ 0x%02x (%s)\n",
@@ -1635,7 +1613,6 @@
int err = info->hdl.error;
v4l2_ctrl_handler_free(&info->hdl);
- kfree(info);
return err;
}
/*
@@ -1659,7 +1636,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&info->hdl);
- kfree(info);
return 0;
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 9eac531..825ea86 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -1385,9 +1385,12 @@
}
return 0;
err:
- for (++i; i < S5C73M3_MAX_SUPPLIES; i++)
- regulator_enable(state->supplies[i].consumer);
-
+ for (++i; i < S5C73M3_MAX_SUPPLIES; i++) {
+ int r = regulator_enable(state->supplies[i].consumer);
+ if (r < 0)
+ v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n",
+ state->supplies[i].supply, r);
+ }
return ret;
}
@@ -1511,59 +1514,40 @@
.video = &s5c73m3_oif_video_ops,
};
-static int s5c73m3_configure_gpio(int nr, int val, const char *name)
-{
- unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
- int ret;
-
- if (!gpio_is_valid(nr))
- return 0;
- ret = gpio_request_one(nr, flags, name);
- if (!ret)
- gpio_export(nr, 0);
- return ret;
-}
-
-static int s5c73m3_free_gpios(struct s5c73m3 *state)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(state->gpio); i++) {
- if (!gpio_is_valid(state->gpio[i].gpio))
- continue;
- gpio_free(state->gpio[i].gpio);
- state->gpio[i].gpio = -EINVAL;
- }
- return 0;
-}
-
static int s5c73m3_configure_gpios(struct s5c73m3 *state,
const struct s5c73m3_platform_data *pdata)
{
- const struct s5c73m3_gpio *gpio = &pdata->gpio_stby;
+ struct device *dev = &state->i2c_client->dev;
+ const struct s5c73m3_gpio *gpio;
+ unsigned long flags;
int ret;
state->gpio[STBY].gpio = -EINVAL;
state->gpio[RST].gpio = -EINVAL;
- ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_STBY");
- if (ret) {
- s5c73m3_free_gpios(state);
- return ret;
+ gpio = &pdata->gpio_stby;
+ if (gpio_is_valid(gpio->gpio)) {
+ flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
+ | GPIOF_EXPORT;
+ ret = devm_gpio_request_one(dev, gpio->gpio, flags,
+ "S5C73M3_STBY");
+ if (ret < 0)
+ return ret;
+
+ state->gpio[STBY] = *gpio;
}
- state->gpio[STBY] = *gpio;
- if (gpio_is_valid(gpio->gpio))
- gpio_set_value(gpio->gpio, 0);
gpio = &pdata->gpio_reset;
- ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_RST");
- if (ret) {
- s5c73m3_free_gpios(state);
- return ret;
+ if (gpio_is_valid(gpio->gpio)) {
+ flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
+ | GPIOF_EXPORT;
+ ret = devm_gpio_request_one(dev, gpio->gpio, flags,
+ "S5C73M3_RST");
+ if (ret < 0)
+ return ret;
+
+ state->gpio[RST] = *gpio;
}
- state->gpio[RST] = *gpio;
- if (gpio_is_valid(gpio->gpio))
- gpio_set_value(gpio->gpio, 0);
return 0;
}
@@ -1626,10 +1610,11 @@
state->mclk_frequency = pdata->mclk_frequency;
state->bus_type = pdata->bus_type;
+ state->i2c_client = client;
ret = s5c73m3_configure_gpios(state, pdata);
if (ret)
- goto out_err1;
+ goto out_err;
for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++)
state->supplies[i].supply = s5c73m3_supply_names[i];
@@ -1638,12 +1623,12 @@
state->supplies);
if (ret) {
dev_err(dev, "failed to get regulators\n");
- goto out_err2;
+ goto out_err;
}
ret = s5c73m3_init_controls(state);
if (ret)
- goto out_err2;
+ goto out_err;
state->sensor_pix_size[RES_ISP] = &s5c73m3_isp_resolutions[1];
state->sensor_pix_size[RES_JPEG] = &s5c73m3_jpeg_resolutions[1];
@@ -1659,16 +1644,12 @@
ret = s5c73m3_register_spi_driver(state);
if (ret < 0)
- goto out_err2;
-
- state->i2c_client = client;
+ goto out_err;
v4l2_info(sd, "%s: completed succesfully\n", __func__);
return 0;
-out_err2:
- s5c73m3_free_gpios(state);
-out_err1:
+out_err:
media_entity_cleanup(&sd->entity);
return ret;
}
@@ -1688,7 +1669,6 @@
media_entity_cleanup(&sensor_sd->entity);
s5c73m3_unregister_spi_driver(state);
- s5c73m3_free_gpios(state);
return 0;
}
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index 6f3a9c0..8079e26 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -73,7 +73,7 @@
memset(padding, 0, sizeof(padding));
- for (i = 0; i < count ; i++) {
+ for (i = 0; i < count; i++) {
r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX);
if (r < 0)
return r;
@@ -98,7 +98,7 @@
unsigned int i, j = 0;
int r = 0;
- for (i = 0; i < count ; i++) {
+ for (i = 0; i < count; i++) {
r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX);
if (r < 0)
return r;
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index bdf5e3d..789c02a 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/drivers/media/i2c/s5k6aa.c
@@ -1491,58 +1491,41 @@
/*
* GPIO setup
*/
-static int s5k6aa_configure_gpio(int nr, int val, const char *name)
-{
- unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
- int ret;
-
- if (!gpio_is_valid(nr))
- return 0;
- ret = gpio_request_one(nr, flags, name);
- if (!ret)
- gpio_export(nr, 0);
- return ret;
-}
-
-static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
- if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
- continue;
- gpio_free(s5k6aa->gpio[i].gpio);
- s5k6aa->gpio[i].gpio = -EINVAL;
- }
-}
static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
const struct s5k6aa_platform_data *pdata)
{
- const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ const struct s5k6aa_gpio *gpio;
+ unsigned long flags;
int ret;
s5k6aa->gpio[STBY].gpio = -EINVAL;
s5k6aa->gpio[RST].gpio = -EINVAL;
- ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
- if (ret) {
- s5k6aa_free_gpios(s5k6aa);
- return ret;
+ gpio = &pdata->gpio_stby;
+ if (gpio_is_valid(gpio->gpio)) {
+ flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
+ | GPIOF_EXPORT;
+ ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags,
+ "S5K6AA_STBY");
+ if (ret < 0)
+ return ret;
+
+ s5k6aa->gpio[STBY] = *gpio;
}
- s5k6aa->gpio[STBY] = *gpio;
- if (gpio_is_valid(gpio->gpio))
- gpio_set_value(gpio->gpio, 0);
gpio = &pdata->gpio_reset;
- ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
- if (ret) {
- s5k6aa_free_gpios(s5k6aa);
- return ret;
+ if (gpio_is_valid(gpio->gpio)) {
+ flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW)
+ | GPIOF_EXPORT;
+ ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags,
+ "S5K6AA_RST");
+ if (ret < 0)
+ return ret;
+
+ s5k6aa->gpio[RST] = *gpio;
}
- s5k6aa->gpio[RST] = *gpio;
- if (gpio_is_valid(gpio->gpio))
- gpio_set_value(gpio->gpio, 0);
return 0;
}
@@ -1593,7 +1576,7 @@
ret = s5k6aa_configure_gpios(s5k6aa, pdata);
if (ret)
- goto out_err2;
+ goto out_err;
for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
@@ -1602,12 +1585,12 @@
s5k6aa->supplies);
if (ret) {
dev_err(&client->dev, "Failed to get regulators\n");
- goto out_err3;
+ goto out_err;
}
ret = s5k6aa_initialize_ctrls(s5k6aa);
if (ret)
- goto out_err3;
+ goto out_err;
s5k6aa_presets_data_init(s5k6aa);
@@ -1618,9 +1601,7 @@
return 0;
-out_err3:
- s5k6aa_free_gpios(s5k6aa);
-out_err2:
+out_err:
media_entity_cleanup(&s5k6aa->sd.entity);
return ret;
}
@@ -1628,12 +1609,10 @@
static int s5k6aa_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
media_entity_cleanup(&sd->entity);
- s5k6aa_free_gpios(s5k6aa);
return 0;
}
diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c
index b4e1ccb..70bc72e 100644
--- a/drivers/media/i2c/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
@@ -33,7 +33,6 @@
#include <media/saa6588.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
/* insmod options */
@@ -443,17 +442,9 @@
return 0;
}
-static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops saa6588_core_ops = {
- .g_chip_ident = saa6588_g_chip_ident,
.ioctl = saa6588_ioctl,
};
@@ -478,17 +469,15 @@
v4l_info(client, "saa6588 found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- s = kzalloc(sizeof(*s), GFP_KERNEL);
+ s = devm_kzalloc(&client->dev, sizeof(*s), GFP_KERNEL);
if (s == NULL)
return -ENOMEM;
s->buf_size = bufblocks * 3;
- s->buffer = kmalloc(s->buf_size, GFP_KERNEL);
- if (s->buffer == NULL) {
- kfree(s);
+ s->buffer = devm_kzalloc(&client->dev, s->buf_size, GFP_KERNEL);
+ if (s->buffer == NULL)
return -ENOMEM;
- }
sd = &s->sd;
v4l2_i2c_subdev_init(sd, client, &saa6588_ops);
spin_lock_init(&s->lock);
@@ -516,8 +505,6 @@
cancel_delayed_work_sync(&s->work);
- kfree(s->buffer);
- kfree(s);
return 0;
}
diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c
index 51cd4c8..ac43e92 100644
--- a/drivers/media/i2c/saa7110.c
+++ b/drivers/media/i2c/saa7110.c
@@ -35,7 +35,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
@@ -203,7 +202,7 @@
status = saa7110_read(sd);
if (status & 0x40) {
v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status);
- return decoder->norm; /* no change*/
+ return V4L2_STD_UNKNOWN;
}
if ((status & 3) == 0) {
saa7110_write(sd, 0x06, 0x83);
@@ -265,7 +264,7 @@
static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
- *(v4l2_std_id *)std = determine_norm(sd);
+ *std &= determine_norm(sd);
return 0;
}
@@ -352,13 +351,6 @@
return 0;
}
-static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops saa7110_ctrl_ops = {
@@ -366,7 +358,6 @@
};
static const struct v4l2_subdev_core_ops saa7110_core_ops = {
- .g_chip_ident = saa7110_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -406,7 +397,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL);
+ decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (!decoder)
return -ENOMEM;
sd = &decoder->sd;
@@ -428,7 +419,6 @@
int err = decoder->hdl.error;
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
return err;
}
v4l2_ctrl_handler_setup(&decoder->hdl);
@@ -469,7 +459,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
return 0;
}
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 52c717d..7fd766e 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -46,7 +46,6 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <media/saa7115.h>
#include <asm/div64.h>
@@ -63,6 +62,16 @@
MODULE_PARM_DESC(debug, "Debug level (0-1)");
+enum saa711x_model {
+ SAA7111A,
+ SAA7111,
+ SAA7113,
+ GM7113C,
+ SAA7114,
+ SAA7115,
+ SAA7118,
+};
+
struct saa711x_state {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
@@ -80,7 +89,7 @@
int radio;
int width;
int height;
- u32 ident;
+ enum saa711x_model ident;
u32 audclk_freq;
u32 crystal_freq;
bool ucgc;
@@ -111,10 +120,10 @@
/* Sanity routine to check if a register is present */
static int saa711x_has_reg(const int id, const u8 reg)
{
- if (id == V4L2_IDENT_SAA7111)
+ if (id == SAA7111)
return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
(reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
- if (id == V4L2_IDENT_SAA7111A)
+ if (id == SAA7111A)
return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
reg != 0x14 && reg != 0x18 && reg != 0x19 &&
reg != 0x1d && reg != 0x1e;
@@ -127,16 +136,18 @@
return 0;
switch (id) {
- case V4L2_IDENT_SAA7113:
+ case GM7113C:
+ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && reg < 0x20;
+ case SAA7113:
return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
reg != 0x5d && reg < 0x63;
- case V4L2_IDENT_SAA7114:
+ case SAA7114:
return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
(reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
reg != 0x81 && reg < 0xf0;
- case V4L2_IDENT_SAA7115:
+ case SAA7115:
return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
- case V4L2_IDENT_SAA7118:
+ case SAA7118:
return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
(reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
(reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
@@ -214,7 +225,10 @@
0x00, 0x00
};
-/* SAA7113 init codes */
+/* SAA7113/GM7113C init codes
+ * It's important that R_14... R_17 == 0x00
+ * for the gm7113c chip to deliver stable video
+ */
static const unsigned char saa7113_init[] = {
R_01_INC_DELAY, 0x08,
R_02_INPUT_CNTL_1, 0xc2,
@@ -448,6 +462,24 @@
/* ============== SAA7715 VIDEO templates (end) ======= */
+/* ============== GM7113C VIDEO templates ============= */
+static const unsigned char gm7113c_cfg_60hz_video[] = {
+ R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
+ R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
+
+ 0x00, 0x00
+};
+
+static const unsigned char gm7113c_cfg_50hz_video[] = {
+ R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
+ R_0E_CHROMA_CNTL_1, 0x07,
+
+ 0x00, 0x00
+};
+
+/* ============== GM7113C VIDEO templates (end) ======= */
+
+
static const unsigned char saa7115_cfg_vbi_on[] = {
R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
@@ -932,11 +964,17 @@
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
if (std & V4L2_STD_525_60) {
v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
- saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+ if (state->ident == GM7113C)
+ saa711x_writeregs(sd, gm7113c_cfg_60hz_video);
+ else
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
saa711x_set_size(sd, 720, 480);
} else {
v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
- saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+ if (state->ident == GM7113C)
+ saa711x_writeregs(sd, gm7113c_cfg_50hz_video);
+ else
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
saa711x_set_size(sd, 720, 576);
}
@@ -949,7 +987,8 @@
011 NTSC N (3.58MHz) PAL M (3.58MHz)
100 reserved NTSC-Japan (3.58MHz)
*/
- if (state->ident <= V4L2_IDENT_SAA7113) {
+ if (state->ident <= SAA7113 ||
+ state->ident == GM7113C) {
u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
@@ -968,9 +1007,8 @@
/* restart task B if needed */
int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
- if (taskb && state->ident == V4L2_IDENT_SAA7114) {
+ if (taskb && state->ident == SAA7114)
saa711x_writeregs(sd, saa7115_cfg_vbi_on);
- }
/* switch audio mode too! */
saa711x_s_clock_freq(sd, state->audclk_freq);
@@ -992,7 +1030,7 @@
#else
/* SAA7113 and SAA7118 also should support VBI - Need testing */
- if (state->ident != V4L2_IDENT_SAA7115)
+ if (state->ident != SAA7115)
return;
#endif
@@ -1214,13 +1252,14 @@
u32 input, u32 output, u32 config)
{
struct saa711x_state *state = to_state(sd);
- u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
+ u8 mask = (state->ident <= SAA7111A) ? 0xf8 : 0xf0;
v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
input, output);
/* saa7111/3 does not have these inputs */
- if (state->ident <= V4L2_IDENT_SAA7113 &&
+ if ((state->ident <= SAA7113 ||
+ state->ident == GM7113C) &&
(input == SAA7115_COMPOSITE4 ||
input == SAA7115_COMPOSITE5)) {
return -EINVAL;
@@ -1235,7 +1274,7 @@
state->input = input;
/* saa7111 has slightly different input numbering */
- if (state->ident <= V4L2_IDENT_SAA7111A) {
+ if (state->ident <= SAA7111A) {
if (input >= SAA7115_COMPOSITE4)
input -= 2;
/* saa7111 specific */
@@ -1258,13 +1297,13 @@
(state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
state->output = output;
- if (state->ident == V4L2_IDENT_SAA7114 ||
- state->ident == V4L2_IDENT_SAA7115) {
+ if (state->ident == SAA7114 ||
+ state->ident == SAA7115) {
saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
(saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
(state->output & 0x01));
}
- if (state->ident > V4L2_IDENT_SAA7111A) {
+ if (state->ident > SAA7111A) {
if (config & SAA7115_IDQ_IS_DEFAULT)
saa711x_write(sd, R_85_I_PORT_SIGNAL_POLAR, 0x20);
else
@@ -1277,7 +1316,7 @@
{
struct saa711x_state *state = to_state(sd);
- if (state->ident > V4L2_IDENT_SAA7111A)
+ if (state->ident > SAA7111A)
return -EINVAL;
saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
(val ? 0x80 : 0));
@@ -1367,7 +1406,7 @@
reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- if (state->ident == V4L2_IDENT_SAA7115) {
+ if (state->ident == SAA7115) {
reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
@@ -1389,6 +1428,7 @@
*std &= V4L2_STD_SECAM;
break;
default:
+ *std = V4L2_STD_UNKNOWN;
/* Can't detect anything */
break;
}
@@ -1397,8 +1437,10 @@
v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
/* horizontal/vertical not locked */
- if (reg1f & 0x40)
+ if (reg1f & 0x40) {
+ *std = V4L2_STD_UNKNOWN;
goto ret;
+ }
if (reg1f & 0x20)
*std &= V4L2_STD_525_60;
@@ -1418,7 +1460,7 @@
int reg1f;
*status = V4L2_IN_ST_NO_SIGNAL;
- if (state->ident == V4L2_IDENT_SAA7115)
+ if (state->ident == SAA7115)
reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
@@ -1429,12 +1471,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = saa711x_read(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
@@ -1442,25 +1478,11 @@
static int saa711x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
#endif
-static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct saa711x_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
-}
-
static int saa711x_log_status(struct v4l2_subdev *sd)
{
struct saa711x_state *state = to_state(sd);
@@ -1469,7 +1491,7 @@
int vcr;
v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
- if (state->ident != V4L2_IDENT_SAA7115) {
+ if (state->ident != SAA7115) {
/* status for the saa7114 */
reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
signalOk = (reg1f & 0xc1) == 0x81;
@@ -1520,7 +1542,6 @@
static const struct v4l2_subdev_core_ops saa711x_core_ops = {
.log_status = saa711x_log_status,
- .g_chip_ident = saa711x_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -1571,55 +1592,145 @@
.vbi = &saa711x_vbi_ops,
};
+#define CHIP_VER_SIZE 16
+
/* ----------------------------------------------------------------------- */
+/**
+ * saa711x_detect_chip - Detects the saa711x (or clone) variant
+ * @client: I2C client structure.
+ * @id: I2C device ID structure.
+ * @name: Name of the device to be filled.
+ *
+ * Detects the Philips/NXP saa711x chip, or some clone of it.
+ * if 'id' is NULL or id->driver_data is equal to 1, it auto-probes
+ * the analog demod.
+ * If the tuner is not found, it returns -ENODEV.
+ * If auto-detection is disabled and the tuner doesn't match what it was
+ * requred, it returns -EINVAL and fills 'name'.
+ * If the chip is found, it returns the chip ID and fills 'name'.
+ */
+static int saa711x_detect_chip(struct i2c_client *client,
+ const struct i2c_device_id *id,
+ char *name)
+{
+ char chip_ver[CHIP_VER_SIZE];
+ char chip_id;
+ int i;
+ int autodetect;
+
+ autodetect = !id || id->driver_data == 1;
+
+ /* Read the chip version register */
+ for (i = 0; i < CHIP_VER_SIZE; i++) {
+ i2c_smbus_write_byte_data(client, 0, i);
+ chip_ver[i] = i2c_smbus_read_byte_data(client, 0);
+ name[i] = (chip_ver[i] & 0x0f) + '0';
+ if (name[i] > '9')
+ name[i] += 'a' - '9' - 1;
+ }
+ name[i] = '\0';
+
+ /* Check if it is a Philips/NXP chip */
+ if (!memcmp(name + 1, "f711", 4)) {
+ chip_id = name[5];
+ snprintf(name, CHIP_VER_SIZE, "saa711%c", chip_id);
+
+ if (!autodetect && strcmp(name, id->name))
+ return -EINVAL;
+
+ switch (chip_id) {
+ case '1':
+ if (chip_ver[0] & 0xf0) {
+ snprintf(name, CHIP_VER_SIZE, "saa711%ca", chip_id);
+ v4l_info(client, "saa7111a variant found\n");
+ return SAA7111A;
+ }
+ return SAA7111;
+ case '3':
+ return SAA7113;
+ case '4':
+ return SAA7114;
+ case '5':
+ return SAA7115;
+ case '8':
+ return SAA7118;
+ default:
+ v4l2_info(client,
+ "WARNING: Philips/NXP chip unknown - Falling back to saa7111\n");
+ return SAA7111;
+ }
+ }
+
+ /* Check if it is a gm7113c */
+ if (!memcmp(name, "0000", 4)) {
+ chip_id = 0;
+ for (i = 0; i < 4; i++) {
+ chip_id = chip_id << 1;
+ chip_id |= (chip_ver[i] & 0x80) ? 1 : 0;
+ }
+
+ /*
+ * Note: From the datasheet, only versions 1 and 2
+ * exists. However, tests on a device labeled as:
+ * "GM7113C 1145" returned "10" on all 16 chip
+ * version (reg 0x00) reads. So, we need to also
+ * accept at least verion 0. For now, let's just
+ * assume that a device that returns "0000" for
+ * the lower nibble is a gm7113c.
+ */
+
+ strlcpy(name, "gm7113c", CHIP_VER_SIZE);
+
+ if (!autodetect && strcmp(name, id->name))
+ return -EINVAL;
+
+ v4l_dbg(1, debug, client,
+ "It seems to be a %s chip (%*ph) @ 0x%x.\n",
+ name, 16, chip_ver, client->addr << 1);
+
+ return GM7113C;
+ }
+
+ /* Chip was not discovered. Return its ID and don't bind */
+ v4l_dbg(1, debug, client, "chip %*ph @ 0x%x is unknown.\n",
+ 16, chip_ver, client->addr << 1);
+ return -ENODEV;
+}
+
static int saa711x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa711x_state *state;
struct v4l2_subdev *sd;
struct v4l2_ctrl_handler *hdl;
- int i;
- char name[17];
- char chip_id;
- int autodetect = !id || id->driver_data == 1;
+ int ident;
+ char name[CHIP_VER_SIZE + 1];
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- for (i = 0; i < 0x0f; i++) {
- i2c_smbus_write_byte_data(client, 0, i);
- name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
- if (name[i] > '9')
- name[i] += 'a' - '9' - 1;
- }
- name[i] = '\0';
-
- chip_id = name[5];
-
- /* Check whether this chip is part of the saa711x series */
- if (memcmp(name + 1, "f711", 4)) {
- v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
- client->addr << 1, name);
+ ident = saa711x_detect_chip(client, id, name);
+ if (ident == -EINVAL) {
+ /* Chip exists, but doesn't match */
+ v4l_warn(client, "found %s while %s was expected\n",
+ name, id->name);
return -ENODEV;
}
+ if (ident < 0)
+ return ident;
- /* Safety check */
- if (!autodetect && id->name[6] != chip_id) {
- v4l_warn(client, "found saa711%c while %s was expected\n",
- chip_id, id->name);
- }
- snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
- v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
- client->addr << 1, client->adapter->name);
+ strlcpy(client->name, name, sizeof(client->name));
- state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+ v4l_info(client, "%s found @ 0x%x (%s)\n", name,
+ client->addr << 1, client->adapter->name);
hdl = &state->hdl;
v4l2_ctrl_handler_init(hdl, 6);
/* add in ascending ID order */
@@ -1640,7 +1751,6 @@
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(state);
return err;
}
v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
@@ -1649,31 +1759,7 @@
state->output = SAA7115_IPORT_ON;
state->enable = 1;
state->radio = 0;
- switch (chip_id) {
- case '1':
- state->ident = V4L2_IDENT_SAA7111;
- if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
- v4l_info(client, "saa7111a variant found\n");
- state->ident = V4L2_IDENT_SAA7111A;
- }
- break;
- case '3':
- state->ident = V4L2_IDENT_SAA7113;
- break;
- case '4':
- state->ident = V4L2_IDENT_SAA7114;
- break;
- case '5':
- state->ident = V4L2_IDENT_SAA7115;
- break;
- case '8':
- state->ident = V4L2_IDENT_SAA7118;
- break;
- default:
- state->ident = V4L2_IDENT_SAA7111;
- v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
- break;
- }
+ state->ident = ident;
state->audclk_freq = 48000;
@@ -1682,18 +1768,19 @@
/* init to 60hz/48khz */
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
switch (state->ident) {
- case V4L2_IDENT_SAA7111:
- case V4L2_IDENT_SAA7111A:
+ case SAA7111:
+ case SAA7111A:
saa711x_writeregs(sd, saa7111_init);
break;
- case V4L2_IDENT_SAA7113:
+ case GM7113C:
+ case SAA7113:
saa711x_writeregs(sd, saa7113_init);
break;
default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
saa711x_writeregs(sd, saa7115_init_auto_input);
}
- if (state->ident > V4L2_IDENT_SAA7111A)
+ if (state->ident > SAA7111A)
saa711x_writeregs(sd, saa7115_init_misc);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
v4l2_ctrl_handler_setup(hdl);
@@ -1712,7 +1799,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- kfree(to_state(sd));
return 0;
}
@@ -1723,6 +1809,7 @@
{ "saa7114", 0 },
{ "saa7115", 0 },
{ "saa7118", 0 },
+ { "gm7113c", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa711x_id);
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
index 8a47ac1..264b755 100644
--- a/drivers/media/i2c/saa7127.c
+++ b/drivers/media/i2c/saa7127.c
@@ -54,7 +54,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/saa7127.h>
static int debug;
@@ -251,10 +250,15 @@
**********************************************************************
*/
+enum saa712x_model {
+ SAA7127,
+ SAA7129,
+};
+
struct saa7127_state {
struct v4l2_subdev sd;
v4l2_std_id std;
- u32 ident;
+ enum saa712x_model ident;
enum saa7127_input_type input_type;
enum saa7127_output_type output_type;
int video_enable;
@@ -482,7 +486,7 @@
inittab = saa7127_init_config_60hz;
state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
- } else if (state->ident == V4L2_IDENT_SAA7129 &&
+ } else if (state->ident == SAA7129 &&
(std & V4L2_STD_SECAM) &&
!(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) {
@@ -517,7 +521,7 @@
break;
case SAA7127_OUTPUT_TYPE_COMPOSITE:
- if (state->ident == V4L2_IDENT_SAA7129)
+ if (state->ident == SAA7129)
state->reg_2d = 0x20; /* CVBS only */
else
state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */
@@ -525,7 +529,7 @@
break;
case SAA7127_OUTPUT_TYPE_SVIDEO:
- if (state->ident == V4L2_IDENT_SAA7129)
+ if (state->ident == SAA7129)
state->reg_2d = 0x18; /* Y + C */
else
state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */
@@ -543,7 +547,7 @@
break;
case SAA7127_OUTPUT_TYPE_BOTH:
- if (state->ident == V4L2_IDENT_SAA7129)
+ if (state->ident == SAA7129)
state->reg_2d = 0x38;
else
state->reg_2d = 0xbf;
@@ -661,12 +665,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = saa7127_read(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
@@ -674,25 +672,11 @@
static int saa7127_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
#endif
-static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7127_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
-}
-
static int saa7127_log_status(struct v4l2_subdev *sd)
{
struct saa7127_state *state = to_state(sd);
@@ -712,7 +696,6 @@
static const struct v4l2_subdev_core_ops saa7127_core_ops = {
.log_status = saa7127_log_status,
- .g_chip_ident = saa7127_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = saa7127_g_register,
.s_register = saa7127_s_register,
@@ -752,7 +735,7 @@
v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
client->addr << 1);
- state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
@@ -767,7 +750,6 @@
if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
(saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
v4l2_dbg(1, debug, sd, "saa7127 not found\n");
- kfree(state);
return -ENODEV;
}
@@ -782,10 +764,10 @@
if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
read_result);
- state->ident = V4L2_IDENT_SAA7129;
+ state->ident = SAA7129;
strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
} else {
- state->ident = V4L2_IDENT_SAA7127;
+ state->ident = SAA7127;
strlcpy(client->name, "saa7127", I2C_NAME_SIZE);
}
}
@@ -809,7 +791,7 @@
saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
saa7127_set_video_enable(sd, 1);
- if (state->ident == V4L2_IDENT_SAA7129)
+ if (state->ident == SAA7129)
saa7127_write_inittab(sd, saa7129_init_config_extra);
return 0;
}
@@ -823,7 +805,6 @@
v4l2_device_unregister_subdev(sd);
/* Turn off TV output */
saa7127_set_video_enable(sd, 0);
- kfree(to_state(sd));
return 0;
}
@@ -831,10 +812,10 @@
static struct i2c_device_id saa7127_id[] = {
{ "saa7127_auto", 0 }, /* auto-detection */
- { "saa7126", V4L2_IDENT_SAA7127 },
- { "saa7127", V4L2_IDENT_SAA7127 },
- { "saa7128", V4L2_IDENT_SAA7129 },
- { "saa7129", V4L2_IDENT_SAA7129 },
+ { "saa7126", SAA7127 },
+ { "saa7127", SAA7127 },
+ { "saa7128", SAA7129 },
+ { "saa7129", SAA7129 },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa7127_id);
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index cf3a0aa..401ca11 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -977,12 +977,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = saa717x_read(sd, reg->reg);
reg->size = 1;
return 0;
@@ -990,14 +984,9 @@
static int saa717x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
u16 addr = reg->reg & 0xffff;
u8 val = reg->val & 0xff;
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
saa717x_write(sd, addr, val);
return 0;
}
@@ -1262,7 +1251,7 @@
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
+ decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL)
return -ENOMEM;
@@ -1276,7 +1265,6 @@
id = saa717x_read(sd, 0x5a0);
if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) {
v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id);
- kfree(decoder);
return -ENODEV;
}
if (id == 0xc2)
@@ -1316,7 +1304,6 @@
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(decoder);
return err;
}
@@ -1353,7 +1340,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c
index 2c6b65c..f56c1c8 100644
--- a/drivers/media/i2c/saa7185.c
+++ b/drivers/media/i2c/saa7185.c
@@ -32,7 +32,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("Philips SAA7185 video encoder driver");
MODULE_AUTHOR("Dave Perks");
@@ -285,17 +284,9 @@
return 0;
}
-static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops saa7185_core_ops = {
- .g_chip_ident = saa7185_g_chip_ident,
.init = saa7185_init,
};
@@ -326,7 +317,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL);
+ encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
if (encoder == NULL)
return -ENOMEM;
encoder->norm = V4L2_STD_NTSC;
@@ -352,7 +343,6 @@
v4l2_device_unregister_subdev(sd);
/* SW: output off is active */
saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40);
- kfree(encoder);
return 0;
}
diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c
index d7d1670..606a4ba 100644
--- a/drivers/media/i2c/saa7191.c
+++ b/drivers/media/i2c/saa7191.c
@@ -22,7 +22,6 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include "saa7191.h"
@@ -272,7 +271,7 @@
dprintk("SAA7191 extended signal auto-detection...\n");
- *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+ *norm &= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
stdc &= ~SAA7191_STDC_SECS;
ctl3 &= ~(SAA7191_CTL3_FSEL);
@@ -303,7 +302,7 @@
if (status & SAA7191_STATUS_FIDT) {
/* 60Hz signal -> NTSC */
dprintk("60Hz signal: NTSC\n");
- *norm = V4L2_STD_NTSC;
+ *norm &= V4L2_STD_NTSC;
return 0;
}
@@ -325,12 +324,13 @@
if (status & SAA7191_STATUS_FIDT) {
dprintk("No 50Hz signal\n");
saa7191_s_std(sd, old_norm);
- return -EAGAIN;
+ *norm = V4L2_STD_UNKNOWN;
+ return 0;
}
if (status & SAA7191_STATUS_CODE) {
dprintk("PAL\n");
- *norm = V4L2_STD_PAL;
+ *norm &= V4L2_STD_PAL;
return saa7191_s_std(sd, old_norm);
}
@@ -350,18 +350,19 @@
/* not 50Hz ? */
if (status & SAA7191_STATUS_FIDT) {
dprintk("No 50Hz signal\n");
- err = -EAGAIN;
+ *norm = V4L2_STD_UNKNOWN;
goto out;
}
if (status & SAA7191_STATUS_CODE) {
/* Color detected -> SECAM */
dprintk("SECAM\n");
- *norm = V4L2_STD_SECAM;
+ *norm &= V4L2_STD_SECAM;
return saa7191_s_std(sd, old_norm);
}
dprintk("No color detected with SECAM - Going back to PAL.\n");
+ *norm = V4L2_STD_UNKNOWN;
out:
return saa7191_s_std(sd, old_norm);
@@ -567,18 +568,9 @@
}
-static int saa7191_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops saa7191_core_ops = {
- .g_chip_ident = saa7191_g_chip_ident,
.g_ctrl = saa7191_g_ctrl,
.s_ctrl = saa7191_s_ctrl,
.s_std = saa7191_s_std,
@@ -605,7 +597,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+ decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (!decoder)
return -ENOMEM;
@@ -615,7 +607,6 @@
err = saa7191_write_block(sd, sizeof(initseq), initseq);
if (err) {
printk(KERN_ERR "SAA7191 initialization failed\n");
- kfree(decoder);
return err;
}
@@ -636,7 +627,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_saa7191(sd));
return 0;
}
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index cae4f46..7ac7580 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -2383,8 +2383,9 @@
}
if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) {
- if (gpio_request_one(sensor->platform_data->xshutdown, 0,
- "SMIA++ xshutdown") != 0) {
+ if (devm_gpio_request_one(&client->dev,
+ sensor->platform_data->xshutdown, 0,
+ "SMIA++ xshutdown") != 0) {
dev_err(&client->dev,
"unable to acquire reset gpio %d\n",
sensor->platform_data->xshutdown);
@@ -2393,10 +2394,8 @@
}
rval = smiapp_power_on(sensor);
- if (rval) {
- rval = -ENODEV;
- goto out_smiapp_power_on;
- }
+ if (rval)
+ return -ENODEV;
rval = smiapp_identify_module(subdev);
if (rval) {
@@ -2656,11 +2655,6 @@
out_power_off:
smiapp_power_off(sensor);
-
-out_smiapp_power_on:
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_free(sensor->platform_data->xshutdown);
-
return rval;
}
@@ -2854,12 +2848,10 @@
device_remove_file(&client->dev, &dev_attr_nvm);
for (i = 0; i < sensor->ssds_used; i++) {
- media_entity_cleanup(&sensor->ssds[i].sd.entity);
v4l2_device_unregister_subdev(&sensor->ssds[i].sd);
+ media_entity_cleanup(&sensor->ssds[i].sd.entity);
}
smiapp_free_controls(sensor);
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_free(sensor->platform_data->xshutdown);
return 0;
}
diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index a2a5cbb..1d384a3 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -18,8 +18,9 @@
#include <linux/module.h>
#include <media/soc_camera.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
/* IMX074 registers */
@@ -77,6 +78,7 @@
struct imx074 {
struct v4l2_subdev subdev;
const struct imx074_datafmt *fmt;
+ struct v4l2_clk *clk;
};
static const struct imx074_datafmt imx074_colour_fmts[] = {
@@ -251,29 +253,13 @@
return reg_write(client, MODE_SELECT, !!enable);
}
-static int imx074_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = V4L2_IDENT_IMX074;
- id->revision = 0;
-
- return 0;
-}
-
static int imx074_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct imx074 *priv = to_imx074(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static int imx074_g_mbus_config(struct v4l2_subdev *sd,
@@ -299,7 +285,6 @@
};
static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
- .g_chip_ident = imx074_g_chip_ident,
.s_power = imx074_s_power,
};
@@ -431,6 +416,7 @@
struct imx074 *priv;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ int ret;
if (!ssdd) {
dev_err(&client->dev, "IMX074: missing platform data!\n");
@@ -451,12 +437,35 @@
priv->fmt = &imx074_colour_fmts[0];
- return imx074_video_probe(client);
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk)) {
+ dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk));
+ return -EPROBE_DEFER;
+ }
+
+ ret = soc_camera_power_init(&client->dev, ssdd);
+ if (ret < 0)
+ goto epwrinit;
+
+ ret = imx074_video_probe(client);
+ if (ret < 0)
+ goto eprobe;
+
+ return v4l2_async_register_subdev(&priv->subdev);
+
+epwrinit:
+eprobe:
+ v4l2_clk_put(priv->clk);
+ return ret;
}
static int imx074_remove(struct i2c_client *client)
{
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct imx074 *priv = to_imx074(client);
+
+ v4l2_async_unregister_subdev(&priv->subdev);
+ v4l2_clk_put(priv->clk);
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
index dd90898..df97033 100644
--- a/drivers/media/i2c/soc_camera/mt9m001.c
+++ b/drivers/media/i2c/soc_camera/mt9m001.c
@@ -16,8 +16,8 @@
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
/*
@@ -94,10 +94,10 @@
struct v4l2_ctrl *exposure;
};
struct v4l2_rect rect; /* Sensor window */
+ struct v4l2_clk *clk;
const struct mt9m001_datafmt *fmt;
const struct mt9m001_datafmt *fmts;
int num_fmts;
- int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
unsigned int total_h;
unsigned short y_skip_top; /* Lines to skip at the top */
};
@@ -320,36 +320,15 @@
return 0;
}
-static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9m001->model;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9m001_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
reg->size = 2;
reg->val = reg_read(client, reg->reg);
@@ -364,12 +343,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
@@ -381,8 +357,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on);
}
static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -505,11 +482,9 @@
switch (data) {
case 0x8411:
case 0x8421:
- mt9m001->model = V4L2_IDENT_MT9M001C12ST;
mt9m001->fmts = mt9m001_colour_fmts;
break;
case 0x8431:
- mt9m001->model = V4L2_IDENT_MT9M001C12STM;
mt9m001->fmts = mt9m001_monochrome_fmts;
break;
default:
@@ -580,7 +555,6 @@
};
static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
- .g_chip_ident = mt9m001_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m001_g_register,
.s_register = mt9m001_s_register,
@@ -710,9 +684,18 @@
mt9m001->rect.width = MT9M001_MAX_WIDTH;
mt9m001->rect.height = MT9M001_MAX_HEIGHT;
+ mt9m001->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(mt9m001->clk)) {
+ ret = PTR_ERR(mt9m001->clk);
+ goto eclkget;
+ }
+
ret = mt9m001_video_probe(ssdd, client);
- if (ret)
+ if (ret) {
+ v4l2_clk_put(mt9m001->clk);
+eclkget:
v4l2_ctrl_handler_free(&mt9m001->hdl);
+ }
return ret;
}
@@ -722,6 +705,7 @@
struct mt9m001 *mt9m001 = to_mt9m001(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ v4l2_clk_put(mt9m001->clk);
v4l2_device_unregister_subdev(&mt9m001->subdev);
v4l2_ctrl_handler_free(&mt9m001->hdl);
mt9m001_video_remove(ssdd);
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index 8bd4e0d..de3605d 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -17,9 +17,9 @@
#include <linux/module.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
/*
* MT9M111, MT9M112 and MT9M131:
@@ -205,10 +205,9 @@
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl *gain;
- int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
- * from v4l2-chip-ident.h */
struct mt9m111_context *ctx;
struct v4l2_rect rect; /* cropping rectangle */
+ struct v4l2_clk *clk;
int width; /* output */
int height; /* sizes */
struct mutex power_lock; /* lock to protect power_count */
@@ -600,24 +599,6 @@
return ret;
}
-static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9m111->model;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9m111_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -625,10 +606,8 @@
struct i2c_client *client = v4l2_get_subdevdata(sd);
int val;
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ if (reg->reg > 0x2ff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
val = mt9m111_reg_read(client, reg->reg);
reg->size = 2;
@@ -645,12 +624,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ if (reg->reg > 0x2ff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
if (mt9m111_reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
@@ -801,14 +777,14 @@
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
int ret;
- ret = soc_camera_power_on(&client->dev, ssdd);
+ ret = soc_camera_power_on(&client->dev, ssdd, mt9m111->clk);
if (ret < 0)
return ret;
ret = mt9m111_resume(mt9m111);
if (ret < 0) {
dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret);
- soc_camera_power_off(&client->dev, ssdd);
+ soc_camera_power_off(&client->dev, ssdd, mt9m111->clk);
}
return ret;
@@ -820,7 +796,7 @@
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
mt9m111_suspend(mt9m111);
- soc_camera_power_off(&client->dev, ssdd);
+ soc_camera_power_off(&client->dev, ssdd, mt9m111->clk);
}
static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
@@ -856,7 +832,6 @@
};
static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
- .g_chip_ident = mt9m111_g_chip_ident,
.s_power = mt9m111_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9m111_g_register,
@@ -923,12 +898,10 @@
switch (data) {
case 0x143a: /* MT9M111 or MT9M131 */
- mt9m111->model = V4L2_IDENT_MT9M111;
dev_info(&client->dev,
"Detected a MT9M111/MT9M131 chip ID %x\n", data);
break;
case 0x148c: /* MT9M112 */
- mt9m111->model = V4L2_IDENT_MT9M112;
dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
break;
default:
@@ -1002,9 +975,18 @@
mt9m111->lastpage = -1;
mutex_init(&mt9m111->power_lock);
+ mt9m111->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(mt9m111->clk)) {
+ ret = PTR_ERR(mt9m111->clk);
+ goto eclkget;
+ }
+
ret = mt9m111_video_probe(client);
- if (ret)
+ if (ret) {
+ v4l2_clk_put(mt9m111->clk);
+eclkget:
v4l2_ctrl_handler_free(&mt9m111->hdl);
+ }
return ret;
}
@@ -1013,6 +995,7 @@
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
+ v4l2_clk_put(mt9m111->clk);
v4l2_device_unregister_subdev(&mt9m111->subdev);
v4l2_ctrl_handler_free(&mt9m111->hdl);
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
index 26a15b8..47d18d0 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/drivers/media/i2c/soc_camera/mt9t031.c
@@ -18,7 +18,7 @@
#include <linux/module.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
@@ -76,7 +76,7 @@
struct v4l2_ctrl *exposure;
};
struct v4l2_rect rect; /* Sensor window */
- int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
+ struct v4l2_clk *clk;
u16 xskip;
u16 yskip;
unsigned int total_h;
@@ -391,36 +391,16 @@
return 0;
}
-static int mt9t031_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9t031->model;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9t031_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
+ reg->size = 1;
reg->val = reg_read(client, reg->reg);
if (reg->val > 0xffff)
@@ -434,12 +414,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
@@ -595,7 +572,7 @@
return 0;
}
-static struct dev_pm_ops mt9t031_dev_pm_ops = {
+static const struct dev_pm_ops mt9t031_dev_pm_ops = {
.runtime_suspend = mt9t031_runtime_suspend,
.runtime_resume = mt9t031_runtime_resume,
};
@@ -610,16 +587,17 @@
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
int ret;
if (on) {
- ret = soc_camera_power_on(&client->dev, ssdd);
+ ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk);
if (ret < 0)
return ret;
vdev->dev.type = &mt9t031_dev_type;
} else {
vdev->dev.type = NULL;
- soc_camera_power_off(&client->dev, ssdd);
+ soc_camera_power_off(&client->dev, ssdd, mt9t031->clk);
}
return 0;
@@ -650,7 +628,6 @@
switch (data) {
case 0x1621:
- mt9t031->model = V4L2_IDENT_MT9T031;
break;
default:
dev_err(&client->dev,
@@ -685,7 +662,6 @@
};
static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
- .g_chip_ident = mt9t031_g_chip_ident,
.s_power = mt9t031_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9t031_g_register,
@@ -812,9 +788,18 @@
mt9t031->xskip = 1;
mt9t031->yskip = 1;
+ mt9t031->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(mt9t031->clk)) {
+ ret = PTR_ERR(mt9t031->clk);
+ goto eclkget;
+ }
+
ret = mt9t031_video_probe(client);
- if (ret)
+ if (ret) {
+ v4l2_clk_put(mt9t031->clk);
+eclkget:
v4l2_ctrl_handler_free(&mt9t031->hdl);
+ }
return ret;
}
@@ -823,6 +808,7 @@
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
+ v4l2_clk_put(mt9t031->clk);
v4l2_device_unregister_subdev(&mt9t031->subdev);
v4l2_ctrl_handler_free(&mt9t031->hdl);
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index a7256b7..46f431a 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -27,7 +27,7 @@
#include <media/mt9t112.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
/* you can check PLL/clock info */
@@ -90,8 +90,8 @@
struct mt9t112_camera_info *info;
struct i2c_client *client;
struct v4l2_rect frame;
+ struct v4l2_clk *clk;
const struct mt9t112_format *format;
- int model;
int num_formats;
u32 flags;
/* for flags */
@@ -738,17 +738,6 @@
/************************************************************************
v4l2_subdev_core_ops
************************************************************************/
-static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- id->ident = priv->model;
- id->revision = 0;
-
- return 0;
-}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9t112_g_register(struct v4l2_subdev *sd,
@@ -781,12 +770,12 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct mt9t112_priv *priv = to_mt9t112(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
- .g_chip_ident = mt9t112_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9t112_g_register,
.s_register = mt9t112_s_register,
@@ -1061,12 +1050,10 @@
switch (chipid) {
case 0x2680:
devname = "mt9t111";
- priv->model = V4L2_IDENT_MT9T111;
priv->num_formats = 1;
break;
case 0x2682:
devname = "mt9t112";
- priv->model = V4L2_IDENT_MT9T112;
priv->num_formats = ARRAY_SIZE(mt9t112_cfmts);
break;
default:
@@ -1108,18 +1095,26 @@
v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
ret = mt9t112_camera_probe(client);
- if (ret)
- return ret;
/* Cannot fail: using the default supported pixel code */
- mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+ if (!ret)
+ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+ else
+ v4l2_clk_put(priv->clk);
return ret;
}
static int mt9t112_remove(struct i2c_client *client)
{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ v4l2_clk_put(priv->clk);
return 0;
}
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index a295e59..f9f95f8 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -19,7 +19,7 @@
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
/*
@@ -133,6 +133,11 @@
.pixclk_fv_lv = MT9V024_PIXCLK_FV_LV,
};
+enum mt9v022_model {
+ MT9V022IX7ATM,
+ MT9V022IX7ATC,
+};
+
struct mt9v022 {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
@@ -149,11 +154,12 @@
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *vblank;
struct v4l2_rect rect; /* Sensor window */
+ struct v4l2_clk *clk;
const struct mt9v022_datafmt *fmt;
const struct mt9v022_datafmt *fmts;
const struct mt9v02x_register *reg;
int num_fmts;
- int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
+ enum mt9v022_model model;
u16 chip_control;
u16 chip_version;
unsigned short y_skip_top; /* Lines to skip at the top */
@@ -406,12 +412,12 @@
switch (mf->code) {
case V4L2_MBUS_FMT_Y8_1X8:
case V4L2_MBUS_FMT_Y10_1X10:
- if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
+ if (mt9v022->model != MT9V022IX7ATM)
return -EINVAL;
break;
case V4L2_MBUS_FMT_SBGGR8_1X8:
case V4L2_MBUS_FMT_SBGGR10_1X10:
- if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
+ if (mt9v022->model != MT9V022IX7ATC)
return -EINVAL;
break;
default:
@@ -457,36 +463,15 @@
return 0;
}
-static int mt9v022_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9v022->model;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9v022_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
reg->size = 2;
reg->val = reg_read(client, reg->reg);
@@ -501,12 +486,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ if (reg->reg > 0xff)
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
@@ -518,8 +500,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on);
}
static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -706,11 +689,11 @@
if (sensor_type && (!strcmp("colour", sensor_type) ||
!strcmp("color", sensor_type))) {
ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
- mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
+ mt9v022->model = MT9V022IX7ATC;
mt9v022->fmts = mt9v022_colour_fmts;
} else {
ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11);
- mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
+ mt9v022->model = MT9V022IX7ATM;
mt9v022->fmts = mt9v022_monochrome_fmts;
}
@@ -740,7 +723,7 @@
mt9v022->fmt = &mt9v022->fmts[0];
dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
- data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
+ data, mt9v022->model == MT9V022IX7ATM ?
"monochrome" : "colour");
ret = mt9v022_init(client);
@@ -768,7 +751,6 @@
};
static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
- .g_chip_ident = mt9v022_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v022_g_register,
.s_register = mt9v022_s_register,
@@ -957,9 +939,18 @@
mt9v022->rect.width = MT9V022_MAX_WIDTH;
mt9v022->rect.height = MT9V022_MAX_HEIGHT;
+ mt9v022->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(mt9v022->clk)) {
+ ret = PTR_ERR(mt9v022->clk);
+ goto eclkget;
+ }
+
ret = mt9v022_video_probe(client);
- if (ret)
+ if (ret) {
+ v4l2_clk_put(mt9v022->clk);
+eclkget:
v4l2_ctrl_handler_free(&mt9v022->hdl);
+ }
return ret;
}
@@ -969,6 +960,7 @@
struct mt9v022 *mt9v022 = to_mt9v022(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ v4l2_clk_put(mt9v022->clk);
v4l2_device_unregister_subdev(&mt9v022->subdev);
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index e316842..6c6b1c3 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -22,7 +22,7 @@
#include <linux/videodev2.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
@@ -303,8 +303,8 @@
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
enum v4l2_mbus_pixelcode cfmt_code;
+ struct v4l2_clk *clk;
const struct ov2640_win_size *win;
- int model;
};
/*
@@ -723,18 +723,6 @@
return -EINVAL;
}
-static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
-
- id->ident = priv->model;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov2640_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -772,8 +760,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct ov2640_priv *priv = to_ov2640(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
/* Select the nearest higher resolution for capture */
@@ -1009,7 +998,6 @@
switch (VERSION(pid, ver)) {
case PID_OV2640:
devname = "ov2640";
- priv->model = V4L2_IDENT_OV2640;
break;
default:
dev_err(&client->dev,
@@ -1034,7 +1022,6 @@
};
static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
- .g_chip_ident = ov2640_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov2640_g_register,
.s_register = ov2640_s_register,
@@ -1113,11 +1100,20 @@
if (priv->hdl.error)
return priv->hdl.error;
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto eclkget;
+ }
+
ret = ov2640_video_probe(client);
- if (ret)
+ if (ret) {
+ v4l2_clk_put(priv->clk);
+eclkget:
v4l2_ctrl_handler_free(&priv->hdl);
- else
+ } else {
dev_info(&adapter->dev, "OV2640 Probed\n");
+ }
return ret;
}
@@ -1126,6 +1122,7 @@
{
struct ov2640_priv *priv = to_ov2640(client);
+ v4l2_clk_put(priv->clk);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
return 0;
diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c
index 9aa56de..0a5c5d4 100644
--- a/drivers/media/i2c/soc_camera/ov5642.c
+++ b/drivers/media/i2c/soc_camera/ov5642.c
@@ -24,7 +24,7 @@
#include <linux/v4l2-mediabus.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
/* OV5642 registers */
@@ -610,6 +610,7 @@
struct v4l2_subdev subdev;
const struct ov5642_datafmt *fmt;
struct v4l2_rect crop_rect;
+ struct v4l2_clk *clk;
/* blanking information */
int total_width;
@@ -848,23 +849,6 @@
return 0;
}
-static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = V4L2_IDENT_OV5642;
- id->revision = 0;
-
- return 0;
-}
-
static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -935,12 +919,13 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct ov5642 *priv = to_ov5642(client);
int ret;
if (!on)
- return soc_camera_power_off(&client->dev, ssdd);
+ return soc_camera_power_off(&client->dev, ssdd, priv->clk);
- ret = soc_camera_power_on(&client->dev, ssdd);
+ ret = soc_camera_power_on(&client->dev, ssdd, priv->clk);
if (ret < 0)
return ret;
@@ -966,7 +951,6 @@
static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
.s_power = ov5642_s_power,
- .g_chip_ident = ov5642_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov5642_get_register,
.s_register = ov5642_set_register,
@@ -1021,6 +1005,7 @@
{
struct ov5642 *priv;
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ int ret;
if (!ssdd) {
dev_err(&client->dev, "OV5642: missing platform data!\n");
@@ -1042,13 +1027,23 @@
priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
priv->total_height = BLANKING_MIN_HEIGHT;
- return ov5642_video_probe(client);
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ ret = ov5642_video_probe(client);
+ if (ret < 0)
+ v4l2_clk_put(priv->clk);
+
+ return ret;
}
static int ov5642_remove(struct i2c_client *client)
{
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct ov5642 *priv = to_ov5642(client);
+ v4l2_clk_put(priv->clk);
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index 991202d..ab01598 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -32,7 +32,7 @@
#include <linux/module.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
/* Register definitions */
@@ -196,6 +196,7 @@
struct v4l2_ctrl *blue;
struct v4l2_ctrl *red;
};
+ struct v4l2_clk *clk;
bool half_scale; /* scale down output by 2 */
struct v4l2_rect rect; /* sensor cropping window */
unsigned long pclk_limit; /* from host */
@@ -390,16 +391,6 @@
return -EINVAL;
}
-/* Get chip identification */
-static int ov6650_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- id->ident = V4L2_IDENT_OV6650;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov6650_get_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -436,8 +427,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct ov6650 *priv = to_ov6650(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
@@ -879,7 +871,6 @@
};
static struct v4l2_subdev_core_ops ov6650_core_ops = {
- .g_chip_ident = ov6650_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov6650_get_register,
.s_register = ov6650_set_register,
@@ -1025,9 +1016,18 @@
priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
priv->colorspace = V4L2_COLORSPACE_JPEG;
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto eclkget;
+ }
+
ret = ov6650_video_probe(client);
- if (ret)
+ if (ret) {
+ v4l2_clk_put(priv->clk);
+eclkget:
v4l2_ctrl_handler_free(&priv->hdl);
+ }
return ret;
}
@@ -1036,6 +1036,7 @@
{
struct ov6650 *priv = to_ov6650(client);
+ v4l2_clk_put(priv->clk);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
return 0;
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index 713d62e..7f2b3c8 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -26,8 +26,8 @@
#include <media/ov772x.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-subdev.h>
/*
@@ -396,10 +396,10 @@
struct ov772x_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_clk *clk;
struct ov772x_camera_info *info;
const struct ov772x_color_format *cfmt;
const struct ov772x_win_size *win;
- int model;
unsigned short flag_vflip:1;
unsigned short flag_hflip:1;
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
@@ -620,17 +620,6 @@
return -EINVAL;
}
-static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct ov772x_priv *priv = to_ov772x(sd);
-
- id->ident = priv->model;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov772x_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -668,8 +657,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct ov772x_priv *priv = to_ov772x(sd);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
@@ -965,11 +955,9 @@
switch (VERSION(pid, ver)) {
case OV7720:
devname = "ov7720";
- priv->model = V4L2_IDENT_OV7720;
break;
case OV7725:
devname = "ov7725";
- priv->model = V4L2_IDENT_OV7725;
break;
default:
dev_err(&client->dev,
@@ -997,7 +985,6 @@
};
static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
- .g_chip_ident = ov772x_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov772x_g_register,
.s_register = ov772x_s_register,
@@ -1088,13 +1075,22 @@
if (priv->hdl.error)
return priv->hdl.error;
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto eclkget;
+ }
+
ret = ov772x_video_probe(priv);
if (ret < 0) {
+ v4l2_clk_put(priv->clk);
+eclkget:
v4l2_ctrl_handler_free(&priv->hdl);
} else {
priv->cfmt = &ov772x_cfmts[0];
priv->win = &ov772x_win_sizes[0];
}
+
return ret;
}
@@ -1102,6 +1098,7 @@
{
struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
+ v4l2_clk_put(priv->clk);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
return 0;
diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c
index 20ca62d..e968c3f 100644
--- a/drivers/media/i2c/soc_camera/ov9640.c
+++ b/drivers/media/i2c/soc_camera/ov9640.c
@@ -28,7 +28,7 @@
#include <linux/videodev2.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
@@ -61,7 +61,7 @@
/* Configurations
* NOTE: for YUV, alter the following registers:
- * COM12 |= OV9640_COM12_YUV_AVG
+ * COM12 |= OV9640_COM12_YUV_AVG
*
* for RGB, alter the following registers:
* COM7 |= OV9640_COM7_RGB
@@ -287,18 +287,6 @@
return -EINVAL;
}
-/* Get chip identification */
-static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- id->ident = priv->model;
- id->revision = priv->revision;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ov9640_get_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -337,8 +325,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
/* select nearest higher resolution for capture */
@@ -615,12 +604,10 @@
switch (VERSION(pid, ver)) {
case OV9640_V2:
devname = "ov9640";
- priv->model = V4L2_IDENT_OV9640;
priv->revision = 2;
break;
case OV9640_V3:
devname = "ov9640";
- priv->model = V4L2_IDENT_OV9640;
priv->revision = 3;
break;
default:
@@ -644,7 +631,6 @@
};
static struct v4l2_subdev_core_ops ov9640_core_ops = {
- .g_chip_ident = ov9640_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov9640_get_register,
.s_register = ov9640_set_register,
@@ -716,10 +702,18 @@
if (priv->hdl.error)
return priv->hdl.error;
- ret = ov9640_video_probe(client);
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto eclkget;
+ }
- if (ret)
+ ret = ov9640_video_probe(client);
+ if (ret) {
+ v4l2_clk_put(priv->clk);
+eclkget:
v4l2_ctrl_handler_free(&priv->hdl);
+ }
return ret;
}
@@ -729,6 +723,7 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ov9640_priv *priv = to_ov9640_sensor(sd);
+ v4l2_clk_put(priv->clk);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
return 0;
diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h
index 6b33a97..65d13ff 100644
--- a/drivers/media/i2c/soc_camera/ov9640.h
+++ b/drivers/media/i2c/soc_camera/ov9640.h
@@ -199,6 +199,7 @@
struct ov9640_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_clk *clk;
int model;
int revision;
diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c
index 012bd62..ea76863 100644
--- a/drivers/media/i2c/soc_camera/ov9740.c
+++ b/drivers/media/i2c/soc_camera/ov9740.c
@@ -17,7 +17,7 @@
#include <linux/v4l2-mediabus.h>
#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev)
@@ -196,8 +196,8 @@
struct ov9740_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_clk *clk;
- int ident;
u16 model;
u8 revision;
u8 manid;
@@ -772,18 +772,6 @@
return 0;
}
-/* Get chip identification */
-static int ov9740_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct ov9740_priv *priv = to_ov9740(sd);
-
- id->ident = priv->ident;
- id->revision = priv->revision;
-
- return 0;
-}
-
static int ov9740_s_power(struct v4l2_subdev *sd, int on)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -792,7 +780,7 @@
int ret;
if (on) {
- ret = soc_camera_power_on(&client->dev, ssdd);
+ ret = soc_camera_power_on(&client->dev, ssdd, priv->clk);
if (ret < 0)
return ret;
@@ -806,7 +794,7 @@
priv->current_enable = true;
}
- soc_camera_power_off(&client->dev, ssdd);
+ soc_camera_power_off(&client->dev, ssdd, priv->clk);
}
return 0;
@@ -887,8 +875,6 @@
goto done;
}
- priv->ident = V4L2_IDENT_OV9740;
-
dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, "
"Manufacturer 0x%02x, SMIA Version 0x%02x\n",
priv->model, priv->revision, priv->manid, priv->smiaver);
@@ -927,7 +913,6 @@
};
static struct v4l2_subdev_core_ops ov9740_core_ops = {
- .g_chip_ident = ov9740_g_chip_ident,
.s_power = ov9740_s_power,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ov9740_get_register,
@@ -975,9 +960,18 @@
if (priv->hdl.error)
return priv->hdl.error;
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto eclkget;
+ }
+
ret = ov9740_video_probe(client);
- if (ret < 0)
+ if (ret < 0) {
+ v4l2_clk_put(priv->clk);
+eclkget:
v4l2_ctrl_handler_free(&priv->hdl);
+ }
return ret;
}
@@ -986,6 +980,7 @@
{
struct ov9740_priv *priv = i2c_get_clientdata(client);
+ v4l2_clk_put(priv->clk);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
return 0;
diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
index 1f9ec3b..7e6d978 100644
--- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c
+++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
@@ -17,8 +17,8 @@
#include <media/rj54n1cb0c.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#define RJ54N1_DEV_CODE 0x0400
@@ -151,6 +151,7 @@
struct rj54n1 {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_clk *clk;
struct rj54n1_clock_div clk_div;
const struct rj54n1_datafmt *fmt;
struct v4l2_rect rect; /* Sensor window */
@@ -1120,37 +1121,16 @@
return 0;
}
-static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = V4L2_IDENT_RJ54N1CB0C;
- id->revision = 0;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int rj54n1_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
- reg->reg < 0x400 || reg->reg > 0x1fff)
+ if (reg->reg < 0x400 || reg->reg > 0x1fff)
/* Registers > 0x0800 are only available from Sharp support */
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
reg->size = 1;
reg->val = reg_read(client, reg->reg);
@@ -1165,14 +1145,10 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
- reg->reg < 0x400 || reg->reg > 0x1fff)
+ if (reg->reg < 0x400 || reg->reg > 0x1fff)
/* Registers >= 0x0800 are only available from Sharp support */
return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
if (reg_write(client, reg->reg, reg->val) < 0)
return -EIO;
@@ -1184,8 +1160,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on);
}
static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1233,7 +1210,6 @@
};
static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
- .g_chip_ident = rj54n1_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = rj54n1_g_register,
.s_register = rj54n1_s_register,
@@ -1382,9 +1358,18 @@
rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
(clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
+ rj54n1->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(rj54n1->clk)) {
+ ret = PTR_ERR(rj54n1->clk);
+ goto eclkget;
+ }
+
ret = rj54n1_video_probe(client, rj54n1_priv);
- if (ret < 0)
+ if (ret < 0) {
+ v4l2_clk_put(rj54n1->clk);
+eclkget:
v4l2_ctrl_handler_free(&rj54n1->hdl);
+ }
return ret;
}
@@ -1394,6 +1379,7 @@
struct rj54n1 *rj54n1 = to_rj54n1(client);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ v4l2_clk_put(rj54n1->clk);
v4l2_device_unregister_subdev(&rj54n1->subdev);
if (ssdd->free_bus)
ssdd->free_bus(ssdd);
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index bad90b1..ab54628 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -27,7 +27,7 @@
#include <media/soc_camera.h>
#include <media/tw9910.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#define GET_ID(val) ((val & 0xF8) >> 3)
@@ -228,6 +228,7 @@
struct tw9910_priv {
struct v4l2_subdev subdev;
+ struct v4l2_clk *clk;
struct tw9910_video_info *info;
const struct tw9910_scale_ctrl *scale;
v4l2_std_id norm;
@@ -518,18 +519,6 @@
return 0;
}
-static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- id->ident = V4L2_IDENT_TW9910;
- id->revision = priv->revision;
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int tw9910_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -540,6 +529,7 @@
if (reg->reg > 0xff)
return -EINVAL;
+ reg->size = 1;
ret = i2c_smbus_read_byte_data(client, reg->reg);
if (ret < 0)
return ret;
@@ -570,8 +560,9 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ struct tw9910_priv *priv = to_tw9910(client);
- return soc_camera_set_power(&client->dev, ssdd, on);
+ return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}
static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
@@ -823,7 +814,6 @@
}
static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
- .g_chip_ident = tw9910_g_chip_ident,
.s_std = tw9910_s_std,
.g_std = tw9910_g_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -912,6 +902,7 @@
struct i2c_adapter *adapter =
to_i2c_adapter(client->dev.parent);
struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client);
+ int ret;
if (!ssdd || !ssdd->drv_priv) {
dev_err(&client->dev, "TW9910: missing platform data!\n");
@@ -935,11 +926,21 @@
v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
- return tw9910_video_probe(client);
+ priv->clk = v4l2_clk_get(&client->dev, "mclk");
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ ret = tw9910_video_probe(client);
+ if (ret < 0)
+ v4l2_clk_put(priv->clk);
+
+ return ret;
}
static int tw9910_remove(struct i2c_client *client)
{
+ struct tw9910_priv *priv = to_tw9910(client);
+ v4l2_clk_put(priv->clk);
return 0;
}
diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c
index 38cbea9..32d8232 100644
--- a/drivers/media/i2c/sony-btf-mpx.c
+++ b/drivers/media/i2c/sony-btf-mpx.c
@@ -30,7 +30,7 @@
static int debug;
module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "debug level 0=off(default) 1=on\n");
+MODULE_PARM_DESC(debug, "debug level 0=off(default) 1=on");
/* #define MPX_DEBUG */
@@ -355,7 +355,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- t = kzalloc(sizeof(struct sony_btf_mpx), GFP_KERNEL);
+ t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
if (t == NULL)
return -ENOMEM;
@@ -374,7 +374,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index e9d95bd..ae94326 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -23,6 +23,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
+#include <media/v4l2-ctrls.h>
#include <media/sr030pc30.h>
static int debug;
@@ -142,17 +143,24 @@
struct sr030pc30_info {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
const struct sr030pc30_platform_data *pdata;
const struct sr030pc30_format *curr_fmt;
const struct sr030pc30_frmsize *curr_win;
- unsigned int auto_wb:1;
- unsigned int auto_exp:1;
unsigned int hflip:1;
unsigned int vflip:1;
unsigned int sleep:1;
- unsigned int exposure;
- u8 blue_balance;
- u8 red_balance;
+ struct {
+ /* auto whitebalance control cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *red;
+ struct v4l2_ctrl *blue;
+ };
+ struct {
+ /* auto exposure control cluster */
+ struct v4l2_ctrl *autoexp;
+ struct v4l2_ctrl *exp;
+ };
u8 i2c_reg_page;
};
@@ -173,52 +181,6 @@
u16 val;
};
-static const struct v4l2_queryctrl sr030pc30_ctrl[] = {
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }, {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .flags = 0,
- }, {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- }, {
- .id = V4L2_CID_EXPOSURE_AUTO,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Auto Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = EXPOS_MIN_MS,
- .maximum = EXPOS_MAX_MS,
- .step = 1,
- .default_value = 1,
- }, {
- }
-};
-
/* supported resolutions */
static const struct sr030pc30_frmsize sr030pc30_sizes[] = {
{
@@ -394,48 +356,6 @@
return ret;
}
-static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on)
-{
- struct sr030pc30_info *info = to_sr030pc30(sd);
- /* auto anti-flicker is also enabled here */
- int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C);
- if (!ret)
- info->auto_exp = on;
- return ret;
-}
-
-static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value)
-{
- struct sr030pc30_info *info = to_sr030pc30(sd);
-
- unsigned long expos = value * info->pdata->clk_rate / (8 * 1000);
-
- int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF);
- if (!ret)
- ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF);
- if (!ret)
- ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF);
- if (!ret) { /* Turn off AE */
- info->exposure = value;
- ret = sr030pc30_enable_autoexposure(sd, 0);
- }
- return ret;
-}
-
-/* Automatic white balance control */
-static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on)
-{
- struct sr030pc30_info *info = to_sr030pc30(sd);
-
- int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F);
- if (!ret)
- ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B);
- if (!ret)
- info->auto_wb = on;
-
- return ret;
-}
-
static int sr030pc30_set_flip(struct v4l2_subdev *sd)
{
struct sr030pc30_info *info = to_sr030pc30(sd);
@@ -498,107 +418,56 @@
return -EINVAL;
}
-static int sr030pc30_queryctrl(struct v4l2_subdev *sd,
- struct v4l2_queryctrl *qc)
+static int sr030pc30_s_ctrl(struct v4l2_ctrl *ctrl)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++)
- if (qc->id == sr030pc30_ctrl[i].id) {
- *qc = sr030pc30_ctrl[i];
- v4l2_dbg(1, debug, sd, "%s id: %d\n",
- __func__, qc->id);
- return 0;
- }
-
- return -EINVAL;
-}
-
-static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value)
-{
- int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value);
- if (!ret)
- to_sr030pc30(sd)->blue_balance = value;
- return ret;
-}
-
-static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value)
-{
- int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value);
- if (!ret)
- to_sr030pc30(sd)->red_balance = value;
- return ret;
-}
-
-static int sr030pc30_s_ctrl(struct v4l2_subdev *sd,
- struct v4l2_control *ctrl)
-{
- int i, ret = 0;
-
- for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++)
- if (ctrl->id == sr030pc30_ctrl[i].id)
- break;
-
- if (i == ARRAY_SIZE(sr030pc30_ctrl))
- return -EINVAL;
-
- if (ctrl->value < sr030pc30_ctrl[i].minimum ||
- ctrl->value > sr030pc30_ctrl[i].maximum)
- return -ERANGE;
+ struct sr030pc30_info *info =
+ container_of(ctrl->handler, struct sr030pc30_info, hdl);
+ struct v4l2_subdev *sd = &info->sd;
+ int ret = 0;
v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n",
- __func__, ctrl->id, ctrl->value);
+ __func__, ctrl->id, ctrl->val);
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
- sr030pc30_enable_autowhitebalance(sd, ctrl->value);
- break;
- case V4L2_CID_BLUE_BALANCE:
- ret = sr030pc30_set_bluebalance(sd, ctrl->value);
- break;
- case V4L2_CID_RED_BALANCE:
- ret = sr030pc30_set_redbalance(sd, ctrl->value);
- break;
+ if (ctrl->is_new) {
+ ret = cam_i2c_write(sd, AWB_CTL2_REG,
+ ctrl->val ? 0x2E : 0x2F);
+ if (!ret)
+ ret = cam_i2c_write(sd, AWB_CTL1_REG,
+ ctrl->val ? 0xFB : 0x7B);
+ }
+ if (!ret && info->blue->is_new)
+ ret = cam_i2c_write(sd, MWB_BGAIN_REG, info->blue->val);
+ if (!ret && info->red->is_new)
+ ret = cam_i2c_write(sd, MWB_RGAIN_REG, info->red->val);
+ return ret;
+
case V4L2_CID_EXPOSURE_AUTO:
- sr030pc30_enable_autoexposure(sd,
- ctrl->value == V4L2_EXPOSURE_AUTO);
- break;
- case V4L2_CID_EXPOSURE:
- ret = sr030pc30_set_exposure(sd, ctrl->value);
- break;
+ /* auto anti-flicker is also enabled here */
+ if (ctrl->is_new)
+ ret = cam_i2c_write(sd, AE_CTL1_REG,
+ ctrl->val == V4L2_EXPOSURE_AUTO ? 0xDC : 0x0C);
+ if (info->exp->is_new) {
+ unsigned long expos = info->exp->val;
+
+ expos = expos * info->pdata->clk_rate / (8 * 1000);
+
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_TIMEH_REG,
+ expos >> 16 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_TIMEM_REG,
+ expos >> 8 & 0xFF);
+ if (!ret)
+ ret = cam_i2c_write(sd, EXP_TIMEL_REG,
+ expos & 0xFF);
+ }
+ return ret;
default:
return -EINVAL;
}
- return ret;
-}
-
-static int sr030pc30_g_ctrl(struct v4l2_subdev *sd,
- struct v4l2_control *ctrl)
-{
- struct sr030pc30_info *info = to_sr030pc30(sd);
-
- v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id);
-
- switch (ctrl->id) {
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->value = info->auto_wb;
- break;
- case V4L2_CID_BLUE_BALANCE:
- ctrl->value = info->blue_balance;
- break;
- case V4L2_CID_RED_BALANCE:
- ctrl->value = info->red_balance;
- break;
- case V4L2_CID_EXPOSURE_AUTO:
- ctrl->value = info->auto_exp;
- break;
- case V4L2_CID_EXPOSURE:
- ctrl->value = info->exposure;
- break;
- default:
- return -EINVAL;
- }
return 0;
}
@@ -752,11 +621,19 @@
return ret;
}
+static const struct v4l2_ctrl_ops sr030pc30_ctrl_ops = {
+ .s_ctrl = sr030pc30_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops sr030pc30_core_ops = {
.s_power = sr030pc30_s_power,
- .queryctrl = sr030pc30_queryctrl,
- .s_ctrl = sr030pc30_s_ctrl,
- .g_ctrl = sr030pc30_g_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_video_ops sr030pc30_video_ops = {
@@ -807,6 +684,7 @@
{
struct sr030pc30_info *info;
struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
const struct sr030pc30_platform_data *pdata
= client->dev.platform_data;
int ret;
@@ -820,7 +698,7 @@
if (ret)
return ret;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -830,10 +708,31 @@
v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops);
+ hdl = &info->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ info->awb = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ info->red = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 64);
+ info->blue = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64);
+ info->autoexp = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO, 0, 1, 1, 1);
+ info->exp = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops,
+ V4L2_CID_EXPOSURE, EXPOS_MIN_MS, EXPOS_MAX_MS, 1, 30);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(3, &info->awb, 0, false);
+ v4l2_ctrl_auto_cluster(2, &info->autoexp, V4L2_EXPOSURE_MANUAL, false);
+ v4l2_ctrl_handler_setup(hdl);
+
info->i2c_reg_page = -1;
info->hflip = 1;
- info->auto_exp = 1;
- info->exposure = 30;
return 0;
}
@@ -841,10 +740,9 @@
static int sr030pc30_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct sr030pc30_info *info = to_sr030pc30(sd);
v4l2_device_unregister_subdev(sd);
- kfree(info);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
return 0;
}
diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c
index 28b5121..72af644 100644
--- a/drivers/media/i2c/tda7432.c
+++ b/drivers/media/i2c/tda7432.c
@@ -359,7 +359,7 @@
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- t = kzalloc(sizeof(*t), GFP_KERNEL);
+ t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
if (!t)
return -ENOMEM;
sd = &t->sd;
@@ -380,7 +380,6 @@
int err = t->hdl.error;
v4l2_ctrl_handler_free(&t->hdl);
- kfree(t);
return err;
}
v4l2_ctrl_cluster(2, &t->bass);
@@ -406,7 +405,6 @@
tda7432_set(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&t->hdl);
- kfree(t);
return 0;
}
diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c
index 01441e3..fbdff8b 100644
--- a/drivers/media/i2c/tda9840.c
+++ b/drivers/media/i2c/tda9840.c
@@ -31,7 +31,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("tda9840 driver");
@@ -145,26 +144,14 @@
return 0;
}
-static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0);
-}
-
/* ----------------------------------------------------------------------- */
-static const struct v4l2_subdev_core_ops tda9840_core_ops = {
- .g_chip_ident = tda9840_g_chip_ident,
-};
-
static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
.s_tuner = tda9840_s_tuner,
.g_tuner = tda9840_g_tuner,
};
static const struct v4l2_subdev_ops tda9840_ops = {
- .core = &tda9840_core_ops,
.tuner = &tda9840_tuner_ops,
};
@@ -184,7 +171,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
@@ -201,7 +188,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
return 0;
}
diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c
index 3d5b06a..bbe1a99 100644
--- a/drivers/media/i2c/tea6415c.c
+++ b/drivers/media/i2c/tea6415c.c
@@ -33,7 +33,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include "tea6415c.h"
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
@@ -119,25 +118,13 @@
return ret;
}
-static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0);
-}
-
/* ----------------------------------------------------------------------- */
-static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
- .g_chip_ident = tea6415c_g_chip_ident,
-};
-
static const struct v4l2_subdev_video_ops tea6415c_video_ops = {
.s_routing = tea6415c_s_routing,
};
static const struct v4l2_subdev_ops tea6415c_ops = {
- .core = &tea6415c_core_ops,
.video = &tea6415c_video_ops,
};
@@ -152,7 +139,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
@@ -164,7 +151,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
return 0;
}
diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c
index 3875721..30a8d75 100644
--- a/drivers/media/i2c/tea6420.c
+++ b/drivers/media/i2c/tea6420.c
@@ -33,7 +33,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include "tea6420.h"
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
@@ -90,25 +89,13 @@
return 0;
}
-static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0);
-}
-
/* ----------------------------------------------------------------------- */
-static const struct v4l2_subdev_core_ops tea6420_core_ops = {
- .g_chip_ident = tea6420_g_chip_ident,
-};
-
static const struct v4l2_subdev_audio_ops tea6420_audio_ops = {
.s_routing = tea6420_s_routing,
};
static const struct v4l2_subdev_ops tea6420_ops = {
- .core = &tea6420_core_ops,
.audio = &tea6420_audio_ops,
};
@@ -125,7 +112,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
@@ -146,7 +133,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
return 0;
}
diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c
index c433955..0a2dacb 100644
--- a/drivers/media/i2c/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
@@ -26,7 +26,6 @@
#include <linux/slab.h>
#include <media/ths7303.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-device.h>
#define THS7303_CHANNEL_1 1
@@ -35,11 +34,10 @@
struct ths7303_state {
struct v4l2_subdev sd;
- struct ths7303_platform_data pdata;
+ const struct ths7303_platform_data *pdata;
struct v4l2_bt_timings bt;
int std_id;
int stream_on;
- int driver_data;
};
enum ths7303_filter_mode {
@@ -89,7 +87,7 @@
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ths7303_state *state = to_state(sd);
- struct ths7303_platform_data *pdata = &state->pdata;
+ const struct ths7303_platform_data *pdata = state->pdata;
u8 val, sel = 0;
int err, disable = 0;
@@ -212,15 +210,6 @@
return ths7303_config(sd);
}
-static int ths7303_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ths7303_state *state = to_state(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->driver_data, 0);
-}
-
static const struct v4l2_subdev_video_ops ths7303_video_ops = {
.s_stream = ths7303_s_stream,
.s_std_output = ths7303_s_std_output,
@@ -232,13 +221,6 @@
static int ths7303_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
reg->size = 1;
reg->val = ths7303_read(sd, reg->reg);
return 0;
@@ -247,13 +229,6 @@
static int ths7303_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
ths7303_write(sd, reg->reg, reg->val);
return 0;
}
@@ -340,7 +315,6 @@
}
static const struct v4l2_subdev_core_ops ths7303_core_ops = {
- .g_chip_ident = ths7303_g_chip_ident,
.log_status = ths7303_log_status,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = ths7303_g_register,
@@ -353,32 +327,6 @@
.video = &ths7303_video_ops,
};
-static int ths7303_setup(struct v4l2_subdev *sd)
-{
- struct ths7303_state *state = to_state(sd);
- struct ths7303_platform_data *pdata = &state->pdata;
- int ret;
- u8 mask;
-
- state->stream_on = pdata->init_enable;
-
- mask = state->stream_on ? 0xff : 0xf8;
-
- ret = ths7303_write(sd, THS7303_CHANNEL_1, pdata->ch_1 & mask);
- if (ret)
- return ret;
-
- ret = ths7303_write(sd, THS7303_CHANNEL_2, pdata->ch_2 & mask);
- if (ret)
- return ret;
-
- ret = ths7303_write(sd, THS7303_CHANNEL_3, pdata->ch_3 & mask);
- if (ret)
- return ret;
-
- return 0;
-}
-
static int ths7303_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -386,6 +334,11 @@
struct ths7303_state *state;
struct v4l2_subdev *sd;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -397,20 +350,14 @@
if (!state)
return -ENOMEM;
- if (!pdata)
- v4l_warn(client, "No platform data, using default data!\n");
- else
- state->pdata = *pdata;
-
+ state->pdata = pdata;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &ths7303_ops);
- /* store the driver data to differntiate the chip */
- state->driver_data = (int)id->driver_data;
-
- if (ths7303_setup(sd) < 0) {
- v4l_err(client, "init failed\n");
- return -EIO;
+ /* set to default 480I_576I filter mode */
+ if (ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I) < 0) {
+ v4l_err(client, "Setting to 480I_576I filter mode failed!\n");
+ return -EINVAL;
}
return 0;
@@ -426,8 +373,8 @@
}
static const struct i2c_device_id ths7303_id[] = {
- {"ths7303", V4L2_IDENT_THS7303},
- {"ths7353", V4L2_IDENT_THS7353},
+ {"ths7303", 0},
+ {"ths7353", 0},
{},
};
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
new file mode 100644
index 0000000..a24f90c
--- /dev/null
+++ b/drivers/media/i2c/ths8200.c
@@ -0,0 +1,556 @@
+/*
+ * ths8200 - Texas Instruments THS8200 video encoder driver
+ *
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/v4l2-dv-timings.h>
+
+#include <media/v4l2-device.h>
+
+#include "ths8200_regs.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+MODULE_DESCRIPTION("Texas Instruments THS8200 video encoder driver");
+MODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>");
+MODULE_AUTHOR("Martin Bugge <martin.bugge@cisco.com>");
+MODULE_LICENSE("GPL v2");
+
+struct ths8200_state {
+ struct v4l2_subdev sd;
+ uint8_t chip_version;
+ /* Is the ths8200 powered on? */
+ bool power_on;
+ struct v4l2_dv_timings dv_timings;
+};
+
+static const struct v4l2_dv_timings ths8200_timings[] = {
+ V4L2_DV_BT_CEA_720X480P59_94,
+ V4L2_DV_BT_CEA_1280X720P24,
+ V4L2_DV_BT_CEA_1280X720P25,
+ V4L2_DV_BT_CEA_1280X720P30,
+ V4L2_DV_BT_CEA_1280X720P50,
+ V4L2_DV_BT_CEA_1280X720P60,
+ V4L2_DV_BT_CEA_1920X1080P24,
+ V4L2_DV_BT_CEA_1920X1080P25,
+ V4L2_DV_BT_CEA_1920X1080P30,
+ V4L2_DV_BT_CEA_1920X1080P50,
+ V4L2_DV_BT_CEA_1920X1080P60,
+};
+
+static inline struct ths8200_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ths8200_state, sd);
+}
+
+static inline unsigned hblanking(const struct v4l2_bt_timings *t)
+{
+ return t->hfrontporch + t->hsync + t->hbackporch;
+}
+
+static inline unsigned htotal(const struct v4l2_bt_timings *t)
+{
+ return t->width + t->hfrontporch + t->hsync + t->hbackporch;
+}
+
+static inline unsigned vblanking(const struct v4l2_bt_timings *t)
+{
+ return t->vfrontporch + t->vsync + t->vbackporch;
+}
+
+static inline unsigned vtotal(const struct v4l2_bt_timings *t)
+{
+ return t->height + t->vfrontporch + t->vsync + t->vbackporch;
+}
+
+static int ths8200_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int ths8200_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret == 0)
+ return 0;
+ }
+ v4l2_err(sd, "I2C Write Problem\n");
+ return ret;
+}
+
+/* To set specific bits in the register, a clear-mask is given (to be AND-ed),
+ * and then the value-mask (to be OR-ed).
+ */
+static inline void
+ths8200_write_and_or(struct v4l2_subdev *sd, u8 reg,
+ uint8_t clr_mask, uint8_t val_mask)
+{
+ ths8200_write(sd, reg, (ths8200_read(sd, reg) & clr_mask) | val_mask);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+static int ths8200_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ reg->val = ths8200_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+
+ return 0;
+}
+
+static int ths8200_s_register(struct v4l2_subdev *sd,
+ const struct v4l2_dbg_register *reg)
+{
+ ths8200_write(sd, reg->reg & 0xff, reg->val & 0xff);
+
+ return 0;
+}
+#endif
+
+static void ths8200_print_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings,
+ const char *txt, bool detailed)
+{
+ struct v4l2_bt_timings *bt = &timings->bt;
+ u32 htot, vtot;
+
+ if (timings->type != V4L2_DV_BT_656_1120)
+ return;
+
+ htot = htotal(bt);
+ vtot = vtotal(bt);
+
+ v4l2_info(sd, "%s %dx%d%s%d (%dx%d)",
+ txt, bt->width, bt->height, bt->interlaced ? "i" : "p",
+ (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0,
+ htot, vtot);
+
+ if (detailed) {
+ v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n",
+ bt->hfrontporch,
+ (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-",
+ bt->hsync, bt->hbackporch);
+ v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n",
+ bt->vfrontporch,
+ (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-",
+ bt->vsync, bt->vbackporch);
+ v4l2_info(sd,
+ " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n",
+ bt->pixelclock, bt->flags, bt->standards);
+ }
+}
+
+static int ths8200_log_status(struct v4l2_subdev *sd)
+{
+ struct ths8200_state *state = to_state(sd);
+ uint8_t reg_03 = ths8200_read(sd, THS8200_CHIP_CTL);
+
+ v4l2_info(sd, "----- Chip status -----\n");
+ v4l2_info(sd, "version: %u\n", state->chip_version);
+ v4l2_info(sd, "power: %s\n", (reg_03 & 0x0c) ? "off" : "on");
+ v4l2_info(sd, "reset: %s\n", (reg_03 & 0x01) ? "off" : "on");
+ v4l2_info(sd, "test pattern: %s\n",
+ (reg_03 & 0x20) ? "enabled" : "disabled");
+ v4l2_info(sd, "format: %ux%u\n",
+ ths8200_read(sd, THS8200_DTG2_PIXEL_CNT_MSB) * 256 +
+ ths8200_read(sd, THS8200_DTG2_PIXEL_CNT_LSB),
+ (ths8200_read(sd, THS8200_DTG2_LINE_CNT_MSB) & 0x07) * 256 +
+ ths8200_read(sd, THS8200_DTG2_LINE_CNT_LSB));
+ ths8200_print_timings(sd, &state->dv_timings,
+ "Configured format:", true);
+
+ return 0;
+}
+
+/* Power up/down ths8200 */
+static int ths8200_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ths8200_state *state = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off");
+
+ state->power_on = on;
+
+ /* Power up/down - leave in reset state until input video is present */
+ ths8200_write_and_or(sd, THS8200_CHIP_CTL, 0xf2, (on ? 0x00 : 0x0c));
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops ths8200_core_ops = {
+ .log_status = ths8200_log_status,
+ .s_power = ths8200_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ths8200_g_register,
+ .s_register = ths8200_s_register,
+#endif
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int ths8200_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ths8200_state *state = to_state(sd);
+
+ if (enable && !state->power_on)
+ ths8200_s_power(sd, true);
+
+ ths8200_write_and_or(sd, THS8200_CHIP_CTL, 0xfe,
+ (enable ? 0x01 : 0x00));
+
+ v4l2_dbg(1, debug, sd, "%s: %sable\n",
+ __func__, (enable ? "en" : "dis"));
+
+ return 0;
+}
+
+static void ths8200_core_init(struct v4l2_subdev *sd)
+{
+ /* setup clocks */
+ ths8200_write_and_or(sd, THS8200_CHIP_CTL, 0x3f, 0xc0);
+
+ /**** Data path control (DATA) ****/
+ /* Set FSADJ 700 mV,
+ * bypass 422-444 interpolation,
+ * input format 30 bit RGB444
+ */
+ ths8200_write(sd, THS8200_DATA_CNTL, 0x70);
+
+ /* DTG Mode (Video blocked during blanking
+ * VESA slave
+ */
+ ths8200_write(sd, THS8200_DTG1_MODE, 0x87);
+
+ /**** Display Timing Generator Control, Part 1 (DTG1). ****/
+
+ /* Disable embedded syncs on the output by setting
+ * the amplitude to zero for all channels.
+ */
+ ths8200_write(sd, THS8200_DTG1_Y_SYNC_MSB, 0x2a);
+ ths8200_write(sd, THS8200_DTG1_CBCR_SYNC_MSB, 0x2a);
+}
+
+static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt)
+{
+ uint8_t polarity = 0;
+ uint16_t line_start_active_video = (bt->vsync + bt->vbackporch);
+ uint16_t line_start_front_porch = (vtotal(bt) - bt->vfrontporch);
+
+ /*** System ****/
+ /* Set chip in reset while it is configured */
+ ths8200_s_stream(sd, false);
+
+ /* configure video output timings */
+ ths8200_write(sd, THS8200_DTG1_SPEC_A, bt->hsync);
+ ths8200_write(sd, THS8200_DTG1_SPEC_B, bt->hfrontporch);
+
+ /* Zero for progressive scan formats.*/
+ if (!bt->interlaced)
+ ths8200_write(sd, THS8200_DTG1_SPEC_C, 0x00);
+
+ /* Distance from leading edge of h sync to start of active video.
+ * MSB in 0x2b
+ */
+ ths8200_write(sd, THS8200_DTG1_SPEC_D_LSB,
+ (bt->hbackporch + bt->hsync) & 0xff);
+ /* Zero for SDTV-mode. MSB in 0x2b */
+ ths8200_write(sd, THS8200_DTG1_SPEC_E_LSB, 0x00);
+ /*
+ * MSB for dtg1_spec(d/e/h). See comment for
+ * corresponding LSB registers.
+ */
+ ths8200_write(sd, THS8200_DTG1_SPEC_DEH_MSB,
+ ((bt->hbackporch + bt->hsync) & 0x100) >> 1);
+
+ /* h front porch */
+ ths8200_write(sd, THS8200_DTG1_SPEC_K_LSB, (bt->hfrontporch) & 0xff);
+ ths8200_write(sd, THS8200_DTG1_SPEC_K_MSB,
+ ((bt->hfrontporch) & 0x700) >> 8);
+
+ /* Half the line length. Used to calculate SDTV line types. */
+ ths8200_write(sd, THS8200_DTG1_SPEC_G_LSB, (htotal(bt)/2) & 0xff);
+ ths8200_write(sd, THS8200_DTG1_SPEC_G_MSB,
+ ((htotal(bt)/2) >> 8) & 0x0f);
+
+ /* Total pixels per line (ex. 720p: 1650) */
+ ths8200_write(sd, THS8200_DTG1_TOT_PIXELS_MSB, htotal(bt) >> 8);
+ ths8200_write(sd, THS8200_DTG1_TOT_PIXELS_LSB, htotal(bt) & 0xff);
+
+ /* Frame height and field height */
+ /* Field height should be programmed higher than frame_size for
+ * progressive scan formats
+ */
+ ths8200_write(sd, THS8200_DTG1_FRAME_FIELD_SZ_MSB,
+ ((vtotal(bt) >> 4) & 0xf0) + 0x7);
+ ths8200_write(sd, THS8200_DTG1_FRAME_SZ_LSB, vtotal(bt) & 0xff);
+
+ /* Should be programmed higher than frame_size
+ * for progressive formats
+ */
+ if (!bt->interlaced)
+ ths8200_write(sd, THS8200_DTG1_FIELD_SZ_LSB, 0xff);
+
+ /**** Display Timing Generator Control, Part 2 (DTG2). ****/
+ /* Set breakpoint line numbers and types
+ * THS8200 generates line types with different properties. A line type
+ * that sets all the RGB-outputs to zero is used in the blanking areas,
+ * while a line type that enable the RGB-outputs is used in active video
+ * area. The line numbers for start of active video, start of front
+ * porch and after the last line in the frame must be set with the
+ * corresponding line types.
+ *
+ * Line types:
+ * 0x9 - Full normal sync pulse: Blocks data when dtg1_pass is off.
+ * Used in blanking area.
+ * 0x0 - Active video: Video data is always passed. Used in active
+ * video area.
+ */
+ ths8200_write_and_or(sd, THS8200_DTG2_BP1_2_MSB, 0x88,
+ ((line_start_active_video >> 4) & 0x70) +
+ ((line_start_front_porch >> 8) & 0x07));
+ ths8200_write(sd, THS8200_DTG2_BP3_4_MSB, ((vtotal(bt)) >> 4) & 0x70);
+ ths8200_write(sd, THS8200_DTG2_BP1_LSB, line_start_active_video & 0xff);
+ ths8200_write(sd, THS8200_DTG2_BP2_LSB, line_start_front_porch & 0xff);
+ ths8200_write(sd, THS8200_DTG2_BP3_LSB, (vtotal(bt)) & 0xff);
+
+ /* line types */
+ ths8200_write(sd, THS8200_DTG2_LINETYPE1, 0x90);
+ ths8200_write(sd, THS8200_DTG2_LINETYPE2, 0x90);
+
+ /* h sync width transmitted */
+ ths8200_write(sd, THS8200_DTG2_HLENGTH_LSB, bt->hsync & 0xff);
+ ths8200_write_and_or(sd, THS8200_DTG2_HLENGTH_LSB_HDLY_MSB, 0x3f,
+ (bt->hsync >> 2) & 0xc0);
+
+ /* The pixel value h sync is asserted on */
+ ths8200_write_and_or(sd, THS8200_DTG2_HLENGTH_LSB_HDLY_MSB, 0xe0,
+ (htotal(bt) >> 8) & 0x1f);
+ ths8200_write(sd, THS8200_DTG2_HLENGTH_HDLY_LSB, htotal(bt));
+
+ /* v sync width transmitted */
+ ths8200_write(sd, THS8200_DTG2_VLENGTH1_LSB, (bt->vsync) & 0xff);
+ ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0x3f,
+ ((bt->vsync) >> 2) & 0xc0);
+
+ /* The pixel value v sync is asserted on */
+ ths8200_write_and_or(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, 0xf8,
+ (vtotal(bt)>>8) & 0x7);
+ ths8200_write(sd, THS8200_DTG2_VDLY1_LSB, vtotal(bt));
+
+ /* For progressive video vlength2 must be set to all 0 and vdly2 must
+ * be set to all 1.
+ */
+ ths8200_write(sd, THS8200_DTG2_VLENGTH2_LSB, 0x00);
+ ths8200_write(sd, THS8200_DTG2_VLENGTH2_MSB_VDLY2_MSB, 0x07);
+ ths8200_write(sd, THS8200_DTG2_VDLY2_LSB, 0xff);
+
+ /* Internal delay factors to synchronize the sync pulses and the data */
+ /* Experimental values delays (hor 4, ver 1) */
+ ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_MSB, (htotal(bt)>>8) & 0x1f);
+ ths8200_write(sd, THS8200_DTG2_HS_IN_DLY_LSB, (htotal(bt) - 4) & 0xff);
+ ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_MSB, 0);
+ ths8200_write(sd, THS8200_DTG2_VS_IN_DLY_LSB, 1);
+
+ /* Polarity of received and transmitted sync signals */
+ if (bt->polarities & V4L2_DV_HSYNC_POS_POL) {
+ polarity |= 0x01; /* HS_IN */
+ polarity |= 0x08; /* HS_OUT */
+ }
+ if (bt->polarities & V4L2_DV_VSYNC_POS_POL) {
+ polarity |= 0x02; /* VS_IN */
+ polarity |= 0x10; /* VS_OUT */
+ }
+
+ /* RGB mode, no embedded timings */
+ /* Timing of video input bus is derived from HS, VS, and FID dedicated
+ * inputs
+ */
+ ths8200_write(sd, THS8200_DTG2_CNTL, 0x47 | polarity);
+
+ /* leave reset */
+ ths8200_s_stream(sd, true);
+
+ v4l2_dbg(1, debug, sd, "%s: frame %dx%d, polarity %d\n"
+ "horizontal: front porch %d, back porch %d, sync %d\n"
+ "vertical: sync %d\n", __func__, htotal(bt), vtotal(bt),
+ polarity, bt->hfrontporch, bt->hbackporch,
+ bt->hsync, bt->vsync);
+}
+
+static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct ths8200_state *state = to_state(sd);
+ int i;
+
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+ if (timings->type != V4L2_DV_BT_656_1120)
+ return -EINVAL;
+
+ /* TODO Support interlaced formats */
+ if (timings->bt.interlaced) {
+ v4l2_dbg(1, debug, sd, "TODO Support interlaced formats\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ths8200_timings); i++) {
+ if (v4l_match_dv_timings(&ths8200_timings[i], timings, 10))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ths8200_timings)) {
+ v4l2_dbg(1, debug, sd, "Unsupported format\n");
+ return -EINVAL;
+ }
+
+ timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS;
+
+ /* save timings */
+ state->dv_timings = *timings;
+
+ ths8200_setup(sd, &timings->bt);
+
+ return 0;
+}
+
+static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct ths8200_state *state = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+ *timings = state->dv_timings;
+
+ return 0;
+}
+
+static int ths8200_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ /* Check requested format index is within range */
+ if (timings->index >= ARRAY_SIZE(ths8200_timings))
+ return -EINVAL;
+
+ timings->timings = ths8200_timings[timings->index];
+
+ return 0;
+}
+
+static int ths8200_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ cap->type = V4L2_DV_BT_656_1120;
+ cap->bt.max_width = 1920;
+ cap->bt.max_height = 1080;
+ cap->bt.min_pixelclock = 27000000;
+ cap->bt.max_pixelclock = 148500000;
+ cap->bt.standards = V4L2_DV_BT_STD_CEA861;
+ cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE;
+
+ return 0;
+}
+
+/* Specific video subsystem operation handlers */
+static const struct v4l2_subdev_video_ops ths8200_video_ops = {
+ .s_stream = ths8200_s_stream,
+ .s_dv_timings = ths8200_s_dv_timings,
+ .g_dv_timings = ths8200_g_dv_timings,
+ .enum_dv_timings = ths8200_enum_dv_timings,
+ .dv_timings_cap = ths8200_dv_timings_cap,
+};
+
+/* V4L2 top level operation handlers */
+static const struct v4l2_subdev_ops ths8200_ops = {
+ .core = &ths8200_core_ops,
+ .video = &ths8200_video_ops,
+};
+
+static int ths8200_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ths8200_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &ths8200_ops);
+
+ state->chip_version = ths8200_read(sd, THS8200_VERSION);
+ v4l2_dbg(1, debug, sd, "chip version 0x%x\n", state->chip_version);
+
+ ths8200_core_init(sd);
+
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+
+ return 0;
+}
+
+static int ths8200_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+
+ ths8200_s_power(sd, false);
+
+ v4l2_device_unregister_subdev(sd);
+
+ return 0;
+}
+
+static struct i2c_device_id ths8200_id[] = {
+ { "ths8200", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ths8200_id);
+
+static struct i2c_driver ths8200_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ths8200",
+ },
+ .probe = ths8200_probe,
+ .remove = ths8200_remove,
+ .id_table = ths8200_id,
+};
+
+module_i2c_driver(ths8200_driver);
diff --git a/drivers/media/i2c/ths8200_regs.h b/drivers/media/i2c/ths8200_regs.h
new file mode 100644
index 0000000..6bc9fd1
--- /dev/null
+++ b/drivers/media/i2c/ths8200_regs.h
@@ -0,0 +1,161 @@
+/*
+ * ths8200 - Texas Instruments THS8200 video encoder driver
+ *
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef THS8200_REGS_H
+#define THS8200_REGS_H
+
+/* Register offset macros */
+#define THS8200_VERSION 0x02
+#define THS8200_CHIP_CTL 0x03
+#define THS8200_CSC_R11 0x04
+#define THS8200_CSC_R12 0x05
+#define THS8200_CSC_R21 0x06
+#define THS8200_CSC_R22 0x07
+#define THS8200_CSC_R31 0x08
+#define THS8200_CSC_R32 0x09
+#define THS8200_CSC_G11 0x0a
+#define THS8200_CSC_G12 0x0b
+#define THS8200_CSC_G21 0x0c
+#define THS8200_CSC_G22 0x0d
+#define THS8200_CSC_G31 0x0e
+#define THS8200_CSC_G32 0x0f
+#define THS8200_CSC_B11 0x10
+#define THS8200_CSC_B12 0x11
+#define THS8200_CSC_B21 0x12
+#define THS8200_CSC_B22 0x13
+#define THS8200_CSC_B31 0x14
+#define THS8200_CSC_B32 0x15
+#define THS8200_CSC_OFFS1 0x16
+#define THS8200_CSC_OFFS12 0x17
+#define THS8200_CSC_OFFS23 0x18
+#define THS8200_CSC_OFFS3 0x19
+#define THS8200_TST_CNTL1 0x1a
+#define THS8200_TST_CNTL2 0x1b
+#define THS8200_DATA_CNTL 0x1c
+#define THS8200_DTG1_Y_SYNC1_LSB 0x1d
+#define THS8200_DTG1_Y_SYNC2_LSB 0x1e
+#define THS8200_DTG1_Y_SYNC3_LSB 0x1f
+#define THS8200_DTG1_CBCR_SYNC1_LSB 0x20
+#define THS8200_DTG1_CBCR_SYNC2_LSB 0x21
+#define THS8200_DTG1_CBCR_SYNC3_LSB 0x22
+#define THS8200_DTG1_Y_SYNC_MSB 0x23
+#define THS8200_DTG1_CBCR_SYNC_MSB 0x24
+#define THS8200_DTG1_SPEC_A 0x25
+#define THS8200_DTG1_SPEC_B 0x26
+#define THS8200_DTG1_SPEC_C 0x27
+#define THS8200_DTG1_SPEC_D_LSB 0x28
+#define THS8200_DTG1_SPEC_D1 0x29
+#define THS8200_DTG1_SPEC_E_LSB 0x2a
+#define THS8200_DTG1_SPEC_DEH_MSB 0x2b
+#define THS8200_DTG1_SPEC_H_LSB 0x2c
+#define THS8200_DTG1_SPEC_I_MSB 0x2d
+#define THS8200_DTG1_SPEC_I_LSB 0x2e
+#define THS8200_DTG1_SPEC_K_LSB 0x2f
+#define THS8200_DTG1_SPEC_K_MSB 0x30
+#define THS8200_DTG1_SPEC_K1 0x31
+#define THS8200_DTG1_SPEC_G_LSB 0x32
+#define THS8200_DTG1_SPEC_G_MSB 0x33
+#define THS8200_DTG1_TOT_PIXELS_MSB 0x34
+#define THS8200_DTG1_TOT_PIXELS_LSB 0x35
+#define THS8200_DTG1_FLD_FLIP_LINECNT_MSB 0x36
+#define THS8200_DTG1_LINECNT_LSB 0x37
+#define THS8200_DTG1_MODE 0x38
+#define THS8200_DTG1_FRAME_FIELD_SZ_MSB 0x39
+#define THS8200_DTG1_FRAME_SZ_LSB 0x3a
+#define THS8200_DTG1_FIELD_SZ_LSB 0x3b
+#define THS8200_DTG1_VESA_CBAR_SIZE 0x3c
+#define THS8200_DAC_CNTL_MSB 0x3d
+#define THS8200_DAC1_CNTL_LSB 0x3e
+#define THS8200_DAC2_CNTL_LSB 0x3f
+#define THS8200_DAC3_CNTL_LSB 0x40
+#define THS8200_CSM_CLIP_GY_LOW 0x41
+#define THS8200_CSM_CLIP_BCB_LOW 0x42
+#define THS8200_CSM_CLIP_RCR_LOW 0x43
+#define THS8200_CSM_CLIP_GY_HIGH 0x44
+#define THS8200_CSM_CLIP_BCB_HIGH 0x45
+#define THS8200_CSM_CLIP_RCR_HIGH 0x46
+#define THS8200_CSM_SHIFT_GY 0x47
+#define THS8200_CSM_SHIFT_BCB 0x48
+#define THS8200_CSM_SHIFT_RCR 0x49
+#define THS8200_CSM_GY_CNTL_MULT_MSB 0x4a
+#define THS8200_CSM_MULT_BCB_RCR_MSB 0x4b
+#define THS8200_CSM_MULT_GY_LSB 0x4c
+#define THS8200_CSM_MULT_BCB_LSB 0x4d
+#define THS8200_CSM_MULT_RCR_LSB 0x4e
+#define THS8200_CSM_MULT_RCR_BCB_CNTL 0x4f
+#define THS8200_CSM_MULT_RCR_LSB 0x4e
+#define THS8200_DTG2_BP1_2_MSB 0x50
+#define THS8200_DTG2_BP3_4_MSB 0x51
+#define THS8200_DTG2_BP5_6_MSB 0x52
+#define THS8200_DTG2_BP7_8_MSB 0x53
+#define THS8200_DTG2_BP9_10_MSB 0x54
+#define THS8200_DTG2_BP11_12_MSB 0x55
+#define THS8200_DTG2_BP13_14_MSB 0x56
+#define THS8200_DTG2_BP15_16_MSB 0x57
+#define THS8200_DTG2_BP1_LSB 0x58
+#define THS8200_DTG2_BP2_LSB 0x59
+#define THS8200_DTG2_BP3_LSB 0x5a
+#define THS8200_DTG2_BP4_LSB 0x5b
+#define THS8200_DTG2_BP5_LSB 0x5c
+#define THS8200_DTG2_BP6_LSB 0x5d
+#define THS8200_DTG2_BP7_LSB 0x5e
+#define THS8200_DTG2_BP8_LSB 0x5f
+#define THS8200_DTG2_BP9_LSB 0x60
+#define THS8200_DTG2_BP10_LSB 0x61
+#define THS8200_DTG2_BP11_LSB 0x62
+#define THS8200_DTG2_BP12_LSB 0x63
+#define THS8200_DTG2_BP13_LSB 0x64
+#define THS8200_DTG2_BP14_LSB 0x65
+#define THS8200_DTG2_BP15_LSB 0x66
+#define THS8200_DTG2_BP16_LSB 0x67
+#define THS8200_DTG2_LINETYPE1 0x68
+#define THS8200_DTG2_LINETYPE2 0x69
+#define THS8200_DTG2_LINETYPE3 0x6a
+#define THS8200_DTG2_LINETYPE4 0x6b
+#define THS8200_DTG2_LINETYPE5 0x6c
+#define THS8200_DTG2_LINETYPE6 0x6d
+#define THS8200_DTG2_LINETYPE7 0x6e
+#define THS8200_DTG2_LINETYPE8 0x6f
+#define THS8200_DTG2_HLENGTH_LSB 0x70
+#define THS8200_DTG2_HLENGTH_LSB_HDLY_MSB 0x71
+#define THS8200_DTG2_HLENGTH_HDLY_LSB 0x72
+#define THS8200_DTG2_VLENGTH1_LSB 0x73
+#define THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB 0x74
+#define THS8200_DTG2_VDLY1_LSB 0x75
+#define THS8200_DTG2_VLENGTH2_LSB 0x76
+#define THS8200_DTG2_VLENGTH2_MSB_VDLY2_MSB 0x77
+#define THS8200_DTG2_VDLY2_LSB 0x78
+#define THS8200_DTG2_HS_IN_DLY_MSB 0x79
+#define THS8200_DTG2_HS_IN_DLY_LSB 0x7a
+#define THS8200_DTG2_VS_IN_DLY_MSB 0x7b
+#define THS8200_DTG2_VS_IN_DLY_LSB 0x7c
+#define THS8200_DTG2_PIXEL_CNT_MSB 0x7d
+#define THS8200_DTG2_PIXEL_CNT_LSB 0x7e
+#define THS8200_DTG2_LINE_CNT_MSB 0x7f
+#define THS8200_DTG2_LINE_CNT_LSB 0x80
+#define THS8200_DTG2_CNTL 0x82
+#define THS8200_CGMS_CNTL_HEADER 0x83
+#define THS8200_CGMS_PAYLOAD_MSB 0x84
+#define THS8200_CGMS_PAYLOAD_LSB 0x85
+#define THS8200_MISC_PPL_LSB 0x86
+#define THS8200_MISC_PPL_MSB 0x87
+#define THS8200_MISC_LPF_MSB 0x88
+#define THS8200_MISC_LPF_LSB 0x89
+
+#endif /* THS8200_REGS_H */
diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c
index 809a75a..ef87f7b 100644
--- a/drivers/media/i2c/tlv320aic23b.c
+++ b/drivers/media/i2c/tlv320aic23b.c
@@ -162,7 +162,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -191,7 +191,6 @@
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
v4l2_ctrl_handler_setup(&state->hdl);
@@ -205,7 +204,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index e0634c8..d76c53a8 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -38,7 +38,6 @@
#include <media/tvaudio.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/i2c-addr.h>
@@ -1838,13 +1837,6 @@
return 0;
}
-static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0);
-}
-
static int tvaudio_log_status(struct v4l2_subdev *sd)
{
struct CHIPSTATE *chip = to_state(sd);
@@ -1863,7 +1855,6 @@
static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
.log_status = tvaudio_log_status,
- .g_chip_ident = tvaudio_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -1910,7 +1901,7 @@
printk("\n");
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
sd = &chip->sd;
@@ -1930,7 +1921,6 @@
}
if (desc->name == NULL) {
v4l2_dbg(1, debug, sd, "no matching chip description found\n");
- kfree(chip);
return -EIO;
}
v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
@@ -2001,7 +1991,6 @@
int err = chip->hdl.error;
v4l2_ctrl_handler_free(&chip->hdl);
- kfree(chip);
return err;
}
/* set controls to the default values */
@@ -2044,7 +2033,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&chip->hdl);
- kfree(chip);
return 0;
}
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index ab8f3fe..9c6d66a 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -39,7 +39,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-mediabus.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-of.h>
#include <media/v4l2-ctrls.h>
#include <media/tvp514x.h>
#include <media/media-entity.h>
@@ -123,6 +123,8 @@
/* mc related members */
struct media_pad pad;
struct v4l2_mbus_framefmt format;
+
+ struct tvp514x_reg *int_seq;
};
/* TVP514x default register values */
@@ -543,8 +545,6 @@
if (std_id == NULL)
return -EINVAL;
- *std_id = V4L2_STD_UNKNOWN;
-
/* To query the standard the TVP514x must power on the ADCs. */
if (!decoder->streaming) {
tvp514x_s_stream(sd, 1);
@@ -553,8 +553,10 @@
/* query the current standard */
current_std = tvp514x_query_current_std(sd);
- if (current_std == STD_INVALID)
+ if (current_std == STD_INVALID) {
+ *std_id = V4L2_STD_UNKNOWN;
return 0;
+ }
input_sel = decoder->input;
@@ -595,10 +597,12 @@
}
/* check whether signal is locked */
sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1);
- if (lock_mask != (sync_lock_status & lock_mask))
+ if (lock_mask != (sync_lock_status & lock_mask)) {
+ *std_id = V4L2_STD_UNKNOWN;
return 0; /* No input detected */
+ }
- *std_id = decoder->std_list[current_std].standard.id;
+ *std_id &= decoder->std_list[current_std].standard.id;
v4l2_dbg(1, debug, sd, "Current STD: %s\n",
decoder->std_list[current_std].standard.name);
@@ -862,7 +866,6 @@
static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable)
{
int err = 0;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct tvp514x_decoder *decoder = to_decoder(sd);
if (decoder->streaming == enable)
@@ -882,11 +885,8 @@
}
case 1:
{
- struct tvp514x_reg *int_seq = (struct tvp514x_reg *)
- client->driver->id_table->driver_data;
-
/* Power Up Sequence */
- err = tvp514x_write_regs(sd, int_seq);
+ err = tvp514x_write_regs(sd, decoder->int_seq);
if (err) {
v4l2_err(sd, "Unable to turn on decoder\n");
return err;
@@ -1055,6 +1055,42 @@
};
+static struct tvp514x_platform_data *
+tvp514x_get_pdata(struct i2c_client *client)
+{
+ struct tvp514x_platform_data *pdata;
+ struct v4l2_of_endpoint bus_cfg;
+ struct device_node *endpoint;
+ unsigned int flags;
+
+ if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
+ return client->dev.platform_data;
+
+ endpoint = v4l2_of_get_next_endpoint(client->dev.of_node, NULL);
+ if (!endpoint)
+ return NULL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ goto done;
+
+ v4l2_of_parse_endpoint(endpoint, &bus_cfg);
+ flags = bus_cfg.bus.parallel.flags;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ pdata->hs_polarity = 1;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ pdata->vs_polarity = 1;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ pdata->clk_polarity = 1;
+
+done:
+ of_node_put(endpoint);
+ return pdata;
+}
+
/**
* tvp514x_probe() - decoder driver i2c probe handler
* @client: i2c driver client device structure
@@ -1066,19 +1102,20 @@
static int
tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
+ struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client);
struct tvp514x_decoder *decoder;
struct v4l2_subdev *sd;
int ret;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- if (!client->dev.platform_data) {
- v4l2_err(client, "No platform data!!\n");
- return -ENODEV;
- }
-
decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (!decoder)
return -ENOMEM;
@@ -1089,8 +1126,10 @@
memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default,
sizeof(tvp514x_reg_list_default));
+ decoder->int_seq = (struct tvp514x_reg *)id->driver_data;
+
/* Copy board specific information here */
- decoder->pdata = client->dev.platform_data;
+ decoder->pdata = pdata;
/**
* Fetch platform specific data, and configure the
@@ -1109,7 +1148,6 @@
/* Register with V4L2 layer as slave device */
sd = &decoder->sd;
v4l2_i2c_subdev_init(sd, client, &tvp514x_ops);
- strlcpy(sd->name, TVP514X_MODULE_NAME, sizeof(sd->name));
#if defined(CONFIG_MEDIA_CONTROLLER)
decoder->pad.flags = MEDIA_PAD_FL_SOURCE;
@@ -1120,7 +1158,6 @@
if (ret < 0) {
v4l2_err(sd, "%s decoder driver failed to register !!\n",
sd->name);
- kfree(decoder);
return ret;
}
#endif
@@ -1231,8 +1268,20 @@
MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id tvp514x_of_match[] = {
+ { .compatible = "ti,tvp5146", },
+ { .compatible = "ti,tvp5146m2", },
+ { .compatible = "ti,tvp5147", },
+ { .compatible = "ti,tvp5147m1", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, tvp514x_of_match);
+#endif
+
static struct i2c_driver tvp514x_driver = {
.driver = {
+ .of_match_table = of_match_ptr(tvp514x_of_match),
.owner = THIS_MODULE,
.name = TVP514X_MODULE_NAME,
},
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 485159a..89c0b13 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/tvp5150.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include "tvp5150_reg.h"
@@ -727,13 +726,11 @@
/* First tests should be against specific std */
- if (std == V4L2_STD_ALL) {
- fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */
- } else if (std & V4L2_STD_NTSC_443) {
+ if (std == V4L2_STD_NTSC_443) {
fmt = VIDEO_STD_NTSC_4_43_BIT;
- } else if (std & V4L2_STD_PAL_M) {
+ } else if (std == V4L2_STD_PAL_M) {
fmt = VIDEO_STD_PAL_M_BIT;
- } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ } else if (std == V4L2_STD_PAL_N || std == V4L2_STD_PAL_Nc) {
fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
} else {
/* Then, test against generic ones */
@@ -1031,31 +1028,11 @@
return 0;
}
-static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- int rev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
- tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
- rev);
-}
-
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
int res;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
res = tvp5150_read(sd, reg->reg & 0xff);
if (res < 0) {
v4l2_err(sd, "%s: failed with error = %d\n", __func__, res);
@@ -1069,12 +1046,6 @@
static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
@@ -1098,7 +1069,6 @@
.log_status = tvp5150_log_status,
.s_std = tvp5150_s_std,
.reset = tvp5150_reset,
- .g_chip_ident = tvp5150_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = tvp5150_g_register,
.s_register = tvp5150_s_register,
@@ -1152,10 +1122,9 @@
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
- core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
- if (!core) {
+ core = devm_kzalloc(&c->dev, sizeof(*core), GFP_KERNEL);
+ if (!core)
return -ENOMEM;
- }
sd = &core->sd;
v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
@@ -1166,7 +1135,7 @@
for (i = 0; i < 4; i++) {
res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
if (res < 0)
- goto free_core;
+ return res;
tvp5150_id[i] = res;
}
@@ -1209,7 +1178,7 @@
if (core->hdl.error) {
res = core->hdl.error;
v4l2_ctrl_handler_free(&core->hdl);
- goto free_core;
+ return res;
}
v4l2_ctrl_handler_setup(&core->hdl);
@@ -1225,10 +1194,6 @@
if (debug > 1)
tvp5150_log_status(sd);
return 0;
-
-free_core:
- kfree(core);
- return res;
}
static int tvp5150_remove(struct i2c_client *c)
@@ -1242,7 +1207,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(to_tvp5150(sd));
return 0;
}
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 027809c..a4e4948 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -32,7 +32,6 @@
#include <linux/v4l2-dv-timings.h>
#include <media/tvp7002.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include "tvp7002_reg.h"
@@ -41,9 +40,6 @@
MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>");
MODULE_LICENSE("GPL");
-/* Module Name */
-#define TVP7002_MODULE_NAME "tvp7002"
-
/* I2C retry attempts */
#define I2C_RETRY_COUNT (5)
@@ -424,6 +420,7 @@
int streaming;
const struct tvp7002_timings_definition *current_timings;
+ struct media_pad pad;
};
/*
@@ -535,29 +532,6 @@
}
/*
- * tvp7002_g_chip_ident() - Get chip identification number
- * @sd: ptr to v4l2_subdev struct
- * @chip: ptr to v4l2_dbg_chip_ident struct
- *
- * Obtains the chip's identification number.
- * Returns zero or -EINVAL if read operation fails.
- */
-static int tvp7002_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- u8 rev;
- int error;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev);
-
- if (error < 0)
- return error;
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev);
-}
-
-/*
* tvp7002_write_inittab() - Write initialization values
* @sd: ptr to v4l2_subdev struct
* @regs: ptr to i2c_reg_value struct
@@ -738,23 +712,17 @@
*
* Get the value of a TVP7002 decoder device register.
* Returns zero when successful, -EINVAL if register read fails or
- * access to I2C client fails, -EPERM if the call is not allowed
- * by disabled CAP_SYS_ADMIN.
+ * access to I2C client fails.
*/
static int tvp7002_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 val;
int ret;
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
ret = tvp7002_read(sd, reg->reg & 0xff, &val);
reg->val = val;
+ reg->size = 1;
return ret;
}
@@ -764,19 +732,11 @@
* @reg: ptr to v4l2_dbg_register struct
*
* Get the value of a TVP7002 decoder device register.
- * Returns zero when successful, -EINVAL if register read fails or
- * -EPERM if call not allowed.
+ * Returns zero when successful, -EINVAL if register read fails.
*/
static int tvp7002_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff);
}
#endif
@@ -880,9 +840,67 @@
.s_ctrl = tvp7002_s_ctrl,
};
+/*
+ * tvp7002_enum_mbus_code() - Enum supported digital video format on pad
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @fh: file handle for the subdev
+ * @code: pointer to subdev enum mbus code struct
+ *
+ * Enumerate supported digital video formats for pad.
+ */
+static int
+tvp7002_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* Check requested format index is within range */
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_YUYV10_1X20;
+
+ return 0;
+}
+
+/*
+ * tvp7002_get_pad_format() - get video format on pad
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @fh: file handle for the subdev
+ * @fmt: pointer to subdev format struct
+ *
+ * get video format for pad.
+ */
+static int
+tvp7002_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct tvp7002 *tvp7002 = to_tvp7002(sd);
+
+ fmt->format.code = V4L2_MBUS_FMT_YUYV10_1X20;
+ fmt->format.width = tvp7002->current_timings->timings.bt.width;
+ fmt->format.height = tvp7002->current_timings->timings.bt.height;
+ fmt->format.field = tvp7002->current_timings->scanmode;
+ fmt->format.colorspace = tvp7002->current_timings->color_space;
+
+ return 0;
+}
+
+/*
+ * tvp7002_set_pad_format() - set video format on pad
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @fh: file handle for the subdev
+ * @fmt: pointer to subdev format struct
+ *
+ * set video format for pad.
+ */
+static int
+tvp7002_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ return tvp7002_get_pad_format(sd, fh, fmt);
+}
+
/* V4L2 core operation handlers */
static const struct v4l2_subdev_core_ops tvp7002_core_ops = {
- .g_chip_ident = tvp7002_g_chip_ident,
.log_status = tvp7002_log_status,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
@@ -910,10 +928,18 @@
.enum_mbus_fmt = tvp7002_enum_mbus_fmt,
};
+/* media pad related operation handlers */
+static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = {
+ .enum_mbus_code = tvp7002_enum_mbus_code,
+ .get_fmt = tvp7002_get_pad_format,
+ .set_fmt = tvp7002_set_pad_format,
+};
+
/* V4L2 top level operation handlers */
static const struct v4l2_subdev_ops tvp7002_ops = {
.core = &tvp7002_core_ops,
.video = &tvp7002_video_ops,
+ .pad = &tvp7002_pad_ops,
};
/*
@@ -993,19 +1019,34 @@
timings = device->current_timings->timings;
error = tvp7002_s_dv_timings(sd, &timings);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ device->pad.flags = MEDIA_PAD_FL_SOURCE;
+ device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ device->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+
+ error = media_entity_init(&device->sd.entity, 1, &device->pad, 0);
+ if (error < 0)
+ return error;
+#endif
+
v4l2_ctrl_handler_init(&device->hdl, 1);
v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops,
V4L2_CID_GAIN, 0, 255, 1, 0);
sd->ctrl_handler = &device->hdl;
if (device->hdl.error) {
- int err = device->hdl.error;
-
- v4l2_ctrl_handler_free(&device->hdl);
- return err;
+ error = device->hdl.error;
+ goto error;
}
v4l2_ctrl_handler_setup(&device->hdl);
return 0;
+
+error:
+ v4l2_ctrl_handler_free(&device->hdl);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&device->sd.entity);
+#endif
+ return error;
}
/*
@@ -1022,7 +1063,9 @@
v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter"
"on address 0x%x\n", c->addr);
-
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&device->sd.entity);
+#endif
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&device->hdl);
return 0;
diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c
index c5dc2c3b..f58607d 100644
--- a/drivers/media/i2c/tw2804.c
+++ b/drivers/media/i2c/tw2804.c
@@ -23,7 +23,6 @@
#include <linux/slab.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#define TW2804_REG_AUTOGAIN 0x02
@@ -368,8 +367,7 @@
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- state = kzalloc(sizeof(struct tw2804), GFP_KERNEL);
-
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -410,7 +408,6 @@
err = state->hdl.error;
if (err) {
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
@@ -427,7 +424,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c
index 87880b1..285b759 100644
--- a/drivers/media/i2c/tw9903.c
+++ b/drivers/media/i2c/tw9903.c
@@ -215,7 +215,7 @@
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- dec = kzalloc(sizeof(struct tw9903), GFP_KERNEL);
+ dec = devm_kzalloc(&client->dev, sizeof(*dec), GFP_KERNEL);
if (dec == NULL)
return -ENOMEM;
sd = &dec->sd;
@@ -233,7 +233,6 @@
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(dec);
return err;
}
@@ -242,7 +241,6 @@
if (write_regs(sd, initial_registers) < 0) {
v4l2_err(client, "error initializing TW9903\n");
- kfree(dec);
return -EINVAL;
}
@@ -255,7 +253,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&to_state(sd)->hdl);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c
index accd79e..f6bef25 100644
--- a/drivers/media/i2c/tw9906.c
+++ b/drivers/media/i2c/tw9906.c
@@ -183,7 +183,7 @@
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- dec = kzalloc(sizeof(struct tw9906), GFP_KERNEL);
+ dec = devm_kzalloc(&client->dev, sizeof(*dec), GFP_KERNEL);
if (dec == NULL)
return -ENOMEM;
sd = &dec->sd;
@@ -201,7 +201,6 @@
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(dec);
return err;
}
@@ -210,7 +209,6 @@
if (write_regs(sd, initial_registers) < 0) {
v4l2_err(client, "error initializing TW9906\n");
- kfree(dec);
return -EINVAL;
}
@@ -223,7 +221,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&to_state(sd)->hdl);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c
index 3af4085..081786d 100644
--- a/drivers/media/i2c/uda1342.c
+++ b/drivers/media/i2c/uda1342.c
@@ -69,7 +69,7 @@
dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n",
client->addr, adapter->name);
- sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = devm_kzalloc(&client->dev, sizeof(*sd), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
@@ -89,7 +89,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
return 0;
}
diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c
index f0a0921..d248e6a 100644
--- a/drivers/media/i2c/upd64031a.c
+++ b/drivers/media/i2c/upd64031a.c
@@ -27,7 +27,6 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/upd64031a.h>
/* --------------------- read registers functions define -------------------- */
@@ -147,13 +146,6 @@
return upd64031a_s_frequency(sd, NULL);
}
-static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0);
-}
-
static int upd64031a_log_status(struct v4l2_subdev *sd)
{
v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n",
@@ -164,12 +156,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = upd64031a_read(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
@@ -177,12 +163,6 @@
static int upd64031a_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
@@ -192,7 +172,6 @@
static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
.log_status = upd64031a_log_status,
- .g_chip_ident = upd64031a_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = upd64031a_g_register,
.s_register = upd64031a_s_register,
@@ -230,7 +209,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -249,7 +228,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c
index 343e021..3a152ce 100644
--- a/drivers/media/i2c/upd64083.c
+++ b/drivers/media/i2c/upd64083.c
@@ -27,7 +27,6 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/upd64083.h>
MODULE_DESCRIPTION("uPD64083 driver");
@@ -122,12 +121,6 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = upd64083_read(sd, reg->reg & 0xff);
reg->size = 1;
return 0;
@@ -135,24 +128,11 @@
static int upd64083_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff);
return 0;
}
#endif
-static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0);
-}
-
static int upd64083_log_status(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -169,7 +149,6 @@
static const struct v4l2_subdev_core_ops upd64083_core_ops = {
.log_status = upd64083_log_status,
- .g_chip_ident = upd64083_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = upd64083_g_register,
.s_register = upd64083_s_register,
@@ -202,7 +181,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -221,7 +200,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c
index e71f139..6a3a3ff 100644
--- a/drivers/media/i2c/vp27smpx.c
+++ b/drivers/media/i2c/vp27smpx.c
@@ -29,7 +29,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
MODULE_DESCRIPTION("vp27smpx driver");
MODULE_AUTHOR("Hans Verkuil");
@@ -112,13 +111,6 @@
return 0;
}
-static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0);
-}
-
static int vp27smpx_log_status(struct v4l2_subdev *sd)
{
struct vp27smpx_state *state = to_state(sd);
@@ -132,7 +124,6 @@
static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
.log_status = vp27smpx_log_status,
- .g_chip_ident = vp27smpx_g_chip_ident,
.s_std = vp27smpx_s_std,
};
@@ -169,7 +160,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -186,7 +177,6 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c
index 2f67b4c..ece90df 100644
--- a/drivers/media/i2c/vpx3220.c
+++ b/drivers/media/i2c/vpx3220.c
@@ -27,7 +27,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
@@ -49,7 +48,6 @@
unsigned char reg[255];
v4l2_std_id norm;
- int ident;
int input;
int enable;
};
@@ -297,7 +295,7 @@
static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
{
int res = V4L2_IN_ST_NO_SIGNAL, status;
- v4l2_std_id std = 0;
+ v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
status = vpx3220_fp_read(sd, 0x0f3);
@@ -314,19 +312,21 @@
case 0x10:
case 0x14:
case 0x18:
- std = V4L2_STD_PAL;
+ std &= V4L2_STD_PAL;
break;
case 0x08:
- std = V4L2_STD_SECAM;
+ std &= V4L2_STD_SECAM;
break;
case 0x04:
case 0x0c:
case 0x1c:
- std = V4L2_STD_NTSC;
+ std &= V4L2_STD_NTSC;
break;
}
+ } else {
+ std = V4L2_STD_UNKNOWN;
}
if (pstd)
*pstd = std;
@@ -442,14 +442,6 @@
return -EINVAL;
}
-static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct vpx3220 *decoder = to_vpx3220(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
@@ -457,7 +449,6 @@
};
static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
- .g_chip_ident = vpx3220_g_chip_ident,
.init = vpx3220_init,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
@@ -499,7 +490,7 @@
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL);
+ decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
if (decoder == NULL)
return -ENOMEM;
sd = &decoder->sd;
@@ -521,7 +512,6 @@
int err = decoder->hdl.error;
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
return err;
}
v4l2_ctrl_handler_setup(&decoder->hdl);
@@ -529,7 +519,6 @@
ver = i2c_smbus_read_byte_data(client, 0x00);
pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
i2c_smbus_read_byte_data(client, 0x01);
- decoder->ident = V4L2_IDENT_VPX3220A;
if (ver == 0xec) {
switch (pn) {
case 0x4680:
@@ -537,11 +526,9 @@
break;
case 0x4260:
name = "vpx3216b";
- decoder->ident = V4L2_IDENT_VPX3216B;
break;
case 0x4280:
name = "vpx3214c";
- decoder->ident = V4L2_IDENT_VPX3214C;
break;
}
}
@@ -566,7 +553,7 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
+
return 0;
}
diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index f366fad..25bdd93 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
@@ -27,7 +27,6 @@
#include <linux/types.h>
#include <linux/videodev2.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
@@ -722,27 +721,9 @@
return 0;
}
-static int vs6624_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- int rev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8)
- | vs6624_read(sd, VS6624_FW_VSN_MINOR);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev);
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->val = vs6624_read(sd, reg->reg & 0xffff);
reg->size = 1;
return 0;
@@ -750,12 +731,6 @@
static int vs6624_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, ®->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff);
return 0;
}
@@ -766,7 +741,6 @@
};
static const struct v4l2_subdev_core_ops vs6624_core_ops = {
- .g_chip_ident = vs6624_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = vs6624_g_register,
.s_register = vs6624_s_register,
@@ -805,20 +779,18 @@
if (ce == NULL)
return -EINVAL;
- ret = gpio_request(*ce, "VS6624 Chip Enable");
+ ret = devm_gpio_request_one(&client->dev, *ce, GPIOF_OUT_INIT_HIGH,
+ "VS6624 Chip Enable");
if (ret) {
v4l_err(client, "failed to request GPIO %d\n", *ce);
return ret;
}
- gpio_direction_output(*ce, 1);
/* wait 100ms before any further i2c writes are performed */
mdelay(100);
- sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
- if (sensor == NULL) {
- gpio_free(*ce);
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)
return -ENOMEM;
- }
sd = &sensor->sd;
v4l2_i2c_subdev_init(sd, client, &vs6624_ops);
@@ -866,30 +838,22 @@
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(sensor);
- gpio_free(*ce);
return err;
}
/* initialize the hardware to the default control values */
ret = v4l2_ctrl_handler_setup(hdl);
- if (ret) {
+ if (ret)
v4l2_ctrl_handler_free(hdl);
- kfree(sensor);
- gpio_free(*ce);
- }
return ret;
}
static int vs6624_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct vs6624 *sensor = to_vs6624(sd);
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- gpio_free(sensor->ce_pin);
- kfree(sensor);
return 0;
}
diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c
index 3bb99e9..3be73f6 100644
--- a/drivers/media/i2c/wm8739.c
+++ b/drivers/media/i2c/wm8739.c
@@ -29,7 +29,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("wm8739 driver");
@@ -160,13 +159,6 @@
return 0;
}
-static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0);
-}
-
static int wm8739_log_status(struct v4l2_subdev *sd)
{
struct wm8739_state *state = to_state(sd);
@@ -184,7 +176,6 @@
static const struct v4l2_subdev_core_ops wm8739_core_ops = {
.log_status = wm8739_log_status,
- .g_chip_ident = wm8739_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -220,7 +211,7 @@
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -237,7 +228,6 @@
int err = state->hdl.error;
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
v4l2_ctrl_cluster(3, &state->volume);
@@ -271,7 +261,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c
index 27c27b4..3f584a7 100644
--- a/drivers/media/i2c/wm8775.c
+++ b/drivers/media/i2c/wm8775.c
@@ -33,7 +33,6 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/wm8775.h>
@@ -158,13 +157,6 @@
return -EINVAL;
}
-static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
-}
-
static int wm8775_log_status(struct v4l2_subdev *sd)
{
struct wm8775_state *state = to_state(sd);
@@ -188,7 +180,6 @@
static const struct v4l2_subdev_core_ops wm8775_core_ops = {
.log_status = wm8775_log_status,
- .g_chip_ident = wm8775_g_chip_ident,
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
@@ -241,7 +232,7 @@
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
@@ -261,7 +252,6 @@
err = state->hdl.error;
if (err) {
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return err;
}
@@ -319,7 +309,6 @@
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
return 0;
}
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 1957c0d..d5a7a13 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -142,6 +142,8 @@
for (p = 0; p < entity->num_pads; p++) {
struct media_pad_desc pad;
+
+ memset(&pad, 0, sizeof(pad));
media_device_kpad_to_upad(&entity->pads[p], &pad);
if (copy_to_user(&links->pads[p], &pad, sizeof(pad)))
return -EFAULT;
@@ -159,6 +161,7 @@
if (entity->links[l].source->entity != entity)
continue;
+ memset(&link, 0, sizeof(link));
media_device_kpad_to_upad(entity->links[l].source,
&link.source);
media_device_kpad_to_upad(entity->links[l].sink,
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index e1cd132..cb30ffb 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -429,6 +429,56 @@
}
EXPORT_SYMBOL_GPL(media_entity_create_link);
+void __media_entity_remove_links(struct media_entity *entity)
+{
+ unsigned int i;
+
+ for (i = 0; i < entity->num_links; i++) {
+ struct media_link *link = &entity->links[i];
+ struct media_entity *remote;
+ unsigned int r = 0;
+
+ if (link->source->entity == entity)
+ remote = link->sink->entity;
+ else
+ remote = link->source->entity;
+
+ while (r < remote->num_links) {
+ struct media_link *rlink = &remote->links[r];
+
+ if (rlink != link->reverse) {
+ r++;
+ continue;
+ }
+
+ if (link->source->entity == entity)
+ remote->num_backlinks--;
+
+ if (--remote->num_links == 0)
+ break;
+
+ /* Insert last entry in place of the dropped link. */
+ *rlink = remote->links[remote->num_links];
+ }
+ }
+
+ entity->num_links = 0;
+ entity->num_backlinks = 0;
+}
+EXPORT_SYMBOL_GPL(__media_entity_remove_links);
+
+void media_entity_remove_links(struct media_entity *entity)
+{
+ /* Do nothing if the entity is not registered. */
+ if (entity->parent == NULL)
+ return;
+
+ mutex_lock(&entity->parent->graph_mutex);
+ __media_entity_remove_links(entity);
+ mutex_unlock(&entity->parent->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_entity_remove_links);
+
static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
{
int ret;
@@ -496,25 +546,17 @@
mdev = source->parent;
- if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) {
- ret = mdev->link_notify(link->source, link->sink,
- MEDIA_LNK_FL_ENABLED);
+ if (mdev->link_notify) {
+ ret = mdev->link_notify(link, flags,
+ MEDIA_DEV_NOTIFY_PRE_LINK_CH);
if (ret < 0)
return ret;
}
ret = __media_entity_setup_link_notify(link, flags);
- if (ret < 0)
- goto err;
- if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)
- mdev->link_notify(link->source, link->sink, 0);
-
- return 0;
-
-err:
- if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify)
- mdev->link_notify(link->source, link->sink, 0);
+ if (mdev->link_notify)
+ mdev->link_notify(link, flags, MEDIA_DEV_NOTIFY_POST_LINK_CH);
return ret;
}
@@ -560,17 +602,16 @@
EXPORT_SYMBOL_GPL(media_entity_find_link);
/**
- * media_entity_remote_source - Find the source pad at the remote end of a link
- * @pad: Sink pad at the local end of the link
+ * media_entity_remote_pad - Find the pad at the remote end of a link
+ * @pad: Pad at the local end of the link
*
- * Search for a remote source pad connected to the given sink pad by iterating
- * over all links originating or terminating at that pad until an enabled link
- * is found.
+ * Search for a remote pad connected to the given pad by iterating over all
+ * links originating or terminating at that pad until an enabled link is found.
*
* Return a pointer to the pad at the remote end of the first found enabled
* link, or NULL if no enabled link has been found.
*/
-struct media_pad *media_entity_remote_source(struct media_pad *pad)
+struct media_pad *media_entity_remote_pad(struct media_pad *pad)
{
unsigned int i;
@@ -590,4 +631,4 @@
return NULL;
}
-EXPORT_SYMBOL_GPL(media_entity_remote_source);
+EXPORT_SYMBOL_GPL(media_entity_remote_pad);
diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c
index 06231b8..d12bd33 100644
--- a/drivers/media/parport/bw-qcam.c
+++ b/drivers/media/parport/bw-qcam.c
@@ -687,6 +687,7 @@
parport_release(qcam->pdev);
mutex_unlock(&qcam->lock);
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
if (len != size)
vb->state = VB2_BUF_STATE_ERROR;
vb2_set_plane_payload(vb, 0, len);
@@ -964,6 +965,7 @@
q->drv_priv = qcam;
q->ops = &qcam_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
err = vb2_queue_init(q);
if (err < 0) {
v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name);
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index d4e2ed3..53196f1 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -1,6 +1,7 @@
+if PCI && MEDIA_SUPPORT
+
menuconfig MEDIA_PCI_SUPPORT
bool "Media PCI Adapters"
- depends on PCI && MEDIA_SUPPORT
help
Enable media drivers for PCI/PCIe bus.
If you have such devices, say Y.
@@ -45,3 +46,4 @@
endif
endif #MEDIA_PCI_SUPPORT
+endif #PCI
diff --git a/drivers/media/pci/b2c2/flexcop-pci.c b/drivers/media/pci/b2c2/flexcop-pci.c
index 44f8fb5..447afbd 100644
--- a/drivers/media/pci/b2c2/flexcop-pci.c
+++ b/drivers/media/pci/b2c2/flexcop-pci.c
@@ -432,18 +432,7 @@
.remove = flexcop_pci_remove,
};
-static int __init flexcop_pci_module_init(void)
-{
- return pci_register_driver(&flexcop_pci_driver);
-}
-
-static void __exit flexcop_pci_module_exit(void)
-{
- pci_unregister_driver(&flexcop_pci_driver);
-}
-
-module_init(flexcop_pci_module_init);
-module_exit(flexcop_pci_module_exit);
+module_pci_driver(flexcop_pci_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_NAME);
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index b7dc921..e564aac 100644
--- a/drivers/media/pci/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -131,7 +131,7 @@
"[yet another chipset flaw workaround]");
MODULE_PARM_DESC(latency,"pci latency timer");
MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
-MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
+MODULE_PARM_DESC(pll, "specify installed crystal (0=none, 28=28 MHz, 35=35 MHz, 14=14 MHz)");
MODULE_PARM_DESC(tuner,"specify installed tuner type");
MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore");
MODULE_PARM_DESC(audiodev, "specify audio device:\n"
@@ -2705,7 +2705,7 @@
.has_radio = 1,
.has_remote = 1,
},
- [BTTV_BOARD_VD012] = {
+ [BTTV_BOARD_VD012] = {
/* D.Heer@Phytec.de */
.name = "PHYTEC VD-012 (bt878)",
.video_inputs = 4,
@@ -2718,7 +2718,7 @@
.tuner_type = TUNER_ABSENT,
.tuner_addr = ADDR_UNSET,
},
- [BTTV_BOARD_VD012_X1] = {
+ [BTTV_BOARD_VD012_X1] = {
/* D.Heer@Phytec.de */
.name = "PHYTEC VD-012-X1 (bt878)",
.video_inputs = 4,
@@ -2731,7 +2731,7 @@
.tuner_type = TUNER_ABSENT,
.tuner_addr = ADDR_UNSET,
},
- [BTTV_BOARD_VD012_X2] = {
+ [BTTV_BOARD_VD012_X2] = {
/* D.Heer@Phytec.de */
.name = "PHYTEC VD-012-X2 (bt878)",
.video_inputs = 4,
@@ -2744,7 +2744,7 @@
.tuner_type = TUNER_ABSENT,
.tuner_addr = ADDR_UNSET,
},
- [BTTV_BOARD_GEOVISION_GV800S] = {
+ [BTTV_BOARD_GEOVISION_GV800S] = {
/* Bruno Christo <bchristo@inf.ufsm.br>
*
* GeoVision GV-800(S) has 4 Conexant Fusion 878A:
@@ -2771,7 +2771,7 @@
.no_tda7432 = 1,
.muxsel_hook = gv800s_muxsel,
},
- [BTTV_BOARD_GEOVISION_GV800S_SL] = {
+ [BTTV_BOARD_GEOVISION_GV800S_SL] = {
/* Bruno Christo <bchristo@inf.ufsm.br>
*
* GeoVision GV-800(S) has 4 Conexant Fusion 878A:
@@ -2808,6 +2808,7 @@
.tuner_type = TUNER_ABSENT,
.tuner_addr = ADDR_UNSET,
},
+ /* ---- card 0xa0---------------------------------- */
[BTTV_BOARD_TVT_TD3116] = {
.name = "Tongwei Video Technology TD-3116",
.video_inputs = 16,
@@ -2825,6 +2826,35 @@
.muxsel = MUXSEL(2, 3, 1, 0),
.tuner_type = TUNER_ABSENT,
},
+ [BTTV_BOARD_ADLINK_MPG24] = {
+ /* Adlink MPG24 */
+ .name = "Adlink MPG24",
+ .video_inputs = 1,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_BT848_CAP_14] = {
+ .name = "Bt848 Capture 14MHz",
+ .video_inputs = 4,
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_14,
+ .tuner_type = TUNER_ABSENT,
+ },
+ [BTTV_BOARD_CYBERVISION_CV06] = {
+ .name = "CyberVision CV06 (SV)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
};
@@ -3390,6 +3420,10 @@
btv->pll.pll_ifreq=35468950;
btv->pll.pll_crystal=BT848_IFORM_XT1;
}
+ if (PLL_14 == bttv_tvcards[btv->c.type].pll) {
+ btv->pll.pll_ifreq = 14318181;
+ btv->pll.pll_crystal = BT848_IFORM_XT0;
+ }
/* insmod options can override */
switch (pll[btv->c.nr]) {
case 0: /* none */
@@ -3409,6 +3443,12 @@
btv->pll.pll_ofreq = 0;
btv->pll.pll_crystal = BT848_IFORM_XT1;
break;
+ case 3: /* 14 MHz */
+ case 14:
+ btv->pll.pll_ifreq = 14318181;
+ btv->pll.pll_ofreq = 0;
+ btv->pll.pll_crystal = BT848_IFORM_XT0;
+ break;
}
}
btv->pll.pll_current = -1;
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index e7d0884..c6532de 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -50,7 +50,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-chip-ident.h>
#include <media/tvaudio.h>
#include <media/msp3400.h>
@@ -1761,9 +1760,9 @@
struct bttv *btv = fh->btv;
if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
- *id = V4L2_STD_625_50;
+ *id &= V4L2_STD_625_50;
else
- *id = V4L2_STD_525_60;
+ *id &= V4L2_STD_525_60;
return 0;
}
@@ -1907,28 +1906,6 @@
return 0;
}
-static int bttv_g_chip_ident(struct file *file, void *f, struct v4l2_dbg_chip_ident *chip)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = btv->id;
- if (chip->ident == PCI_DEVICE_ID_FUSION879)
- chip->ident = V4L2_IDENT_BT879;
- }
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
- /* TODO: is this correct? */
- return bttv_call_all_err(btv, core, g_chip_ident, chip);
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int bttv_g_register(struct file *file, void *f,
struct v4l2_dbg_register *reg)
@@ -1936,16 +1913,6 @@
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (!v4l2_chip_match_host(®->match)) {
- /* TODO: subdev errors should not be ignored, this should become a
- subdev helper function. */
- bttv_call_all(btv, core, g_register, reg);
- return 0;
- }
-
/* bt848 has a 12-bit register space */
reg->reg &= 0xfff;
reg->val = btread(reg->reg);
@@ -1960,16 +1927,6 @@
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (!v4l2_chip_match_host(®->match)) {
- /* TODO: subdev errors should not be ignored, this should become a
- subdev helper function. */
- bttv_call_all(btv, core, s_register, reg);
- return 0;
- }
-
/* bt848 has a 12-bit register space */
btwrite(reg->val, reg->reg & 0xfff);
@@ -3209,7 +3166,6 @@
.vidioc_querystd = bttv_querystd,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = bttv_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = bttv_g_register,
.vidioc_s_register = bttv_s_register,
diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
index 6139ce2..df578ef 100644
--- a/drivers/media/pci/bt8xx/bttv.h
+++ b/drivers/media/pci/bt8xx/bttv.h
@@ -185,6 +185,9 @@
#define BTTV_BOARD_PV183 0x9f
#define BTTV_BOARD_TVT_TD3116 0xa0
#define BTTV_BOARD_APOSONIC_WDVR 0xa1
+#define BTTV_BOARD_ADLINK_MPG24 0xa2
+#define BTTV_BOARD_BT848_CAP_14 0xa3
+#define BTTV_BOARD_CYBERVISION_CV06 0xa4
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
@@ -232,6 +235,7 @@
#define PLL_NONE 0
#define PLL_28 1
#define PLL_35 2
+#define PLL_14 3
/* i2c audio flags */
unsigned int no_msp34xx:1;
diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c
index 38b1d64..c4890a4 100644
--- a/drivers/media/pci/cx18/cx18-av-core.c
+++ b/drivers/media/pci/cx18/cx18-av-core.c
@@ -22,7 +22,6 @@
* 02110-1301, USA.
*/
-#include <media/v4l2-chip-ident.h>
#include "cx18-driver.h"
#include "cx18-io.h"
#include "cx18-cards.h"
@@ -1231,35 +1230,14 @@
return 0;
}
-static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match)
-{
- return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1;
-}
-
-static int cx18_av_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct cx18_av_state *state = to_cx18_av_state(sd);
-
- if (cx18_av_dbg_match(&chip->match)) {
- chip->ident = state->id;
- chip->revision = state->rev;
- }
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int cx18_av_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
{
struct cx18 *cx = v4l2_get_subdevdata(sd);
- if (!cx18_av_dbg_match(®->match))
- return -EINVAL;
if ((reg->reg & 0x3) != 0)
return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->size = 4;
reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc);
return 0;
@@ -1270,12 +1248,8 @@
{
struct cx18 *cx = v4l2_get_subdevdata(sd);
- if (!cx18_av_dbg_match(®->match))
- return -EINVAL;
if ((reg->reg & 0x3) != 0)
return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val);
return 0;
}
@@ -1286,17 +1260,9 @@
};
static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
- .g_chip_ident = cx18_av_g_chip_ident,
.log_status = cx18_av_log_status,
.load_fw = cx18_av_load_fw,
.reset = cx18_av_reset,
- .g_ctrl = v4l2_subdev_g_ctrl,
- .s_ctrl = v4l2_subdev_s_ctrl,
- .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
- .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
- .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
- .queryctrl = v4l2_subdev_queryctrl,
- .querymenu = v4l2_subdev_querymenu,
.s_std = cx18_av_s_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx18_av_g_register,
@@ -1344,8 +1310,6 @@
int err;
state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
- state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
- ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN;
state->vid_input = CX18_AV_COMPOSITE7;
state->aud_input = CX18_AV_AUDIO8;
diff --git a/drivers/media/pci/cx18/cx18-av-core.h b/drivers/media/pci/cx18/cx18-av-core.h
index e9c69d9..4c559e8 100644
--- a/drivers/media/pci/cx18/cx18-av-core.h
+++ b/drivers/media/pci/cx18/cx18-av-core.h
@@ -104,7 +104,6 @@
enum cx18_av_audio_input aud_input;
u32 audclk_freq;
int audmode;
- u32 id;
u32 rev;
int is_initialized;
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index aee7b6d..1110bcb 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -39,7 +39,6 @@
#include "cx18-cards.h"
#include "cx18-av-core.h"
#include <media/tveeprom.h>
-#include <media/v4l2-chip-ident.h>
u16 cx18_service2vbi(int type)
{
@@ -362,73 +361,18 @@
return 0;
}
-static int cx18_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- int err = 0;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- switch (chip->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- switch (chip->match.addr) {
- case 0:
- chip->ident = V4L2_IDENT_CX23418;
- chip->revision = cx18_read_reg(cx, 0xC72028);
- break;
- case 1:
- /*
- * The A/V decoder is always present, but in the rare
- * case that the card doesn't have analog, we don't
- * use it. We find it w/o using the cx->sd_av pointer
- */
- cx18_call_hw(cx, CX18_HW_418_AV,
- core, g_chip_ident, chip);
- break;
- default:
- /*
- * Could return ident = V4L2_IDENT_UNKNOWN if we had
- * other host chips at higher addresses, but we don't
- */
- err = -EINVAL; /* per V4L2 spec */
- break;
- }
- break;
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
- cx18_call_all(cx, core, g_chip_ident, chip);
- break;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /*
- * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
- * to look if a chip is at the address with no driver. That's a
- * dangerous thing to do with EEPROMs anyway.
- */
- cx18_call_all(cx, core, g_chip_ident, chip);
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int cx18_g_register(struct file *file, void *fh,
struct v4l2_dbg_register *reg)
{
struct cx18 *cx = fh2id(fh)->cx;
- if (v4l2_chip_match_host(®->match)) {
- if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
- return -EINVAL;
- reg->size = 4;
- reg->val = cx18_read_enc(cx, reg->reg);
- return 0;
- }
- /* FIXME - errors shouldn't be ignored */
- cx18_call_all(cx, core, g_register, reg);
+ if (reg->reg & 0x3)
+ return -EINVAL;
+ if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+ return -EINVAL;
+ reg->size = 4;
+ reg->val = cx18_read_enc(cx, reg->reg);
return 0;
}
@@ -437,14 +381,11 @@
{
struct cx18 *cx = fh2id(fh)->cx;
- if (v4l2_chip_match_host(®->match)) {
- if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
- return -EINVAL;
- cx18_write_enc(cx, reg->val, reg->reg);
- return 0;
- }
- /* FIXME - errors shouldn't be ignored */
- cx18_call_all(cx, core, s_register, reg);
+ if (reg->reg & 0x3)
+ return -EINVAL;
+ if (reg->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+ return -EINVAL;
+ cx18_write_enc(cx, reg->val, reg->reg);
return 0;
}
#endif
@@ -1162,7 +1103,6 @@
.vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap,
.vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap,
.vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap,
- .vidioc_g_chip_ident = cx18_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = cx18_g_register,
.vidioc_s_register = cx18_s_register,
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
index 6dea11a..e3fc2c7 100644
--- a/drivers/media/pci/cx23885/cx23885-417.c
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -1217,8 +1217,7 @@
struct cx23885_fh *fh = file->private_data;
struct cx23885_dev *dev = fh->dev;
- call_all(dev, core, g_std, id);
-
+ *id = dev->tvnorm;
return 0;
}
@@ -1661,7 +1660,6 @@
};
static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
- .vidioc_querystd = vidioc_g_std,
.vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
@@ -1690,8 +1688,8 @@
.vidioc_log_status = vidioc_log_status,
.vidioc_querymenu = vidioc_querymenu,
.vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_chip_ident = cx23885_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_chip_info = cx23885_g_chip_info,
.vidioc_g_register = cx23885_g_register,
.vidioc_s_register = cx23885_s_register,
#endif
@@ -1702,7 +1700,6 @@
.fops = &mpeg_fops,
.ioctl_ops = &mpeg_ioctl_ops,
.tvnorms = CX23885_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
};
void cx23885_417_unregister(struct cx23885_dev *dev)
@@ -1735,7 +1732,7 @@
*vfd = *template;
snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
cx23885_boards[tsport->dev->board].name, type);
- vfd->parent = &pci->dev;
+ vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
return vfd;
}
diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c
index acdb6d5..271d69d 100644
--- a/drivers/media/pci/cx23885/cx23885-ioctl.c
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.c
@@ -24,93 +24,21 @@
#include "cx23885.h"
#include "cx23885-ioctl.h"
-#include <media/v4l2-chip-ident.h>
-
-int cx23885_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx23885_g_chip_info(struct file *file, void *fh,
+ struct v4l2_dbg_chip_info *chip)
{
struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
- int err = 0;
- u8 rev;
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- switch (chip->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- switch (chip->match.addr) {
- case 0:
- rev = cx_read(RDR_CFG2) & 0xff;
- switch (dev->pci->device) {
- case 0x8852:
- /* rev 0x04 could be '885 or '888. Pick '888. */
- if (rev == 0x04)
- chip->ident = V4L2_IDENT_CX23888;
- else
- chip->ident = V4L2_IDENT_CX23885;
- break;
- case 0x8880:
- if (rev == 0x0e || rev == 0x0f)
- chip->ident = V4L2_IDENT_CX23887;
- else
- chip->ident = V4L2_IDENT_CX23888;
- break;
- default:
- chip->ident = V4L2_IDENT_UNKNOWN;
- break;
- }
- chip->revision = (dev->pci->device << 16) | (rev << 8) |
- (dev->hwrevision & 0xff);
- break;
- case 1:
- if (dev->v4l_device != NULL) {
- chip->ident = V4L2_IDENT_CX23417;
- chip->revision = 0;
- }
- break;
- case 2:
- /*
- * The integrated IR controller on the CX23888 is
- * host chip 2. It may not be used/initialized or sd_ir
- * may be pointing at the cx25840 subdevice for the
- * IR controller on the CX23885. Thus we find it
- * without using the dev->sd_ir pointer.
- */
- call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
- chip);
- break;
- default:
- err = -EINVAL; /* per V4L2 spec */
- break;
- }
- break;
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
- call_all(dev, core, g_chip_ident, chip);
- break;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /*
- * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
- * to look if a chip is at the address with no driver. That's a
- * dangerous thing to do with EEPROMs anyway.
- */
- call_all(dev, core, g_chip_ident, chip);
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int cx23885_g_host_register(struct cx23885_dev *dev,
- struct v4l2_dbg_register *reg)
-{
- if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+ if (chip->match.addr > 1)
return -EINVAL;
-
- reg->size = 4;
- reg->val = cx_read(reg->reg);
+ if (chip->match.addr == 1) {
+ if (dev->v4l_device == NULL)
+ return -EINVAL;
+ strlcpy(chip->name, "cx23417", sizeof(chip->name));
+ } else {
+ strlcpy(chip->name, dev->v4l2_dev.name, sizeof(chip->name));
+ }
return 0;
}
@@ -138,32 +66,16 @@
{
struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ if (reg->match.addr > 1)
+ return -EINVAL;
+ if (reg->match.addr)
+ return cx23417_g_register(dev, reg);
- if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
- switch (reg->match.addr) {
- case 0:
- return cx23885_g_host_register(dev, reg);
- case 1:
- return cx23417_g_register(dev, reg);
- default:
- break;
- }
- }
-
- /* FIXME - any error returns should not be ignored */
- call_all(dev, core, g_register, reg);
- return 0;
-}
-
-static int cx23885_s_host_register(struct cx23885_dev *dev,
- const struct v4l2_dbg_register *reg)
-{
if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
return -EINVAL;
- cx_write(reg->reg, reg->val);
+ reg->size = 4;
+ reg->val = cx_read(reg->reg);
return 0;
}
@@ -186,22 +98,15 @@
{
struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ if (reg->match.addr > 1)
+ return -EINVAL;
+ if (reg->match.addr)
+ return cx23417_s_register(dev, reg);
- if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
- switch (reg->match.addr) {
- case 0:
- return cx23885_s_host_register(dev, reg);
- case 1:
- return cx23417_s_register(dev, reg);
- default:
- break;
- }
- }
+ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+ return -EINVAL;
- /* FIXME - any error returns should not be ignored */
- call_all(dev, core, s_register, reg);
+ cx_write(reg->reg, reg->val);
return 0;
}
#endif
diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h
index a608096..92d9f07 100644
--- a/drivers/media/pci/cx23885/cx23885-ioctl.h
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.h
@@ -24,8 +24,8 @@
#ifndef _CX23885_IOCTL_H_
#define _CX23885_IOCTL_H_
-int cx23885_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip);
+int cx23885_g_chip_info(struct file *file, void *fh,
+ struct v4l2_dbg_chip_info *chip);
#ifdef CONFIG_VIDEO_ADV_DEBUG
int cx23885_g_register(struct file *file, void *fh,
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index ed08c89..e33d1a7 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -1254,8 +1254,7 @@
struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
dprintk(1, "%s()\n", __func__);
- call_all(dev, core, g_std, id);
-
+ *id = dev->tvnorm;
return 0;
}
@@ -1743,7 +1742,6 @@
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_std = vidioc_g_std,
- .vidioc_querystd = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
@@ -1757,8 +1755,8 @@
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_g_chip_ident = cx23885_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_chip_info = cx23885_g_chip_info,
.vidioc_g_register = cx23885_g_register,
.vidioc_s_register = cx23885_s_register,
#endif
@@ -1773,7 +1771,6 @@
.fops = &video_fops,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = CX23885_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
};
static const struct v4l2_file_operations radio_fops = {
@@ -1822,7 +1819,7 @@
cx23885_vbi_template = cx23885_video_template;
strcpy(cx23885_vbi_template.name, "cx23885-vbi");
- dev->tvnorm = cx23885_video_template.current_norm;
+ dev->tvnorm = V4L2_STD_NTSC_M;
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c
index fa672fe..2c951de 100644
--- a/drivers/media/pci/cx23885/cx23888-ir.c
+++ b/drivers/media/pci/cx23885/cx23888-ir.c
@@ -25,7 +25,6 @@
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/rc-core.h>
#include "cx23885.h"
@@ -131,8 +130,6 @@
struct cx23888_ir_state {
struct v4l2_subdev sd;
struct cx23885_dev *dev;
- u32 id;
- u32 rev;
struct v4l2_subdev_ir_parameters rx_params;
struct mutex rx_params_lock;
@@ -1086,23 +1083,6 @@
return 0;
}
-static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match)
-{
- return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2;
-}
-
-static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct cx23888_ir_state *state = to_state(sd);
-
- if (cx23888_ir_dbg_match(&chip->match)) {
- chip->ident = state->id;
- chip->revision = state->rev;
- }
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int cx23888_ir_g_register(struct v4l2_subdev *sd,
struct v4l2_dbg_register *reg)
@@ -1110,14 +1090,10 @@
struct cx23888_ir_state *state = to_state(sd);
u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
- if (!cx23888_ir_dbg_match(®->match))
- return -EINVAL;
if ((addr & 0x3) != 0)
return -EINVAL;
if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
reg->size = 4;
reg->val = cx23888_ir_read4(state->dev, addr);
return 0;
@@ -1129,21 +1105,16 @@
struct cx23888_ir_state *state = to_state(sd);
u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
- if (!cx23888_ir_dbg_match(®->match))
- return -EINVAL;
if ((addr & 0x3) != 0)
return -EINVAL;
if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
cx23888_ir_write4(state->dev, addr, reg->val);
return 0;
}
#endif
static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
- .g_chip_ident = cx23888_ir_g_chip_ident,
.log_status = cx23888_ir_log_status,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx23888_ir_g_register,
@@ -1217,8 +1188,6 @@
return -ENOMEM;
state->dev = dev;
- state->id = V4L2_IDENT_CX23888_IR;
- state->rev = 0;
sd = &state->sd;
v4l2_subdev_init(sd, &cx23888_ir_controller_ops);
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
index a87a0e1..e18a7ac 100644
--- a/drivers/media/pci/cx88/cx88-cards.c
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -744,7 +744,7 @@
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
/* Some variants use a tda9874 and so need the tvaudio module. */
- .audio_chip = V4L2_IDENT_TVAUDIO,
+ .audio_chip = CX88_AUDIO_TVAUDIO,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
@@ -976,7 +976,7 @@
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_chip = V4L2_IDENT_WM8775,
+ .audio_chip = CX88_AUDIO_WM8775,
.i2sinputcntl = 2,
.input = {{
.type = CX88_VMUX_DVB,
@@ -1014,7 +1014,7 @@
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
- .audio_chip = V4L2_IDENT_WM8775,
+ .audio_chip = CX88_AUDIO_WM8775,
.input = {{
.type = CX88_VMUX_DVB,
.vmux = 0,
@@ -1376,7 +1376,7 @@
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
- .audio_chip = V4L2_IDENT_WM8775,
+ .audio_chip = CX88_AUDIO_WM8775,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
@@ -1461,7 +1461,7 @@
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
- .audio_chip = V4L2_IDENT_WM8775,
+ .audio_chip = CX88_AUDIO_WM8775,
/*
* gpio0 as reported by Mike Crash <mike AT mikecrash.com>
*/
@@ -1929,7 +1929,7 @@
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
- .audio_chip = V4L2_IDENT_WM8775,
+ .audio_chip = CX88_AUDIO_WM8775,
/*
* GPIO0 (WINTV2000)
*
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
index c8f3dcc..ad59dc9 100644
--- a/drivers/media/pci/cx88/cx88-core.c
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -1034,7 +1034,14 @@
if (NULL == vfd)
return NULL;
*vfd = *template_;
+ /*
+ * The dev pointer of v4l2_device is NULL, instead we set the
+ * video_device dev_parent pointer to the correct PCI bus device.
+ * This driver is a rare example where there is one v4l2_device,
+ * but the video nodes have different parent (PCI) devices.
+ */
vfd->v4l2_dev = &core->v4l2_dev;
+ vfd->dev_parent = &pci->dev;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
core->name, type, core->board.name);
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index c7a9be1..ecf21d9 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1353,26 +1353,14 @@
return cx88_set_freq(core, f);
}
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- if (!v4l2_chip_match_host(&chip->match))
- return -EINVAL;
- chip->revision = 0;
- chip->ident = V4L2_IDENT_UNKNOWN;
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vidioc_g_register (struct file *file, void *fh,
struct v4l2_dbg_register *reg)
{
struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
/* cx2388x has a 24-bit register space */
- reg->val = cx_read(reg->reg & 0xffffff);
+ reg->val = cx_read(reg->reg & 0xfffffc);
reg->size = 4;
return 0;
}
@@ -1382,9 +1370,7 @@
{
struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- cx_write(reg->reg & 0xffffff, reg->val);
+ cx_write(reg->reg & 0xfffffc, reg->val);
return 0;
}
#endif
@@ -1578,7 +1564,6 @@
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
@@ -1612,7 +1597,6 @@
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
@@ -1643,7 +1627,6 @@
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
@@ -1794,7 +1777,7 @@
/* load and configure helper modules */
- if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ if (core->board.audio_chip == CX88_AUDIO_WM8775) {
struct i2c_board_info wm8775_info = {
.type = "wm8775",
.addr = 0x36 >> 1,
@@ -1815,7 +1798,7 @@
}
}
- if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
+ if (core->board.audio_chip == CX88_AUDIO_TVAUDIO) {
/* This probes for a tda9874 as is used on some
Pixelview Ultra boards. */
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index 51ce2c0..afe0eae 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -30,7 +30,6 @@
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/videobuf-dma-sg.h>
-#include <media/v4l2-chip-ident.h>
#include <media/cx2341x.h>
#include <media/videobuf-dvb.h>
#include <media/ir-kbd-i2c.h>
@@ -259,6 +258,11 @@
unsigned int audioroute:4;
};
+enum cx88_audio_chip {
+ CX88_AUDIO_WM8775,
+ CX88_AUDIO_TVAUDIO,
+};
+
struct cx88_board {
const char *name;
unsigned int tuner_type;
@@ -269,7 +273,7 @@
struct cx88_input input[MAX_CX88_INPUT];
struct cx88_input radio;
enum cx88_board_type mpeg;
- unsigned int audio_chip;
+ enum cx88_audio_chip audio_chip;
int num_frontends;
/* Used for I2S devices */
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index 026767b..ab797fe 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -1241,18 +1241,7 @@
.remove = dm1105_remove,
};
-static int __init dm1105_init(void)
-{
- return pci_register_driver(&dm1105_driver);
-}
-
-static void __exit dm1105_exit(void)
-{
- pci_unregister_driver(&dm1105_driver);
-}
-
-module_init(dm1105_init);
-module_exit(dm1105_exit);
+module_pci_driver(dm1105_driver);
MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
MODULE_DESCRIPTION("SDMC DM1105 DVB driver");
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
index b809bc8..c08ae3e 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.c
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -58,7 +58,6 @@
#include <linux/dma-mapping.h>
#include <media/tveeprom.h>
#include <media/saa7115.h>
-#include <media/v4l2-chip-ident.h>
#include "tuner-xc2028.h"
/* If you have already X v4l cards, then set this to X. This way
@@ -968,15 +967,10 @@
}
if (hw & IVTV_HW_SAA711X) {
- struct v4l2_dbg_chip_ident v;
-
/* determine the exact saa711x model */
itv->hw_flags &= ~IVTV_HW_SAA711X;
- v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER;
- strlcpy(v.match.name, "saa7115", sizeof(v.match.name));
- ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
- if (v.ident == V4L2_IDENT_SAA7114) {
+ if (strstr(itv->sd_video->name, "saa7114")) {
itv->hw_flags |= IVTV_HW_SAA7114;
/* VBI is not yet supported by the saa7114 driver. */
itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 9cbbce0..807b275 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -34,7 +34,6 @@
#include "ivtv-cards.h"
#include <media/saa7127.h>
#include <media/tveeprom.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-event.h>
#include <linux/dvb/audio.h>
@@ -692,31 +691,13 @@
return ret;
}
-static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match))
- chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
- /* TODO: is this correct? */
- return ivtv_call_all_err(itv, core, g_chip_ident, chip);
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ivtv_itvc(struct ivtv *itv, bool get, u64 reg, u64 *val)
{
volatile u8 __iomem *reg_start;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ if (reg & 0x3)
+ return -EINVAL;
if (reg >= IVTV_REG_OFFSET && reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
reg_start = itv->reg_mem - IVTV_REG_OFFSET;
else if (itv->has_cx23415 && reg >= IVTV_DECODER_OFFSET &&
@@ -738,29 +719,16 @@
{
struct ivtv *itv = fh2id(fh)->itv;
- if (v4l2_chip_match_host(®->match)) {
- reg->size = 4;
- return ivtv_itvc(itv, true, reg->reg, ®->val);
- }
- /* TODO: subdev errors should not be ignored, this should become a
- subdev helper function. */
- ivtv_call_all(itv, core, g_register, reg);
- return 0;
+ reg->size = 4;
+ return ivtv_itvc(itv, true, reg->reg, ®->val);
}
static int ivtv_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg)
{
struct ivtv *itv = fh2id(fh)->itv;
+ u64 val = reg->val;
- if (v4l2_chip_match_host(®->match)) {
- u64 val = reg->val;
-
- return ivtv_itvc(itv, false, reg->reg, &val);
- }
- /* TODO: subdev errors should not be ignored, this should become a
- subdev helper function. */
- ivtv_call_all(itv, core, s_register, reg);
- return 0;
+ return ivtv_itvc(itv, false, reg->reg, &val);
}
#endif
@@ -1914,7 +1882,6 @@
.vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay,
.vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out,
.vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap,
- .vidioc_g_chip_ident = ivtv_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = ivtv_g_register,
.vidioc_s_register = ivtv_s_register,
diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c
index 6fe9fe5..104914a 100644
--- a/drivers/media/pci/mantis/hopper_cards.c
+++ b/drivers/media/pci/mantis/hopper_cards.c
@@ -260,18 +260,7 @@
.remove = hopper_pci_remove,
};
-static int hopper_init(void)
-{
- return pci_register_driver(&hopper_pci_driver);
-}
-
-static void hopper_exit(void)
-{
- return pci_unregister_driver(&hopper_pci_driver);
-}
-
-module_init(hopper_init);
-module_exit(hopper_exit);
+module_pci_driver(hopper_pci_driver);
MODULE_DESCRIPTION("HOPPER driver");
MODULE_AUTHOR("Manu Abraham");
diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c
index 932a0d7..801fc55 100644
--- a/drivers/media/pci/mantis/mantis_cards.c
+++ b/drivers/media/pci/mantis/mantis_cards.c
@@ -290,18 +290,7 @@
.remove = mantis_pci_remove,
};
-static int mantis_init(void)
-{
- return pci_register_driver(&mantis_pci_driver);
-}
-
-static void mantis_exit(void)
-{
- return pci_unregister_driver(&mantis_pci_driver);
-}
-
-module_init(mantis_init);
-module_exit(mantis_exit);
+module_pci_driver(mantis_pci_driver);
MODULE_DESCRIPTION("MANTIS driver");
MODULE_AUTHOR("Manu Abraham");
diff --git a/drivers/media/pci/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c
index 07aa887..07a2074 100644
--- a/drivers/media/pci/mantis/mantis_vp1041.c
+++ b/drivers/media/pci/mantis/mantis_vp1041.c
@@ -273,7 +273,7 @@
.demod_address = 0x68, /* 0xd0 >> 1 */
.xtal_freq = 27000000,
- .inversion = IQ_SWAP_ON, /* 1 */
+ .inversion = IQ_SWAP_ON,
.lo_clk = 76500000,
.hi_clk = 99000000,
diff --git a/drivers/media/pci/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c
index 2290fae..4938285 100644
--- a/drivers/media/pci/pluto2/pluto2.c
+++ b/drivers/media/pci/pluto2/pluto2.c
@@ -796,18 +796,7 @@
.remove = pluto2_remove,
};
-static int __init pluto2_init(void)
-{
- return pci_register_driver(&pluto2_driver);
-}
-
-static void __exit pluto2_exit(void)
-{
- pci_unregister_driver(&pluto2_driver);
-}
-
-module_init(pluto2_init);
-module_exit(pluto2_exit);
+module_pci_driver(pluto2_driver);
MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
MODULE_DESCRIPTION("Pluto2 driver");
diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index e921108..75ce142 100644
--- a/drivers/media/pci/pt1/pt1.c
+++ b/drivers/media/pci/pt1/pt1.c
@@ -1225,20 +1225,7 @@
.id_table = pt1_id_table,
};
-
-static int __init pt1_init(void)
-{
- return pci_register_driver(&pt1_driver);
-}
-
-
-static void __exit pt1_cleanup(void)
-{
- pci_unregister_driver(&pt1_driver);
-}
-
-module_init(pt1_init);
-module_exit(pt1_cleanup);
+module_pci_driver(pt1_driver);
MODULE_AUTHOR("Takahito HIRANO <hiranotaka@zng.info>");
MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver");
diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c
index f147b05..8ac4b1f 100644
--- a/drivers/media/pci/saa7134/saa6752hs.c
+++ b/drivers/media/pci/saa7134/saa6752hs.c
@@ -34,8 +34,8 @@
#include <linux/types.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/init.h>
#include <linux/crc32.h>
@@ -92,7 +92,12 @@
struct saa6752hs_state {
struct v4l2_subdev sd;
- int chip;
+ struct v4l2_ctrl_handler hdl;
+ struct { /* video bitrate mode control cluster */
+ struct v4l2_ctrl *video_bitrate_mode;
+ struct v4l2_ctrl *video_bitrate;
+ struct v4l2_ctrl *video_bitrate_peak;
+ };
u32 revision;
int has_ac3;
struct saa6752hs_mpeg_params params;
@@ -362,314 +367,70 @@
return 0;
}
-
-static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
- struct v4l2_ext_control *ctrl)
+static int saa6752hs_try_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct saa6752hs_state *h =
+ container_of(ctrl->handler, struct saa6752hs_state, hdl);
+
switch (ctrl->id) {
- case V4L2_CID_MPEG_STREAM_TYPE:
- ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
- break;
- case V4L2_CID_MPEG_STREAM_PID_PMT:
- ctrl->value = params->ts_pid_pmt;
- break;
- case V4L2_CID_MPEG_STREAM_PID_AUDIO:
- ctrl->value = params->ts_pid_audio;
- break;
- case V4L2_CID_MPEG_STREAM_PID_VIDEO:
- ctrl->value = params->ts_pid_video;
- break;
- case V4L2_CID_MPEG_STREAM_PID_PCR:
- ctrl->value = params->ts_pid_pcr;
- break;
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- ctrl->value = params->au_encoding;
- break;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- ctrl->value = params->au_l2_bitrate;
- break;
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- if (!has_ac3)
- return -EINVAL;
- ctrl->value = params->au_ac3_bitrate;
- break;
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
- break;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- ctrl->value = params->vi_aspect;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- ctrl->value = params->vi_bitrate * 1000;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
- ctrl->value = params->vi_bitrate_peak * 1000;
- break;
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- ctrl->value = params->vi_bitrate_mode;
+ /* peak bitrate shall be >= normal bitrate */
+ if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ h->video_bitrate_peak->val < h->video_bitrate->val)
+ h->video_bitrate_peak->val = h->video_bitrate->val;
break;
- default:
- return -EINVAL;
}
return 0;
}
-static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
- struct v4l2_ext_control *ctrl, int set)
+static int saa6752hs_s_ctrl(struct v4l2_ctrl *ctrl)
{
- int old = 0, new;
-
- new = ctrl->value;
- switch (ctrl->id) {
- case V4L2_CID_MPEG_STREAM_TYPE:
- old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
- if (set && new != old)
- return -ERANGE;
- new = old;
- break;
- case V4L2_CID_MPEG_STREAM_PID_PMT:
- old = params->ts_pid_pmt;
- if (set && new > MPEG_PID_MAX)
- return -ERANGE;
- if (new > MPEG_PID_MAX)
- new = MPEG_PID_MAX;
- params->ts_pid_pmt = new;
- break;
- case V4L2_CID_MPEG_STREAM_PID_AUDIO:
- old = params->ts_pid_audio;
- if (set && new > MPEG_PID_MAX)
- return -ERANGE;
- if (new > MPEG_PID_MAX)
- new = MPEG_PID_MAX;
- params->ts_pid_audio = new;
- break;
- case V4L2_CID_MPEG_STREAM_PID_VIDEO:
- old = params->ts_pid_video;
- if (set && new > MPEG_PID_MAX)
- return -ERANGE;
- if (new > MPEG_PID_MAX)
- new = MPEG_PID_MAX;
- params->ts_pid_video = new;
- break;
- case V4L2_CID_MPEG_STREAM_PID_PCR:
- old = params->ts_pid_pcr;
- if (set && new > MPEG_PID_MAX)
- return -ERANGE;
- if (new > MPEG_PID_MAX)
- new = MPEG_PID_MAX;
- params->ts_pid_pcr = new;
- break;
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- old = params->au_encoding;
- if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
- (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
- return -ERANGE;
- params->au_encoding = new;
- break;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- old = params->au_l2_bitrate;
- if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K &&
- new != V4L2_MPEG_AUDIO_L2_BITRATE_384K)
- return -ERANGE;
- if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K)
- new = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
- else
- new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
- params->au_l2_bitrate = new;
- break;
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- if (!has_ac3)
- return -EINVAL;
- old = params->au_ac3_bitrate;
- if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K &&
- new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K)
- return -ERANGE;
- if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K)
- new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K;
- else
- new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K;
- params->au_ac3_bitrate = new;
- break;
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
- if (set && new != old)
- return -ERANGE;
- new = old;
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
- if (set && new != old)
- return -ERANGE;
- new = old;
- break;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- old = params->vi_aspect;
- if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 &&
- new != V4L2_MPEG_VIDEO_ASPECT_4x3)
- return -ERANGE;
- if (new != V4L2_MPEG_VIDEO_ASPECT_16x9)
- new = V4L2_MPEG_VIDEO_ASPECT_4x3;
- params->vi_aspect = new;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- old = params->vi_bitrate * 1000;
- new = 1000 * (new / 1000);
- if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
- return -ERANGE;
- if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
- new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
- params->vi_bitrate = new / 1000;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
- old = params->vi_bitrate_peak * 1000;
- new = 1000 * (new / 1000);
- if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
- return -ERANGE;
- if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
- new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
- params->vi_bitrate_peak = new / 1000;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- old = params->vi_bitrate_mode;
- params->vi_bitrate_mode = new;
- break;
- default:
- return -EINVAL;
- }
- ctrl->value = new;
- return 0;
-}
-
-
-static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
-{
- struct saa6752hs_state *h = to_state(sd);
+ struct saa6752hs_state *h =
+ container_of(ctrl->handler, struct saa6752hs_state, hdl);
struct saa6752hs_mpeg_params *params = &h->params;
- int err;
- switch (qctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
- h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 :
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
- 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
-
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_AUDIO_L2_BITRATE_256K,
- V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
- V4L2_MPEG_AUDIO_L2_BITRATE_256K);
-
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- if (!h->has_ac3)
- return -EINVAL;
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
- V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1,
- V4L2_MPEG_AUDIO_AC3_BITRATE_256K);
-
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
- V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1,
- V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
-
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
-
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_VIDEO_ASPECT_4x3,
- V4L2_MPEG_VIDEO_ASPECT_16x9, 1,
- V4L2_MPEG_VIDEO_ASPECT_4x3);
-
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
- err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
- if (err == 0 &&
- params->vi_bitrate_mode ==
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
- return err;
-
+ switch (ctrl->id) {
case V4L2_CID_MPEG_STREAM_TYPE:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
- V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1,
- V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
-
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- return v4l2_ctrl_query_fill(qctrl,
- V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
- V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
- case V4L2_CID_MPEG_STREAM_PID_PMT:
- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16);
- case V4L2_CID_MPEG_STREAM_PID_AUDIO:
- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260);
- case V4L2_CID_MPEG_STREAM_PID_VIDEO:
- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256);
- case V4L2_CID_MPEG_STREAM_PID_PCR:
- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259);
-
- default:
break;
- }
- return -EINVAL;
-}
-
-static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu)
-{
- static const u32 mpeg_audio_encoding[] = {
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
- V4L2_CTRL_MENU_IDS_END
- };
- static const u32 mpeg_audio_ac3_encoding[] = {
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
- V4L2_MPEG_AUDIO_ENCODING_AC3,
- V4L2_CTRL_MENU_IDS_END
- };
- static u32 mpeg_audio_l2_bitrate[] = {
- V4L2_MPEG_AUDIO_L2_BITRATE_256K,
- V4L2_MPEG_AUDIO_L2_BITRATE_384K,
- V4L2_CTRL_MENU_IDS_END
- };
- static u32 mpeg_audio_ac3_bitrate[] = {
- V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
- V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
- V4L2_CTRL_MENU_IDS_END
- };
- struct saa6752hs_state *h = to_state(sd);
- struct v4l2_queryctrl qctrl;
- int err;
-
- qctrl.id = qmenu->id;
- err = saa6752hs_queryctrl(sd, &qctrl);
- if (err)
- return err;
- switch (qmenu->id) {
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- return v4l2_ctrl_query_menu_valid_items(qmenu,
- mpeg_audio_l2_bitrate);
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- if (!h->has_ac3)
- return -EINVAL;
- return v4l2_ctrl_query_menu_valid_items(qmenu,
- mpeg_audio_ac3_bitrate);
+ case V4L2_CID_MPEG_STREAM_PID_PMT:
+ params->ts_pid_pmt = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+ params->ts_pid_audio = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+ params->ts_pid_video = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_STREAM_PID_PCR:
+ params->ts_pid_pcr = ctrl->val;
+ break;
case V4L2_CID_MPEG_AUDIO_ENCODING:
- return v4l2_ctrl_query_menu_valid_items(qmenu,
- h->has_ac3 ? mpeg_audio_ac3_encoding :
- mpeg_audio_encoding);
+ params->au_encoding = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ params->au_l2_bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ params->au_ac3_bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ break;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ params->vi_aspect = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ params->vi_bitrate_mode = ctrl->val;
+ params->vi_bitrate = h->video_bitrate->val / 1000;
+ params->vi_bitrate_peak = h->video_bitrate_peak->val / 1000;
+ v4l2_ctrl_activate(h->video_bitrate_peak,
+ ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ break;
+ default:
+ return -EINVAL;
}
- return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
+ return 0;
}
static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes)
@@ -793,58 +554,6 @@
return 0;
}
-static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set)
-{
- struct saa6752hs_state *h = to_state(sd);
- struct saa6752hs_mpeg_params params;
- int i;
-
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- params = h->params;
- for (i = 0; i < ctrls->count; i++) {
- int err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, set);
-
- if (err) {
- ctrls->error_idx = i;
- return err;
- }
- }
- if (set)
- h->params = params;
- return 0;
-}
-
-static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
-{
- return saa6752hs_do_ext_ctrls(sd, ctrls, 1);
-}
-
-static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
-{
- return saa6752hs_do_ext_ctrls(sd, ctrls, 0);
-}
-
-static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
-{
- struct saa6752hs_state *h = to_state(sd);
- int i;
-
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- for (i = 0; i < ctrls->count; i++) {
- int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i);
-
- if (err) {
- ctrls->error_idx = i;
- return err;
- }
- }
- return 0;
-}
-
static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
{
struct saa6752hs_state *h = to_state(sd);
@@ -859,10 +568,36 @@
return 0;
}
+static int saa6752hs_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+ int dist_352, dist_480, dist_720;
+
+ f->code = V4L2_MBUS_FMT_FIXED;
+
+ dist_352 = abs(f->width - 352);
+ dist_480 = abs(f->width - 480);
+ dist_720 = abs(f->width - 720);
+ if (dist_720 < dist_480) {
+ f->width = 720;
+ f->height = 576;
+ } else if (dist_480 < dist_352) {
+ f->width = 480;
+ f->height = 576;
+ } else {
+ f->width = 352;
+ if (abs(f->height - 576) < abs(f->height - 288))
+ f->height = 576;
+ else
+ f->height = 288;
+ }
+ f->field = V4L2_FIELD_INTERLACED;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
{
struct saa6752hs_state *h = to_state(sd);
- int dist_352, dist_480, dist_720;
if (f->code != V4L2_MBUS_FMT_FIXED)
return -EINVAL;
@@ -879,30 +614,15 @@
D1 | 720x576 | 720x480
*/
- dist_352 = abs(f->width - 352);
- dist_480 = abs(f->width - 480);
- dist_720 = abs(f->width - 720);
- if (dist_720 < dist_480) {
- f->width = 720;
- f->height = 576;
+ saa6752hs_try_mbus_fmt(sd, f);
+ if (f->width == 720)
h->video_format = SAA6752HS_VF_D1;
- } else if (dist_480 < dist_352) {
- f->width = 480;
- f->height = 576;
+ else if (f->width == 480)
h->video_format = SAA6752HS_VF_2_3_D1;
- } else {
- f->width = 352;
- if (abs(f->height - 576) <
- abs(f->height - 288)) {
- f->height = 576;
- h->video_format = SAA6752HS_VF_1_2_D1;
- } else {
- f->height = 288;
- h->video_format = SAA6752HS_VF_SIF;
- }
- }
- f->field = V4L2_FIELD_INTERLACED;
- f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else if (f->height == 576)
+ h->video_format = SAA6752HS_VF_1_2_D1;
+ else
+ h->video_format = SAA6752HS_VF_SIF;
return 0;
}
@@ -914,30 +634,28 @@
return 0;
}
-static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct saa6752hs_state *h = to_state(sd);
-
- return v4l2_chip_ident_i2c_client(client,
- chip, h->chip, h->revision);
-}
-
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = {
+ .try_ctrl = saa6752hs_try_ctrl,
+ .s_ctrl = saa6752hs_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
- .g_chip_ident = saa6752hs_g_chip_ident,
.init = saa6752hs_init,
- .queryctrl = saa6752hs_queryctrl,
- .querymenu = saa6752hs_querymenu,
- .g_ext_ctrls = saa6752hs_g_ext_ctrls,
- .s_ext_ctrls = saa6752hs_s_ext_ctrls,
- .try_ext_ctrls = saa6752hs_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = saa6752hs_s_std,
};
static const struct v4l2_subdev_video_ops saa6752hs_video_ops = {
.s_mbus_fmt = saa6752hs_s_mbus_fmt,
+ .try_mbus_fmt = saa6752hs_try_mbus_fmt,
.g_mbus_fmt = saa6752hs_g_mbus_fmt,
};
@@ -951,6 +669,7 @@
{
struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL);
struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
u8 addr = 0x13;
u8 data[12];
@@ -963,15 +682,88 @@
i2c_master_send(client, &addr, 1);
i2c_master_recv(client, data, sizeof(data));
- h->chip = V4L2_IDENT_SAA6752HS;
h->revision = (data[8] << 8) | data[9];
h->has_ac3 = 0;
if (h->revision == 0x0206) {
- h->chip = V4L2_IDENT_SAA6752HS_AC3;
h->has_ac3 = 1;
- v4l_info(client, "support AC-3\n");
+ v4l_info(client, "supports AC-3\n");
}
h->params = param_defaults;
+
+ hdl = &h->hdl;
+ v4l2_ctrl_handler_init(hdl, 14);
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 :
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ 0x0d, V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K,
+ ~((1 << V4L2_MPEG_AUDIO_L2_BITRATE_256K) |
+ (1 << V4L2_MPEG_AUDIO_L2_BITRATE_384K)),
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K);
+
+ if (h->has_ac3)
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
+ ~((1 << V4L2_MPEG_AUDIO_AC3_BITRATE_256K) |
+ (1 << V4L2_MPEG_AUDIO_AC3_BITRATE_384K)),
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K);
+
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ ~(1 << V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000),
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+ ~(1 << V4L2_MPEG_VIDEO_ENCODING_MPEG_2),
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_MPEG_VIDEO_ASPECT_16x9, 0x01,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+ h->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 1000000, 27000000, 1000, 8000000);
+
+ v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+ ~(1 << V4L2_MPEG_STREAM_TYPE_MPEG2_TS),
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
+
+ h->video_bitrate_mode = v4l2_ctrl_new_std_menu(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ h->video_bitrate = v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE, 1000000, 27000000, 1000, 6000000);
+ v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_STREAM_PID_PMT, 0, (1 << 14) - 1, 1, 16);
+ v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_STREAM_PID_AUDIO, 0, (1 << 14) - 1, 1, 260);
+ v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_STREAM_PID_VIDEO, 0, (1 << 14) - 1, 1, 256);
+ v4l2_ctrl_new_std(hdl, &saa6752hs_ctrl_ops,
+ V4L2_CID_MPEG_STREAM_PID_PCR, 0, (1 << 14) - 1, 1, 259);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(h);
+ return err;
+ }
+ v4l2_ctrl_cluster(3, &h->video_bitrate_mode);
+ v4l2_ctrl_handler_setup(hdl);
h->standard = 0; /* Assume 625 input lines */
return 0;
}
@@ -981,6 +773,7 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&to_state(sd)->hdl);
kfree(to_state(sd));
return 0;
}
@@ -1002,11 +795,3 @@
};
module_i2c_driver(saa6752hs_driver);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index 66a7081..3022eb2 100644
--- a/drivers/media/pci/saa7134/saa7134-empress.c
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
@@ -28,7 +28,6 @@
#include <media/saa6752hs.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
/* ------------------------------------------------------------------ */
@@ -213,7 +212,7 @@
strlcpy(f->description, "MPEG TS", sizeof(f->description));
f->pixelformat = V4L2_PIX_FMT_MPEG;
-
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
@@ -228,6 +227,8 @@
v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -244,6 +245,8 @@
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -252,9 +255,16 @@
struct v4l2_format *f)
{
struct saa7134_dev *dev = file->private_data;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ saa_call_all(dev, video, try_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -413,21 +423,6 @@
return saa_call_empress(dev, core, querymenu, c);
}
-static int empress_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7134_dev *dev = file->private_data;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER &&
- !strcmp(chip->match.name, "saa6752hs"))
- return saa_call_empress(dev, core, g_chip_ident, chip);
- if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR)
- return saa_call_empress(dev, core, g_chip_ident, chip);
- return -EINVAL;
-}
-
static int empress_s_std(struct file *file, void *priv, v4l2_std_id id)
{
struct saa7134_dev *dev = file->private_data;
@@ -475,7 +470,6 @@
.vidioc_querymenu = empress_querymenu,
.vidioc_g_ctrl = empress_g_ctrl,
.vidioc_s_ctrl = empress_s_ctrl,
- .vidioc_g_chip_ident = empress_g_chip_ident,
.vidioc_s_std = empress_s_std,
.vidioc_g_std = empress_g_std,
};
@@ -488,7 +482,6 @@
.ioctl_ops = &ts_ioctl_ops,
.tvnorms = SAA7134_NORMS,
- .current_norm = V4L2_STD_PAL,
};
static void empress_signal_update(struct work_struct *work)
@@ -518,7 +511,7 @@
if (NULL == dev->empress_dev)
return -ENOMEM;
*(dev->empress_dev) = saa7134_empress_template;
- dev->empress_dev->parent = &dev->pci->dev;
+ dev->empress_dev->v4l2_dev = &dev->v4l2_dev;
dev->empress_dev->release = video_device_release;
snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
"%s empress (%s)", dev->name,
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index cc40938..e12bbd8 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -825,20 +825,22 @@
return 0;
}
-static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win)
+static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win, bool try)
{
enum v4l2_field field;
int maxw, maxh;
- if (NULL == dev->ovbuf.base)
+ if (!try && (dev->ovbuf.base == NULL || dev->ovfmt == NULL))
return -EINVAL;
- if (NULL == dev->ovfmt)
- return -EINVAL;
- if (win->w.width < 48 || win->w.height < 32)
- return -EINVAL;
- if (win->clipcount > 2048)
- return -EINVAL;
+ if (win->w.width < 48)
+ win->w.width = 48;
+ if (win->w.height < 32)
+ win->w.height = 32;
+ if (win->clipcount > 8)
+ win->clipcount = 8;
+ win->chromakey = 0;
+ win->global_alpha = 0;
field = win->field;
maxw = dev->crop_current.width;
maxh = dev->crop_current.height;
@@ -853,10 +855,9 @@
case V4L2_FIELD_BOTTOM:
maxh = maxh / 2;
break;
- case V4L2_FIELD_INTERLACED:
- break;
default:
- return -EINVAL;
+ field = V4L2_FIELD_INTERLACED;
+ break;
}
win->field = field;
@@ -872,20 +873,20 @@
unsigned long base,control,bpl;
int err;
- err = verify_preview(dev,&fh->win);
+ err = verify_preview(dev, &dev->win, false);
if (0 != err)
return err;
- dev->ovfield = fh->win.field;
+ dev->ovfield = dev->win.field;
dprintk("start_preview %dx%d+%d+%d %s field=%s\n",
- fh->win.w.width,fh->win.w.height,
- fh->win.w.left,fh->win.w.top,
- dev->ovfmt->name,v4l2_field_names[dev->ovfield]);
+ dev->win.w.width, dev->win.w.height,
+ dev->win.w.left, dev->win.w.top,
+ dev->ovfmt->name, v4l2_field_names[dev->ovfield]);
/* setup window + clipping */
- set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height,
+ set_size(dev, TASK_B, dev->win.w.width, dev->win.w.height,
V4L2_FIELD_HAS_BOTH(dev->ovfield));
- setup_clipping(dev,fh->clips,fh->nclips,
+ setup_clipping(dev, dev->clips, dev->nclips,
V4L2_FIELD_HAS_BOTH(dev->ovfield));
if (dev->ovfmt->yuv)
saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
@@ -895,8 +896,8 @@
/* dma: setup channel 1 (= Video Task B) */
base = (unsigned long)dev->ovbuf.base;
- base += dev->ovbuf.fmt.bytesperline * fh->win.w.top;
- base += dev->ovfmt->depth/8 * fh->win.w.left;
+ base += dev->ovbuf.fmt.bytesperline * dev->win.w.top;
+ base += dev->ovfmt->depth/8 * dev->win.w.left;
bpl = dev->ovbuf.fmt.bytesperline;
control = SAA7134_RS_CONTROL_BURST_16;
if (dev->ovfmt->bswap)
@@ -1024,38 +1025,38 @@
int err;
/* sanity checks */
- if (NULL == fh->fmt)
+ if (NULL == dev->fmt)
return -EINVAL;
- if (fh->width < 48 ||
- fh->height < 32 ||
- fh->width/4 > dev->crop_current.width ||
- fh->height/4 > dev->crop_current.height ||
- fh->width > dev->crop_bounds.width ||
- fh->height > dev->crop_bounds.height)
+ if (dev->width < 48 ||
+ dev->height < 32 ||
+ dev->width/4 > dev->crop_current.width ||
+ dev->height/4 > dev->crop_current.height ||
+ dev->width > dev->crop_bounds.width ||
+ dev->height > dev->crop_bounds.height)
return -EINVAL;
- size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ size = (dev->width * dev->height * dev->fmt->depth) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
- vb->i,fh->width,fh->height,size,v4l2_field_names[field],
- fh->fmt->name);
- if (buf->vb.width != fh->width ||
- buf->vb.height != fh->height ||
+ vb->i, dev->width, dev->height, size, v4l2_field_names[field],
+ dev->fmt->name);
+ if (buf->vb.width != dev->width ||
+ buf->vb.height != dev->height ||
buf->vb.size != size ||
buf->vb.field != field ||
- buf->fmt != fh->fmt) {
+ buf->fmt != dev->fmt) {
saa7134_dma_free(q,buf);
}
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
buf->vb.size = size;
buf->vb.field = field;
- buf->fmt = fh->fmt;
+ buf->fmt = dev->fmt;
buf->pt = &fh->pt_cap;
dev->video_q.curr = NULL;
@@ -1082,8 +1083,9 @@
buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
- *size = fh->fmt->depth * fh->width * fh->height >> 3;
+ *size = dev->fmt->depth * dev->width * dev->height >> 3;
if (0 == *count)
*count = gbuffers;
*count = saa7134_buffer_count(*size,*count);
@@ -1287,15 +1289,17 @@
/* ------------------------------------------------------------------ */
-static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
+static struct videobuf_queue *saa7134_queue(struct file *file)
{
- struct videobuf_queue* q = NULL;
+ struct video_device *vdev = video_devdata(file);
+ struct saa7134_fh *fh = file->private_data;
+ struct videobuf_queue *q = NULL;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
q = &fh->cap;
break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
q = &fh->vbi;
break;
default:
@@ -1304,12 +1308,14 @@
return q;
}
-static int saa7134_resource(struct saa7134_fh *fh)
+static int saa7134_resource(struct file *file)
{
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ struct video_device *vdev = video_devdata(file);
+
+ if (vdev->vfl_type == VFL_TYPE_GRABBER)
return RESOURCE_VIDEO;
- if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ if (vdev->vfl_type == VFL_TYPE_VBI)
return RESOURCE_VBI;
BUG();
@@ -1321,23 +1327,6 @@
struct video_device *vdev = video_devdata(file);
struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_fh *fh;
- enum v4l2_buf_type type = 0;
- int radio = 0;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
-
- dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev),
- radio, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
@@ -1347,11 +1336,6 @@
v4l2_fh_init(&fh->fh, vdev);
file->private_data = fh;
fh->dev = dev;
- fh->radio = radio;
- fh->type = type;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
- fh->width = 720;
- fh->height = 576;
videobuf_queue_sg_init(&fh->cap, &video_qops,
&dev->pci->dev, &dev->slock,
@@ -1368,7 +1352,7 @@
saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
- if (fh->radio) {
+ if (vdev->vfl_type == VFL_TYPE_RADIO) {
/* switch to radio mode */
saa7134_tvaudio_setinput(dev,&card(dev).radio);
saa_call_all(dev, tuner, s_radio);
@@ -1384,19 +1368,20 @@
static ssize_t
video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7134_fh *fh = file->private_data;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
if (res_locked(fh->dev,RESOURCE_VIDEO))
return -EBUSY;
- return videobuf_read_one(saa7134_queue(fh),
+ return videobuf_read_one(saa7134_queue(file),
data, count, ppos,
file->f_flags & O_NONBLOCK);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
if (!res_get(fh->dev,fh,RESOURCE_VBI))
return -EBUSY;
- return videobuf_read_stream(saa7134_queue(fh),
+ return videobuf_read_stream(saa7134_queue(file),
data, count, ppos, 1,
file->f_flags & O_NONBLOCK);
break;
@@ -1409,11 +1394,12 @@
static unsigned int
video_poll(struct file *file, struct poll_table_struct *wait)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7134_fh *fh = file->private_data;
struct videobuf_buffer *buf = NULL;
unsigned int rc = 0;
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+ if (vdev->vfl_type == VFL_TYPE_VBI)
return videobuf_poll_stream(file, &fh->vbi, wait);
if (res_check(fh,RESOURCE_VIDEO)) {
@@ -1451,6 +1437,7 @@
static int video_release(struct file *file)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7134_fh *fh = file->private_data;
struct saa7134_dev *dev = fh->dev;
struct saa6588_command cmd;
@@ -1489,7 +1476,7 @@
saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
saa_call_all(dev, core, s_power, 0);
- if (fh->radio)
+ if (vdev->vfl_type == VFL_TYPE_RADIO)
saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
/* free stuff */
@@ -1507,9 +1494,7 @@
static int video_mmap(struct file *file, struct vm_area_struct * vma)
{
- struct saa7134_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(saa7134_queue(fh), vma);
+ return videobuf_mmap_mapper(saa7134_queue(file), vma);
}
static ssize_t radio_read(struct file *file, char __user *data,
@@ -1570,15 +1555,18 @@
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ (f->fmt.pix.width * dev->fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -1586,14 +1574,33 @@
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct v4l2_clip __user *clips = f->fmt.win.clips;
+ u32 clipcount = f->fmt.win.clipcount;
+ int err = 0;
+ int i;
if (saa7134_no_overlay > 0) {
printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
- f->fmt.win = fh->win;
+ mutex_lock(&dev->lock);
+ f->fmt.win = dev->win;
+ f->fmt.win.clips = clips;
+ if (clips == NULL)
+ clipcount = 0;
+ if (dev->nclips < clipcount)
+ clipcount = dev->nclips;
+ f->fmt.win.clipcount = clipcount;
- return 0;
+ for (i = 0; !err && i < clipcount; i++) {
+ if (copy_to_user(&f->fmt.win.clips[i].c, &dev->clips[i].c,
+ sizeof(struct v4l2_rect)))
+ err = -EFAULT;
+ }
+ mutex_unlock(&dev->lock);
+
+ return err;
}
static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
@@ -1623,10 +1630,9 @@
case V4L2_FIELD_BOTTOM:
maxh = maxh / 2;
break;
- case V4L2_FIELD_INTERLACED:
- break;
default:
- return -EINVAL;
+ field = V4L2_FIELD_INTERLACED;
+ break;
}
f->fmt.pix.field = field;
@@ -1643,6 +1649,8 @@
(f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -1658,22 +1666,25 @@
return -EINVAL;
}
- return verify_preview(dev, &f->fmt.win);
+ if (f->fmt.win.clips == NULL)
+ f->fmt.win.clipcount = 0;
+ return verify_preview(dev, &f->fmt.win, true);
}
static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
int err;
err = saa7134_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
+ dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
fh->cap.field = f->fmt.pix.field;
return 0;
}
@@ -1690,20 +1701,19 @@
printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
return -EINVAL;
}
- err = verify_preview(dev, &f->fmt.win);
+ if (f->fmt.win.clips == NULL)
+ f->fmt.win.clipcount = 0;
+ err = verify_preview(dev, &f->fmt.win, true);
if (0 != err)
return err;
mutex_lock(&dev->lock);
- fh->win = f->fmt.win;
- fh->nclips = f->fmt.win.clipcount;
+ dev->win = f->fmt.win;
+ dev->nclips = f->fmt.win.clipcount;
- if (fh->nclips > 8)
- fh->nclips = 8;
-
- if (copy_from_user(fh->clips, f->fmt.win.clips,
- sizeof(struct v4l2_clip)*fh->nclips)) {
+ if (copy_from_user(dev->clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip) * dev->nclips)) {
mutex_unlock(&dev->lock);
return -EFAULT;
}
@@ -2057,7 +2067,6 @@
if (0 != f->tuner)
return -EINVAL;
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
saa_call_all(dev, tuner, g_frequency, f);
return 0;
@@ -2071,10 +2080,6 @@
if (0 != f->tuner)
return -EINVAL;
- if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
- if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
- return -EINVAL;
mutex_lock(&dev->lock);
saa_call_all(dev, tuner, s_frequency, f);
@@ -2186,27 +2191,23 @@
static int saa7134_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
- struct saa7134_fh *fh = priv;
- return videobuf_reqbufs(saa7134_queue(fh), p);
+ return videobuf_reqbufs(saa7134_queue(file), p);
}
static int saa7134_querybuf(struct file *file, void *priv,
struct v4l2_buffer *b)
{
- struct saa7134_fh *fh = priv;
- return videobuf_querybuf(saa7134_queue(fh), b);
+ return videobuf_querybuf(saa7134_queue(file), b);
}
static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
- struct saa7134_fh *fh = priv;
- return videobuf_qbuf(saa7134_queue(fh), b);
+ return videobuf_qbuf(saa7134_queue(file), b);
}
static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
- struct saa7134_fh *fh = priv;
- return videobuf_dqbuf(saa7134_queue(fh), b,
+ return videobuf_dqbuf(saa7134_queue(file), b,
file->f_flags & O_NONBLOCK);
}
@@ -2215,7 +2216,7 @@
{
struct saa7134_fh *fh = priv;
struct saa7134_dev *dev = fh->dev;
- int res = saa7134_resource(fh);
+ int res = saa7134_resource(file);
if (!res_get(dev, fh, res))
return -EBUSY;
@@ -2227,11 +2228,11 @@
* Unfortunately, I lack register-level documentation to check the
* Linux FIFO setup and confirm the perfect value.
*/
- pm_qos_add_request(&fh->qos_request,
+ pm_qos_add_request(&dev->qos_request,
PM_QOS_CPU_DMA_LATENCY,
20);
- return videobuf_streamon(saa7134_queue(fh));
+ return videobuf_streamon(saa7134_queue(file));
}
static int saa7134_streamoff(struct file *file, void *priv,
@@ -2240,11 +2241,11 @@
int err;
struct saa7134_fh *fh = priv;
struct saa7134_dev *dev = fh->dev;
- int res = saa7134_resource(fh);
+ int res = saa7134_resource(file);
- pm_qos_remove_request(&fh->qos_request);
+ pm_qos_remove_request(&dev->qos_request);
- err = videobuf_streamoff(saa7134_queue(fh));
+ err = videobuf_streamoff(saa7134_queue(file));
if (err < 0)
return err;
res_free(dev, fh, res);
@@ -2258,9 +2259,7 @@
struct saa7134_fh *fh = priv;
struct saa7134_dev *dev = fh->dev;
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- reg->val = saa_readb(reg->reg);
+ reg->val = saa_readb(reg->reg & 0xffffff);
reg->size = 1;
return 0;
}
@@ -2271,9 +2270,7 @@
struct saa7134_fh *fh = priv;
struct saa7134_dev *dev = fh->dev;
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- saa_writeb(reg->reg&0xffffff, reg->val);
+ saa_writeb(reg->reg & 0xffffff, reg->val);
return 0;
}
#endif
@@ -2287,9 +2284,7 @@
if (0 != t->index)
return -EINVAL;
- memset(t, 0, sizeof(*t));
strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
saa_call_all(dev, tuner, g_tuner, t);
t->audmode &= V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO;
@@ -2443,7 +2438,6 @@
.fops = &video_fops,
.ioctl_ops = &video_ioctl_ops,
.tvnorms = SAA7134_NORMS,
- .current_norm = V4L2_STD_PAL,
};
struct video_device saa7134_radio_template = {
@@ -2480,6 +2474,16 @@
dev->video_q.timeout.function = saa7134_buffer_timeout;
dev->video_q.timeout.data = (unsigned long)(&dev->video_q);
dev->video_q.dev = dev;
+ dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ dev->width = 720;
+ dev->height = 576;
+ dev->win.w.width = dev->width;
+ dev->win.w.height = dev->height;
+ dev->win.field = V4L2_FIELD_INTERLACED;
+ dev->ovbuf.fmt.width = dev->width;
+ dev->ovbuf.fmt.height = dev->height;
+ dev->ovbuf.fmt.pixelformat = dev->fmt->fourcc;
+ dev->ovbuf.fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
if (saa7134_boards[dev->board].video_out)
saa7134_videoport_init(dev);
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
index d2ad16c..8d1453a 100644
--- a/drivers/media/pci/saa7134/saa7134.h
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -471,19 +471,9 @@
struct saa7134_fh {
struct v4l2_fh fh;
struct saa7134_dev *dev;
- unsigned int radio;
- enum v4l2_buf_type type;
unsigned int resources;
- struct pm_qos_request qos_request;
-
- /* video overlay */
- struct v4l2_window win;
- struct v4l2_clip clips[8];
- unsigned int nclips;
/* video capture */
- struct saa7134_format *fmt;
- unsigned int width,height;
struct videobuf_queue cap;
struct saa7134_pgtable pt_cap;
@@ -592,12 +582,19 @@
struct saa7134_format *ovfmt;
unsigned int ovenable;
enum v4l2_field ovfield;
+ struct v4l2_window win;
+ struct v4l2_clip clips[8];
+ unsigned int nclips;
+
/* video+ts+vbi capture */
struct saa7134_dmaqueue video_q;
struct saa7134_dmaqueue vbi_q;
unsigned int video_fieldcount;
unsigned int vbi_fieldcount;
+ struct saa7134_format *fmt;
+ unsigned int width, height;
+ struct pm_qos_request qos_request;
/* various v4l controls */
struct saa7134_tvnorm *tvnorm; /* video */
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
index 71e8bea..33abe33 100644
--- a/drivers/media/pci/saa7146/mxb.c
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -669,14 +669,10 @@
{
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (v4l2_chip_match_host(®->match)) {
- reg->val = saa7146_read(dev, reg->reg);
- reg->size = 4;
- return 0;
- }
- call_all(dev, core, g_register, reg);
+ if (reg->reg > pci_resource_len(dev->pci, 0) - 4)
+ return -EINVAL;
+ reg->val = saa7146_read(dev, reg->reg);
+ reg->size = 4;
return 0;
}
@@ -684,13 +680,10 @@
{
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (v4l2_chip_match_host(®->match)) {
- saa7146_write(dev, reg->reg, reg->val);
- return 0;
- }
- return call_all(dev, core, s_register, reg);
+ if (reg->reg > pci_resource_len(dev->pci, 0) - 4)
+ return -EINVAL;
+ saa7146_write(dev, reg->reg, reg->val);
+ return 0;
}
#endif
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index 7618fda..d37ee37 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -1196,6 +1196,12 @@
if (NULL == dev)
return -ENOMEM;
+ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+ if (err < 0) {
+ dev_err(&pci_dev->dev, "v4l2_device_register failed\n");
+ goto fail_free;
+ }
+
/* pci init */
dev->pci = pci_dev;
if (pci_enable_device(pci_dev)) {
@@ -1367,6 +1373,7 @@
fail_irq:
saa7164_dev_unregister(dev);
fail_free:
+ v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
return err;
}
@@ -1439,6 +1446,7 @@
mutex_unlock(&devlist);
saa7164_dev_unregister(dev);
+ v4l2_device_unregister(&dev->v4l2_dev);
kfree(dev);
}
diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c
index 0b74fb2..9266965 100644
--- a/drivers/media/pci/saa7164/saa7164-encoder.c
+++ b/drivers/media/pci/saa7164/saa7164-encoder.c
@@ -228,6 +228,7 @@
return -EINVAL;
port->encodernorm = saa7164_tvnorms[i];
+ port->std = id;
/* Update the audio decoder while is not running in
* auto detect mode.
@@ -239,6 +240,15 @@
return 0;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+
+ *id = port->std;
+ return 0;
+}
+
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
@@ -1288,46 +1298,9 @@
.unlocked_ioctl = video_ioctl2,
};
-static int saa7164_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
- struct saa7164_dev *dev = port->dev;
- dprintk(DBGLVL_ENC, "%s()\n", __func__);
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int saa7164_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
- struct saa7164_dev *dev = port->dev;
- dprintk(DBGLVL_ENC, "%s()\n", __func__);
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- return 0;
-}
-
-static int saa7164_s_register(struct file *file, void *fh,
- const struct v4l2_dbg_register *reg)
-{
- struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
- struct saa7164_dev *dev = port->dev;
- dprintk(DBGLVL_ENC, "%s()\n", __func__);
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- return 0;
-}
-#endif
-
static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
.vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
@@ -1346,11 +1319,6 @@
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
.vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_chip_ident = saa7164_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = saa7164_g_register,
- .vidioc_s_register = saa7164_s_register,
-#endif
};
static struct video_device saa7164_mpeg_template = {
@@ -1359,7 +1327,6 @@
.ioctl_ops = &mpeg_ioctl_ops,
.minor = -1,
.tvnorms = SAA7164_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
};
static struct video_device *saa7164_encoder_alloc(
@@ -1381,7 +1348,7 @@
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
type, saa7164_boards[dev->board].name);
- vfd->parent = &pci->dev;
+ vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
return vfd;
}
@@ -1426,6 +1393,7 @@
port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3;
port->encoder_params.refdist = 1;
port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE;
+ port->std = V4L2_STD_NTSC_M;
if (port->encodernorm.id & V4L2_STD_525_60)
port->height = 480;
diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c
index da224eb..6e025fe 100644
--- a/drivers/media/pci/saa7164/saa7164-vbi.c
+++ b/drivers/media/pci/saa7164/saa7164-vbi.c
@@ -200,6 +200,7 @@
return -EINVAL;
port->encodernorm = saa7164_tvnorms[i];
+ port->std = id;
/* Update the audio decoder while is not running in
* auto detect mode.
@@ -211,6 +212,15 @@
return 0;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7164_encoder_fh *fh = file->private_data;
+ struct saa7164_port *port = fh->port;
+
+ *id = port->std;
+ return 0;
+}
+
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
@@ -1236,6 +1246,7 @@
static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
.vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
@@ -1254,15 +1265,6 @@
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
.vidioc_queryctrl = vidioc_queryctrl,
-#if 0
- .vidioc_g_chip_ident = saa7164_g_chip_ident,
-#endif
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-#if 0
- .vidioc_g_register = saa7164_g_register,
- .vidioc_s_register = saa7164_s_register,
-#endif
-#endif
.vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt,
.vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt,
.vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt,
@@ -1274,7 +1276,6 @@
.ioctl_ops = &vbi_ioctl_ops,
.minor = -1,
.tvnorms = SAA7164_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
};
static struct video_device *saa7164_vbi_alloc(
@@ -1296,7 +1297,7 @@
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
type, saa7164_boards[dev->board].name);
- vfd->parent = &pci->dev;
+ vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
return vfd;
}
@@ -1333,6 +1334,7 @@
goto failed;
}
+ port->std = V4L2_STD_NTSC_M;
video_set_drvdata(port->v4l_device, port);
result = video_register_device(port->v4l_device,
VFL_TYPE_VBI, -1);
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
index 437284e..8b29e89 100644
--- a/drivers/media/pci/saa7164/saa7164.h
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -63,7 +63,7 @@
#include <dmxdev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-device.h>
#include "saa7164-reg.h"
#include "saa7164-types.h"
@@ -376,6 +376,7 @@
/* Encoder */
/* Defaults established in saa7164-encoder.c */
struct saa7164_tvnorm encodernorm;
+ v4l2_std_id std;
u32 height;
u32 width;
u32 freq;
@@ -427,6 +428,8 @@
struct list_head devlist;
atomic_t refcount;
+ struct v4l2_device v4l2_dev;
+
/* pci stuff */
struct pci_dev *pci;
unsigned char pci_rev, pci_lat;
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index 7005695..77edc11 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -1047,7 +1047,8 @@
ret = sta2x11_vip_init_controls(vip);
if (ret)
goto free_mem;
- if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev))
+ ret = v4l2_device_register(&pdev->dev, &vip->v4l2_dev);
+ if (ret)
goto free_mem;
dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n",
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
index 1f8b1bb..0ba3875 100644
--- a/drivers/media/pci/ttpci/budget-av.c
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -1128,7 +1128,7 @@
// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */
.xtal_freq = 27000000,
- .inversion = IQ_SWAP_OFF, /* 1 */
+ .inversion = IQ_SWAP_OFF,
.lo_clk = 76500000,
.hi_clk = 90000000,
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
index 98e5241..0acf920 100644
--- a/drivers/media/pci/ttpci/budget-ci.c
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -1280,7 +1280,7 @@
.demod_address = 0x68,
.xtal_freq = 27000000,
- .inversion = IQ_SWAP_ON, /* 1 */
+ .inversion = IQ_SWAP_ON,
.lo_clk = 76500000,
.hi_clk = 99000000,
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
index bb53d24..923d59a 100644
--- a/drivers/media/pci/zoran/zoran_card.c
+++ b/drivers/media/pci/zoran/zoran_card.c
@@ -1050,7 +1050,7 @@
* Now add the template and register the device unit.
*/
memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
- zr->video_dev->parent = &zr->pci_dev->dev;
+ zr->video_dev->v4l2_dev = &zr->v4l2_dev;
strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
/* It's not a mem2mem device, but you can both capture and output from
one and the same device. This should really be split up into two
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
index d133c30..e7e9840 100644
--- a/drivers/media/pci/zoran/zoran_driver.c
+++ b/drivers/media/pci/zoran/zoran_driver.c
@@ -1456,29 +1456,6 @@
return -EINVAL;
}
- if (norm == V4L2_STD_ALL) {
- unsigned int status = 0;
- v4l2_std_id std = 0;
-
- decoder_call(zr, video, querystd, &std);
- decoder_call(zr, core, s_std, std);
-
- /* let changes come into effect */
- ssleep(2);
-
- decoder_call(zr, video, g_input_status, &status);
- if (status & V4L2_IN_ST_NO_SIGNAL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no norm detected\n",
- ZR_DEVNAME(zr), __func__);
- /* reset norm */
- decoder_call(zr, core, s_std, zr->norm);
- return -EIO;
- }
-
- norm = std;
- }
if (norm & V4L2_STD_SECAM)
zr->timing = zr->card.tvn[2];
else if (norm & V4L2_STD_NTSC)
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 25eaf61..08de865 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -36,7 +36,7 @@
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
- depends on VIDEO_DEV && ARCH_SHMOBILE
+ depends on VIDEO_DEV && ARCH_SHMOBILE && I2C
select VIDEOBUF_DMA_CONTIG
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 0e55b08..7f838c6 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -32,7 +32,6 @@
#include <linux/time.h>
#include <linux/types.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -649,18 +648,30 @@
return 0;
}
+static int bcap_enum_dv_timings(struct file *file, void *priv,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, video,
+ enum_dv_timings, timings);
+}
+
+static int bcap_query_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, video,
+ query_dv_timings, timings);
+}
+
static int bcap_g_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
struct bcap_device *bcap_dev = video_drvdata(file);
- int ret;
- ret = v4l2_subdev_call(bcap_dev->sd, video,
- g_dv_timings, timings);
- if (ret < 0)
- return ret;
-
- bcap_dev->dv_timings = *timings;
+ *timings = bcap_dev->dv_timings;
return 0;
}
@@ -864,41 +875,6 @@
return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
}
-static int bcap_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int bcap_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- g_register, reg);
-}
-
-static int bcap_dbg_s_register(struct file *file, void *priv,
- const struct v4l2_dbg_register *reg)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- s_register, reg);
-}
-#endif
-
static int bcap_log_status(struct file *file, void *priv)
{
struct bcap_device *bcap_dev = video_drvdata(file);
@@ -921,6 +897,8 @@
.vidioc_g_std = bcap_g_std,
.vidioc_s_dv_timings = bcap_s_dv_timings,
.vidioc_g_dv_timings = bcap_g_dv_timings,
+ .vidioc_query_dv_timings = bcap_query_dv_timings,
+ .vidioc_enum_dv_timings = bcap_enum_dv_timings,
.vidioc_reqbufs = bcap_reqbufs,
.vidioc_querybuf = bcap_querybuf,
.vidioc_qbuf = bcap_qbuf,
@@ -929,11 +907,6 @@
.vidioc_streamoff = bcap_streamoff,
.vidioc_g_parm = bcap_g_parm,
.vidioc_s_parm = bcap_s_parm,
- .vidioc_g_chip_ident = bcap_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = bcap_dbg_g_register,
- .vidioc_s_register = bcap_dbg_s_register,
-#endif
.vidioc_log_status = bcap_log_status,
};
@@ -960,7 +933,7 @@
int ret;
config = pdev->dev.platform_data;
- if (!config) {
+ if (!config || !config->num_inputs) {
v4l2_err(pdev->dev.driver, "Unable to get board config\n");
return -ENODEV;
}
@@ -1031,7 +1004,9 @@
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- vb2_queue_init(q);
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto err_free_handler;
mutex_init(&bcap_dev->mutex);
init_completion(&bcap_dev->comp);
@@ -1067,11 +1042,6 @@
NULL);
if (bcap_dev->sd) {
int i;
- if (!config->num_inputs) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to work without input\n");
- goto err_unreg_vdev;
- }
/* update tvnorms from the sub devices */
for (i = 0; i < config->num_inputs; i++)
@@ -1079,6 +1049,7 @@
} else {
v4l2_err(&bcap_dev->v4l2_dev,
"Unable to register sub device\n");
+ ret = -ENODEV;
goto err_unreg_vdev;
}
diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
index 01b5b50..15e9c2b 100644
--- a/drivers/media/platform/blackfin/ppi.c
+++ b/drivers/media/platform/blackfin/ppi.c
@@ -266,6 +266,18 @@
bfin_write32(®->vcnt, params->height);
if (params->int_mask)
bfin_write32(®->imsk, params->int_mask & 0xFF);
+ if (ppi->ppi_control & PORT_DIR) {
+ u32 hsync_width, vsync_width, vsync_period;
+
+ hsync_width = params->hsync
+ * params->bpp / params->dlen;
+ vsync_width = params->vsync * samples_per_line;
+ vsync_period = samples_per_line * params->frame;
+ bfin_write32(®->fs1_wlhb, hsync_width);
+ bfin_write32(®->fs1_paspl, samples_per_line);
+ bfin_write32(®->fs2_wlvb, vsync_width);
+ bfin_write32(®->fs2_palpf, vsync_period);
+ }
break;
}
default:
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 9d1481a..df4ada88 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -49,16 +49,14 @@
#define CODA_MAX_FRAMEBUFFERS 2
-#define MAX_W 720
-#define MAX_H 576
-#define CODA_MAX_FRAME_SIZE 0x90000
+#define MAX_W 8192
+#define MAX_H 8192
+#define CODA_MAX_FRAME_SIZE 0x100000
#define FMO_SLICE_SAVE_BUF_SIZE (32)
#define CODA_DEFAULT_GAMMA 4096
#define MIN_W 176
#define MIN_H 144
-#define MAX_W 720
-#define MAX_H 576
#define S_ALIGN 1 /* multiple of 2 */
#define W_ALIGN 1 /* multiple of 2 */
@@ -67,7 +65,7 @@
#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh)
static int coda_debug;
-module_param(coda_debug, int, 0);
+module_param(coda_debug, int, 0644);
MODULE_PARM_DESC(coda_debug, "Debug level (0-1)");
enum {
@@ -75,11 +73,6 @@
V4L2_M2M_DST = 1,
};
-enum coda_fmt_type {
- CODA_FMT_ENC,
- CODA_FMT_RAW,
-};
-
enum coda_inst_type {
CODA_INST_ENCODER,
CODA_INST_DECODER,
@@ -93,14 +86,21 @@
struct coda_fmt {
char *name;
u32 fourcc;
- enum coda_fmt_type type;
+};
+
+struct coda_codec {
+ u32 mode;
+ u32 src_fourcc;
+ u32 dst_fourcc;
+ u32 max_w;
+ u32 max_h;
};
struct coda_devtype {
char *firmware;
enum coda_product product;
- struct coda_fmt *formats;
- unsigned int num_formats;
+ struct coda_codec *codecs;
+ unsigned int num_codecs;
size_t workbuf_size;
};
@@ -109,7 +109,7 @@
unsigned int width;
unsigned int height;
unsigned int sizeimage;
- struct coda_fmt *fmt;
+ unsigned int fourcc;
};
struct coda_aux_buf {
@@ -137,12 +137,12 @@
spinlock_t irqlock;
struct mutex dev_mutex;
+ struct mutex coda_mutex;
struct v4l2_m2m_dev *m2m_dev;
struct vb2_alloc_ctx *alloc_ctx;
struct list_head instances;
unsigned long instance_mask;
struct delayed_work timeout;
- struct completion done;
};
struct coda_params {
@@ -164,11 +164,12 @@
struct coda_dev *dev;
struct list_head list;
int aborting;
- int rawstreamon;
- int compstreamon;
+ int streamon_out;
+ int streamon_cap;
u32 isequence;
struct coda_q_data q_data[2];
enum coda_inst_type inst_type;
+ struct coda_codec *codec;
enum v4l2_colorspace colorspace;
struct coda_params params;
struct v4l2_m2m_ctx *m2m_ctx;
@@ -257,62 +258,89 @@
}
/*
- * Add one array of supported formats for each version of Coda:
- * i.MX27 -> codadx6
- * i.MX51 -> coda7
- * i.MX6 -> coda960
+ * Array of all formats supported by any version of Coda:
*/
-static struct coda_fmt codadx6_formats[] = {
+static struct coda_fmt coda_formats[] = {
{
- .name = "YUV 4:2:0 Planar",
+ .name = "YUV 4:2:0 Planar, YCbCr",
.fourcc = V4L2_PIX_FMT_YUV420,
- .type = CODA_FMT_RAW,
+ },
+ {
+ .name = "YUV 4:2:0 Planar, YCrCb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
},
{
.name = "H264 Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264,
- .type = CODA_FMT_ENC,
},
{
.name = "MPEG4 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG4,
- .type = CODA_FMT_ENC,
},
};
-static struct coda_fmt coda7_formats[] = {
- {
- .name = "YUV 4:2:0 Planar",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .type = CODA_FMT_RAW,
- },
- {
- .name = "H264 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H264,
- .type = CODA_FMT_ENC,
- },
- {
- .name = "MPEG4 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG4,
- .type = CODA_FMT_ENC,
- },
+#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
+ { mode, src_fourcc, dst_fourcc, max_w, max_h }
+
+/*
+ * Arrays of codecs supported by each given version of Coda:
+ * i.MX27 -> codadx6
+ * i.MX5x -> coda7
+ * i.MX6 -> coda960
+ * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
+ */
+static struct coda_codec codadx6_codecs[] = {
+ CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576),
+ CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
};
-static struct coda_fmt *find_format(struct coda_dev *dev, struct v4l2_format *f)
+static struct coda_codec coda7_codecs[] = {
+ CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720),
+ CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
+};
+
+static bool coda_format_is_yuv(u32 fourcc)
{
- struct coda_fmt *formats = dev->devtype->formats;
- int num_formats = dev->devtype->num_formats;
- unsigned int k;
+ switch (fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ return true;
+ default:
+ return false;
+ }
+}
- for (k = 0; k < num_formats; k++) {
- if (formats[k].fourcc == f->fmt.pix.pixelformat)
+/*
+ * Normalize all supported YUV 4:2:0 formats to the value used in the codec
+ * tables.
+ */
+static u32 coda_format_normalize_yuv(u32 fourcc)
+{
+ return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
+}
+
+static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc,
+ int dst_fourcc)
+{
+ struct coda_codec *codecs = dev->devtype->codecs;
+ int num_codecs = dev->devtype->num_codecs;
+ int k;
+
+ src_fourcc = coda_format_normalize_yuv(src_fourcc);
+ dst_fourcc = coda_format_normalize_yuv(dst_fourcc);
+ if (src_fourcc == dst_fourcc)
+ return NULL;
+
+ for (k = 0; k < num_codecs; k++) {
+ if (codecs[k].src_fourcc == src_fourcc &&
+ codecs[k].dst_fourcc == dst_fourcc)
break;
}
- if (k == num_formats)
+ if (k == num_codecs)
return NULL;
- return &formats[k];
+ return &codecs[k];
}
/*
@@ -323,7 +351,7 @@
{
strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
strlcpy(cap->card, CODA_NAME, sizeof(cap->card));
- strlcpy(cap->bus_info, CODA_NAME, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
/*
* This is only a mem-to-mem video device. The capture and output
* device capability flags are left only for backward compatibility
@@ -337,17 +365,34 @@
}
static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
- enum coda_fmt_type type)
+ enum v4l2_buf_type type)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_dev *dev = ctx->dev;
- struct coda_fmt *formats = dev->devtype->formats;
+ struct coda_codec *codecs = ctx->dev->devtype->codecs;
+ struct coda_fmt *formats = coda_formats;
struct coda_fmt *fmt;
- int num_formats = dev->devtype->num_formats;
- int i, num = 0;
+ int num_codecs = ctx->dev->devtype->num_codecs;
+ int num_formats = ARRAY_SIZE(coda_formats);
+ int i, k, num = 0;
for (i = 0; i < num_formats; i++) {
- if (formats[i].type == type) {
+ /* Both uncompressed formats are always supported */
+ if (coda_format_is_yuv(formats[i].fourcc)) {
+ if (num == f->index)
+ break;
+ ++num;
+ continue;
+ }
+ /* Compressed formats may be supported, check the codec list */
+ for (k = 0; k < num_codecs; k++) {
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ formats[i].fourcc == codecs[k].dst_fourcc)
+ break;
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ formats[i].fourcc == codecs[k].src_fourcc)
+ break;
+ }
+ if (k < num_codecs) {
if (num == f->index)
break;
++num;
@@ -368,13 +413,13 @@
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return enum_fmt(priv, f, CODA_FMT_ENC);
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return enum_fmt(priv, f, CODA_FMT_RAW);
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT);
}
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
@@ -390,10 +435,10 @@
q_data = get_q_data(ctx, f->type);
f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ f->fmt.pix.pixelformat = q_data->fourcc;
f->fmt.pix.width = q_data->width;
f->fmt.pix.height = q_data->height;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ if (coda_format_is_yuv(f->fmt.pix.pixelformat))
f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2);
else /* encoded formats h.264/mpeg4 */
f->fmt.pix.bytesperline = 0;
@@ -404,8 +449,9 @@
return 0;
}
-static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f)
+static int vidioc_try_fmt(struct coda_codec *codec, struct v4l2_format *f)
{
+ unsigned int max_w, max_h;
enum v4l2_field field;
field = f->fmt.pix.field;
@@ -418,12 +464,21 @@
* if any of the dimensions is unsupported */
f->fmt.pix.field = field;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
- v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
- W_ALIGN, &f->fmt.pix.height,
- MIN_H, MAX_H, H_ALIGN, S_ALIGN);
- f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2);
- f->fmt.pix.sizeimage = f->fmt.pix.width *
+ if (codec) {
+ max_w = codec->max_w;
+ max_h = codec->max_h;
+ } else {
+ max_w = MAX_W;
+ max_h = MAX_H;
+ }
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w,
+ W_ALIGN, &f->fmt.pix.height,
+ MIN_H, max_h, H_ALIGN, S_ALIGN);
+
+ if (coda_format_is_yuv(f->fmt.pix.pixelformat)) {
+ /* Frame stride must be multiple of 8 */
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 8);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
f->fmt.pix.height * 3 / 2;
} else { /*encoded formats h.264/mpeg4 */
f->fmt.pix.bytesperline = 0;
@@ -436,57 +491,38 @@
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- int ret;
- struct coda_fmt *fmt;
struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct coda_codec *codec = NULL;
- fmt = find_format(ctx->dev, f);
- /*
- * Since decoding support is not implemented yet do not allow
- * CODA_FMT_RAW formats in the capture interface.
- */
- if (!fmt || !(fmt->type == CODA_FMT_ENC))
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
+ /* Determine codec by the encoded format */
+ codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
+ f->fmt.pix.pixelformat);
f->fmt.pix.colorspace = ctx->colorspace;
- ret = vidioc_try_fmt(ctx->dev, f);
- if (ret < 0)
- return ret;
-
- return 0;
+ return vidioc_try_fmt(codec, f);
}
static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_fmt *fmt;
- int ret;
+ struct coda_codec *codec;
- fmt = find_format(ctx->dev, f);
- /*
- * Since decoding support is not implemented yet do not allow
- * CODA_FMT formats in the capture interface.
- */
- if (!fmt || !(fmt->type == CODA_FMT_RAW))
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ /* Determine codec by encoded format, returns NULL if raw or invalid */
+ codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
+ V4L2_PIX_FMT_YUV420);
if (!f->fmt.pix.colorspace)
f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
- ret = vidioc_try_fmt(ctx->dev, f);
- if (ret < 0)
- return ret;
-
- return 0;
+ return vidioc_try_fmt(codec, f);
}
static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
{
struct coda_q_data *q_data;
struct vb2_queue *vq;
- int ret;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (!vq)
@@ -501,18 +537,14 @@
return -EBUSY;
}
- ret = vidioc_try_fmt(ctx->dev, f);
- if (ret)
- return ret;
-
- q_data->fmt = find_format(ctx->dev, f);
+ q_data->fourcc = f->fmt.pix.pixelformat;
q_data->width = f->fmt.pix.width;
q_data->height = f->fmt.pix.height;
q_data->sizeimage = f->fmt.pix.sizeimage;
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+ f->type, q_data->width, q_data->height, q_data->fourcc);
return 0;
}
@@ -520,13 +552,14 @@
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
int ret;
ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
- return vidioc_s_fmt(fh_to_ctx(priv), f);
+ return vidioc_s_fmt(ctx, f);
}
static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
@@ -569,6 +602,14 @@
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
+static int vidioc_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
+}
+
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
@@ -617,6 +658,7 @@
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
+ .vidioc_expbuf = vidioc_expbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_create_bufs = vidioc_create_bufs,
@@ -639,11 +681,13 @@
u32 pic_stream_buffer_addr, pic_stream_buffer_size;
u32 dst_fourcc;
+ mutex_lock(&dev->coda_mutex);
+
src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_fourcc = q_data_dst->fmt->fourcc;
+ dst_fourcc = q_data_dst->fourcc;
src_buf->v4l2_buf.sequence = ctx->isequence;
dst_buf->v4l2_buf.sequence = ctx->isequence;
@@ -725,9 +769,20 @@
picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- picture_cb = picture_y + q_data_src->width * q_data_src->height;
- picture_cr = picture_cb + q_data_src->width / 2 *
- q_data_src->height / 2;
+ switch (q_data_src->fourcc) {
+ case V4L2_PIX_FMT_YVU420:
+ /* Switch Cb and Cr for YVU420 format */
+ picture_cr = picture_y + q_data_src->width * q_data_src->height;
+ picture_cb = picture_cr + q_data_src->width / 2 *
+ q_data_src->height / 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ default:
+ picture_cb = picture_y + q_data_src->width * q_data_src->height;
+ picture_cr = picture_cb + q_data_src->width / 2 *
+ q_data_src->height / 2;
+ break;
+ }
coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
@@ -748,7 +803,6 @@
/* 1 second timeout in case CODA locks up */
schedule_delayed_work(&dev->timeout, HZ);
- INIT_COMPLETION(dev->done);
coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
}
@@ -767,6 +821,12 @@
return 0;
}
+ if (ctx->aborting) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: aborting\n");
+ return 0;
+ }
+
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"job ready\n");
return 1;
@@ -775,14 +835,11 @@
static void coda_job_abort(void *priv)
{
struct coda_ctx *ctx = priv;
- struct coda_dev *dev = ctx->dev;
ctx->aborting = 1;
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Aborting task\n");
-
- v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
}
static void coda_lock(void *m2m_priv)
@@ -809,7 +866,12 @@
static void set_default_params(struct coda_ctx *ctx)
{
- struct coda_dev *dev = ctx->dev;
+ int max_w;
+ int max_h;
+
+ ctx->codec = &ctx->dev->devtype->codecs[0];
+ max_w = ctx->codec->max_w;
+ max_h = ctx->codec->max_h;
ctx->params.codec_mode = CODA_MODE_INVALID;
ctx->colorspace = V4L2_COLORSPACE_REC709;
@@ -817,13 +879,13 @@
ctx->aborting = 0;
/* Default formats for output and input queues */
- ctx->q_data[V4L2_M2M_SRC].fmt = &dev->devtype->formats[0];
- ctx->q_data[V4L2_M2M_DST].fmt = &dev->devtype->formats[1];
- ctx->q_data[V4L2_M2M_SRC].width = MAX_W;
- ctx->q_data[V4L2_M2M_SRC].height = MAX_H;
- ctx->q_data[V4L2_M2M_SRC].sizeimage = (MAX_W * MAX_H * 3) / 2;
- ctx->q_data[V4L2_M2M_DST].width = MAX_W;
- ctx->q_data[V4L2_M2M_DST].height = MAX_H;
+ ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
+ ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
+ ctx->q_data[V4L2_M2M_SRC].width = max_w;
+ ctx->q_data[V4L2_M2M_SRC].height = max_h;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
+ ctx->q_data[V4L2_M2M_DST].width = max_w;
+ ctx->q_data[V4L2_M2M_DST].height = max_h;
ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
}
@@ -868,8 +930,6 @@
return -EINVAL;
}
- vb2_set_plane_payload(vb, 0, q_data->sizeimage);
-
return 0;
}
@@ -906,21 +966,34 @@
}
}
+static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
+{
+ struct coda_dev *dev = ctx->dev;
+ u32 *p = ctx->parabuf.vaddr;
+
+ if (dev->devtype->product == CODA_DX6)
+ p[index] = value;
+ else
+ p[index ^ 1] = value;
+}
+
static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc)
{
struct coda_dev *dev = ctx->dev;
int height = q_data->height;
- int width = q_data->width;
- u32 *p;
+ dma_addr_t paddr;
+ int ysize;
int i;
+ ysize = round_up(q_data->width, 8) * height;
+
/* Allocate frame buffers */
ctx->num_internal_frames = CODA_MAX_FRAMEBUFFERS;
for (i = 0; i < ctx->num_internal_frames; i++) {
ctx->internal_frames[i].size = q_data->sizeimage;
if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6)
- ctx->internal_frames[i].size += width / 2 * height / 2;
+ ctx->internal_frames[i].size += ysize/4;
ctx->internal_frames[i].vaddr = dma_alloc_coherent(
&dev->plat_dev->dev, ctx->internal_frames[i].size,
&ctx->internal_frames[i].paddr, GFP_KERNEL);
@@ -931,32 +1004,14 @@
}
/* Register frame buffers in the parameter buffer */
- p = ctx->parabuf.vaddr;
+ for (i = 0; i < ctx->num_internal_frames; i++) {
+ paddr = ctx->internal_frames[i].paddr;
+ coda_parabuf_write(ctx, i * 3 + 0, paddr); /* Y */
+ coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */
+ coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */
- if (dev->devtype->product == CODA_DX6) {
- for (i = 0; i < ctx->num_internal_frames; i++) {
- p[i * 3] = ctx->internal_frames[i].paddr; /* Y */
- p[i * 3 + 1] = p[i * 3] + width * height; /* Cb */
- p[i * 3 + 2] = p[i * 3 + 1] + width / 2 * height / 2; /* Cr */
- }
- } else {
- for (i = 0; i < ctx->num_internal_frames; i += 2) {
- p[i * 3 + 1] = ctx->internal_frames[i].paddr; /* Y */
- p[i * 3] = p[i * 3 + 1] + width * height; /* Cb */
- p[i * 3 + 3] = p[i * 3] + (width / 2) * (height / 2); /* Cr */
-
- if (fourcc == V4L2_PIX_FMT_H264)
- p[96 + i + 1] = p[i * 3 + 3] + (width / 2) * (height / 2);
-
- if (i + 1 < ctx->num_internal_frames) {
- p[i * 3 + 2] = ctx->internal_frames[i+1].paddr; /* Y */
- p[i * 3 + 5] = p[i * 3 + 2] + width * height ; /* Cb */
- p[i * 3 + 4] = p[i * 3 + 5] + (width / 2) * (height / 2); /* Cr */
-
- if (fourcc == V4L2_PIX_FMT_H264)
- p[96 + i] = p[i * 3 + 4] + (width / 2) * (height / 2);
- }
- }
+ if (dev->devtype->product != CODA_DX6 && fourcc == V4L2_PIX_FMT_H264)
+ coda_parabuf_write(ctx, 96 + i, ctx->internal_frames[i].paddr + ysize + ysize/4 + ysize/4);
}
return 0;
@@ -980,6 +1035,28 @@
return nal_size;
}
+static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
+ int header_code, u8 *header, int *size)
+{
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0),
+ CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, vb2_plane_size(buf, 0), CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE);
+ ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+ return ret;
+ }
+ *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(header, vb2_plane_vaddr(buf, 0), *size);
+
+ return 0;
+}
+
static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct coda_ctx *ctx = vb2_get_drv_priv(q);
@@ -990,43 +1067,38 @@
struct vb2_buffer *buf;
u32 dst_fourcc;
u32 value;
- int ret;
+ int ret = 0;
if (count < 1)
return -EINVAL;
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- ctx->rawstreamon = 1;
+ ctx->streamon_out = 1;
else
- ctx->compstreamon = 1;
-
- /* Don't start the coda unless both queues are on */
- if (!(ctx->rawstreamon & ctx->compstreamon))
- return 0;
-
- if (coda_isbusy(dev))
- if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0)
- return -EBUSY;
-
- ctx->gopcounter = ctx->params.gop_size - 1;
+ ctx->streamon_cap = 1;
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (ctx->streamon_out) {
+ if (coda_format_is_yuv(q_data_src->fourcc))
+ ctx->inst_type = CODA_INST_ENCODER;
+ else
+ ctx->inst_type = CODA_INST_DECODER;
+ }
+
+ /* Don't start the coda unless both queues are on */
+ if (!(ctx->streamon_out & ctx->streamon_cap))
+ return 0;
+
+ ctx->gopcounter = ctx->params.gop_size - 1;
buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
bitstream_size = q_data_dst->sizeimage;
- dst_fourcc = q_data_dst->fmt->fourcc;
+ dst_fourcc = q_data_dst->fourcc;
- /* Find out whether coda must encode or decode */
- if (q_data_src->fmt->type == CODA_FMT_RAW &&
- q_data_dst->fmt->type == CODA_FMT_ENC) {
- ctx->inst_type = CODA_INST_ENCODER;
- } else if (q_data_src->fmt->type == CODA_FMT_ENC &&
- q_data_dst->fmt->type == CODA_FMT_RAW) {
- ctx->inst_type = CODA_INST_DECODER;
- v4l2_err(v4l2_dev, "decoding not supported.\n");
- return -EINVAL;
- } else {
+ ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ q_data_dst->fourcc);
+ if (!ctx->codec) {
v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
return -EINVAL;
}
@@ -1035,6 +1107,9 @@
v4l2_err(v4l2_dev, "coda is not initialized.\n");
return -EFAULT;
}
+
+ mutex_lock(&dev->coda_mutex);
+
coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->idx));
coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->idx));
@@ -1057,38 +1132,31 @@
switch (dev->devtype->product) {
case CODA_DX6:
value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET;
+ value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
break;
default:
value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
+ value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
}
- value |= (q_data_src->height & CODA_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
coda_write(dev, ctx->params.framerate,
CODA_CMD_ENC_SEQ_SRC_F_RATE);
+ ctx->params.codec_mode = ctx->codec->mode;
switch (dst_fourcc) {
case V4L2_PIX_FMT_MPEG4:
- if (dev->devtype->product == CODA_DX6)
- ctx->params.codec_mode = CODADX6_MODE_ENCODE_MP4;
- else
- ctx->params.codec_mode = CODA7_MODE_ENCODE_MP4;
-
coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
break;
case V4L2_PIX_FMT_H264:
- if (dev->devtype->product == CODA_DX6)
- ctx->params.codec_mode = CODADX6_MODE_ENCODE_H264;
- else
- ctx->params.codec_mode = CODA7_MODE_ENCODE_H264;
-
coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA);
break;
default:
v4l2_err(v4l2_dev,
"dst format (0x%08x) invalid.\n", dst_fourcc);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
switch (ctx->params.slice_mode) {
@@ -1129,8 +1197,14 @@
value = (CODA_DEFAULT_GAMMA & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET;
coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_GAMMA);
- value = (CODA_DEFAULT_GAMMA > 0) << CODA_OPTION_GAMMA_OFFSET;
- value |= (0 & CODA_OPTION_SLICEREPORT_MASK) << CODA_OPTION_SLICEREPORT_OFFSET;
+ if (CODA_DEFAULT_GAMMA > 0) {
+ if (dev->devtype->product == CODA_DX6)
+ value = 1 << CODADX6_OPTION_GAMMA_OFFSET;
+ else
+ value = 1 << CODA7_OPTION_GAMMA_OFFSET;
+ } else {
+ value = 0;
+ }
coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
if (dst_fourcc == V4L2_PIX_FMT_H264) {
@@ -1145,17 +1219,23 @@
}
}
- if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+ ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT);
+ if (ret < 0) {
v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
- return -ETIMEDOUT;
+ goto out;
}
- if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0)
- return -EFAULT;
+ if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n");
+ ret = -EFAULT;
+ goto out;
+ }
ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
+ goto out;
+ }
coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
coda_write(dev, round_up(q_data_src->width, 8), CODA_CMD_SET_FRAME_BUF_STRIDE);
@@ -1167,9 +1247,10 @@
coda_write(dev, dev->iram_paddr + 68 * 1024, CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
coda_write(dev, 0x0, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
}
- if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+ ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
+ if (ret < 0) {
v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
- return -ETIMEDOUT;
+ goto out;
}
/* Save stream headers */
@@ -1180,33 +1261,22 @@
* Get SPS in the first frame and copy it to an
* intermediate buffer.
*/
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_H264_SPS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[0]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS,
+ &ctx->vpu_header[0][0],
+ &ctx->vpu_header_size[0]);
+ if (ret < 0)
+ goto out;
/*
* Get PPS in the first frame and copy it to an
* intermediate buffer.
*/
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_H264_PPS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[1]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS,
+ &ctx->vpu_header[1][0],
+ &ctx->vpu_header_size[1]);
+ if (ret < 0)
+ goto out;
+
/*
* Length of H.264 headers is variable and thus it might not be
* aligned for the coda to append the encoded frame. In that is
@@ -1222,48 +1292,32 @@
* Get VOS in the first frame and copy it to an
* intermediate buffer
*/
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_MP4V_VOS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[0]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS,
+ &ctx->vpu_header[0][0],
+ &ctx->vpu_header_size[0]);
+ if (ret < 0)
+ goto out;
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_MP4V_VIS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[1]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS,
+ &ctx->vpu_header[1][0],
+ &ctx->vpu_header_size[1]);
+ if (ret < 0)
+ goto out;
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_MP4V_VOL, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[2] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[2][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[2]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL,
+ &ctx->vpu_header[2][0],
+ &ctx->vpu_header_size[2]);
+ if (ret < 0)
+ goto out;
break;
default:
/* No more formats need to save headers at the moment */
break;
}
- return 0;
+out:
+ mutex_unlock(&dev->coda_mutex);
+ return ret;
}
static int coda_stop_streaming(struct vb2_queue *q)
@@ -1274,26 +1328,20 @@
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"%s: output\n", __func__);
- ctx->rawstreamon = 0;
+ ctx->streamon_out = 0;
} else {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"%s: capture\n", __func__);
- ctx->compstreamon = 0;
+ ctx->streamon_cap = 0;
}
/* Don't stop the coda unless both queues are off */
- if (ctx->rawstreamon || ctx->compstreamon)
+ if (ctx->streamon_out || ctx->streamon_cap)
return 0;
- if (coda_isbusy(dev)) {
- if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) {
- v4l2_warn(&dev->v4l2_dev,
- "%s: timeout, sending SEQ_END anyway\n", __func__);
- }
- }
-
cancel_delayed_work(&dev->timeout);
+ mutex_lock(&dev->coda_mutex);
v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"%s: sent command 'SEQ_END' to coda\n", __func__);
if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
@@ -1301,6 +1349,7 @@
"CODA_COMMAND_SEQ_END failed\n");
return -ETIMEDOUT;
}
+ mutex_unlock(&dev->coda_mutex);
coda_free_framebuffers(ctx);
@@ -1431,7 +1480,7 @@
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &coda_qops;
@@ -1443,7 +1492,7 @@
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &coda_qops;
@@ -1484,7 +1533,7 @@
ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
&coda_queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
- int ret = PTR_ERR(ctx->m2m_ctx);
+ ret = PTR_ERR(ctx->m2m_ctx);
v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
__func__, ret);
@@ -1596,12 +1645,14 @@
ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
if (ctx == NULL) {
v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n");
+ mutex_unlock(&dev->coda_mutex);
return IRQ_HANDLED;
}
if (ctx->aborting) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"task has been aborted\n");
+ mutex_unlock(&dev->coda_mutex);
return IRQ_HANDLED;
}
@@ -1611,8 +1662,6 @@
return IRQ_NONE;
}
- complete(&dev->done);
-
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
@@ -1660,6 +1709,8 @@
(dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
"KEYFRAME" : "PFRAME");
+ mutex_unlock(&dev->coda_mutex);
+
v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
return IRQ_HANDLED;
@@ -1671,12 +1722,7 @@
struct coda_dev *dev = container_of(to_delayed_work(work),
struct coda_dev, timeout);
- if (completion_done(&dev->done))
- return;
-
- complete(&dev->done);
-
- v4l2_err(&dev->v4l2_dev, "CODA PIC_RUN timeout, stopping all streams\n");
+ dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout, stopping all streams\n");
mutex_lock(&dev->dev_mutex);
list_for_each_entry(ctx, &dev->instances, list) {
@@ -1684,6 +1730,10 @@
v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
mutex_unlock(&dev->dev_mutex);
+
+ mutex_unlock(&dev->coda_mutex);
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
}
static u32 coda_supported_firmwares[] = {
@@ -1748,6 +1798,10 @@
}
}
+ /* Clear registers */
+ for (i = 0; i < 64; i++)
+ coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4);
+
/* Tell the BIT where to find everything it needs */
coda_write(dev, dev->workbuf.paddr,
CODA_REG_BIT_WORK_BUF_ADDR);
@@ -1911,16 +1965,16 @@
static const struct coda_devtype coda_devdata[] = {
[CODA_IMX27] = {
- .firmware = "v4l-codadx6-imx27.bin",
- .product = CODA_DX6,
- .formats = codadx6_formats,
- .num_formats = ARRAY_SIZE(codadx6_formats),
+ .firmware = "v4l-codadx6-imx27.bin",
+ .product = CODA_DX6,
+ .codecs = codadx6_codecs,
+ .num_codecs = ARRAY_SIZE(codadx6_codecs),
},
[CODA_IMX53] = {
- .firmware = "v4l-coda7541-imx53.bin",
- .product = CODA_7541,
- .formats = coda7_formats,
- .num_formats = ARRAY_SIZE(coda7_formats),
+ .firmware = "v4l-coda7541-imx53.bin",
+ .product = CODA_7541,
+ .codecs = coda7_codecs,
+ .num_codecs = ARRAY_SIZE(coda7_codecs),
},
};
@@ -1962,8 +2016,6 @@
spin_lock_init(&dev->irqlock);
INIT_LIST_HEAD(&dev->instances);
INIT_DELAYED_WORK(&dev->timeout, coda_timeout);
- init_completion(&dev->done);
- complete(&dev->done);
dev->plat_dev = pdev;
dev->clk_per = devm_clk_get(&pdev->dev, "per");
@@ -1985,17 +2037,9 @@
return -ENOENT;
}
- if (devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), CODA_NAME) == NULL) {
- dev_err(&pdev->dev, "failed to request memory region\n");
- return -ENOENT;
- }
- dev->regs_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!dev->regs_base) {
- dev_err(&pdev->dev, "failed to ioremap address region\n");
- return -ENOENT;
- }
+ dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs_base))
+ return PTR_ERR(dev->regs_base);
/* IRQ */
irq = platform_get_irq(pdev, 0);
@@ -2025,6 +2069,7 @@
return ret;
mutex_init(&dev->dev_mutex);
+ mutex_init(&dev->coda_mutex);
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h
index f3f5e43..ace0bf0 100644
--- a/drivers/media/platform/coda.h
+++ b/drivers/media/platform/coda.h
@@ -96,16 +96,12 @@
#define CODA_CMD_ENC_SEQ_BB_START 0x180
#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184
#define CODA_CMD_ENC_SEQ_OPTION 0x188
-#define CODA_OPTION_GAMMA_OFFSET 7
-#define CODA_OPTION_GAMMA_MASK 0x01
+#define CODA7_OPTION_GAMMA_OFFSET 8
+#define CODADX6_OPTION_GAMMA_OFFSET 7
#define CODA_OPTION_LIMITQP_OFFSET 6
-#define CODA_OPTION_LIMITQP_MASK 0x01
#define CODA_OPTION_RCINTRAQP_OFFSET 5
-#define CODA_OPTION_RCINTRAQP_MASK 0x01
#define CODA_OPTION_FMO_OFFSET 4
-#define CODA_OPTION_FMO_MASK 0x01
#define CODA_OPTION_SLICEREPORT_OFFSET 1
-#define CODA_OPTION_SLICEREPORT_MASK 0x01
#define CODA_CMD_ENC_SEQ_COD_STD 0x18c
#define CODA_STD_MPEG4 0
#define CODA_STD_H263 1
@@ -117,7 +113,8 @@
#define CODADX6_PICWIDTH_OFFSET 10
#define CODADX6_PICWIDTH_MASK 0x3ff
#define CODA_PICHEIGHT_OFFSET 0
-#define CODA_PICHEIGHT_MASK 0x3ff
+#define CODADX6_PICHEIGHT_MASK 0x3ff
+#define CODA7_PICHEIGHT_MASK 0xffff
#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194
#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198
#define CODA_MP4PARAM_VERID_OFFSET 6
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index d0b375c..e180ff72 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -944,7 +944,7 @@
cfg->interlaced = vpbe_dev->current_timings.interlaced;
if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
- cfg->pixfmt = PIXFMT_YCbCrI;
+ cfg->pixfmt = PIXFMT_YCBCRI;
/* Change of the default pixel format for both video windows */
if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
@@ -1593,31 +1593,6 @@
return 0;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vpbe_display_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct v4l2_dbg_match *match = ®->match;
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- if (match->type >= 2) {
- v4l2_subdev_call(vpbe_dev->venc,
- core,
- g_register,
- reg);
- }
-
- return 0;
-}
-
-static int vpbe_display_s_register(struct file *file, void *priv,
- const struct v4l2_dbg_register *reg)
-{
- return 0;
-}
-#endif
-
/* vpbe capture ioctl operations */
static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
.vidioc_querycap = vpbe_display_querycap,
@@ -1644,10 +1619,6 @@
.vidioc_s_dv_timings = vpbe_display_s_dv_timings,
.vidioc_g_dv_timings = vpbe_display_g_dv_timings,
.vidioc_enum_dv_timings = vpbe_display_enum_dv_timings,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpbe_display_g_register,
- .vidioc_s_register = vpbe_display_s_register,
-#endif
};
static struct v4l2_file_operations vpbe_fops = {
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index 396a51c..6ed82e8 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
@@ -119,7 +119,7 @@
#define is_rgb_pixfmt(pixfmt) \
(((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888))
#define is_yc_pixfmt(pixfmt) \
- (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \
+ (((pixfmt) == PIXFMT_YCBCRI) || ((pixfmt) == PIXFMT_YCRCBI) || \
((pixfmt) == PIXFMT_NV12))
#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X
#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5)
@@ -360,8 +360,8 @@
osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL,
OSD_TRANSPVALL);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
if (sd->vpbe_type == VPBE_VERSION_3)
osd_modify(sd, OSD_TRANSPVALU_Y, colorkey,
OSD_TRANSPVALU);
@@ -813,8 +813,8 @@
if (osd->vpbe_type == VPBE_VERSION_1)
bad_config = !is_vid_win(layer);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
bad_config = !is_vid_win(layer);
break;
case PIXFMT_RGB888:
@@ -950,9 +950,9 @@
* The caller must ensure that all windows using YC pixfmt use the same
* Cb/Cr order.
*/
- if (pixfmt == PIXFMT_YCbCrI)
+ if (pixfmt == PIXFMT_YCBCRI)
osd_clear(sd, OSD_MODE_CS, OSD_MODE);
- else if (pixfmt == PIXFMT_YCrCbI)
+ else if (pixfmt == PIXFMT_YCRCBI)
osd_set(sd, OSD_MODE_CS, OSD_MODE);
}
@@ -981,8 +981,8 @@
winmd |= (2 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
_osd_enable_rgb888_pixblend(sd, OSDWIN_OSD0);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
winmd |= (3 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
break;
default:
@@ -1128,8 +1128,8 @@
_osd_enable_rgb888_pixblend(sd,
OSDWIN_OSD1);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
winmd |=
(3 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
break;
@@ -1508,7 +1508,7 @@
_osd_init(osd);
/* set default Cb/Cr order */
- osd->yc_pixfmt = PIXFMT_YCbCrI;
+ osd->yc_pixfmt = PIXFMT_YCBCRI;
if (osd->vpbe_type == VPBE_VERSION_3) {
/*
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index ea82a8b..cd08e52 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -17,30 +17,26 @@
* GNU General Public License for more details.
*/
+#include <linux/err.h>
#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/err.h>
#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
#include <linux/v4l2-dv-timings.h>
-#include <mach/hardware.h>
-
#include "vpif.h"
MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
MODULE_LICENSE("GPL");
-#define VPIF_CH0_MAX_MODES (22)
-#define VPIF_CH1_MAX_MODES (02)
-#define VPIF_CH2_MAX_MODES (15)
-#define VPIF_CH3_MAX_MODES (02)
+#define VPIF_CH0_MAX_MODES 22
+#define VPIF_CH1_MAX_MODES 2
+#define VPIF_CH2_MAX_MODES 15
+#define VPIF_CH3_MAX_MODES 2
-static resource_size_t res_len;
-static struct resource *res;
spinlock_t vpif_lock;
void __iomem *vpif_base;
@@ -423,23 +419,12 @@
static int vpif_probe(struct platform_device *pdev)
{
- int status = 0;
+ static struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
-
- res_len = resource_size(res);
-
- res = request_mem_region(res->start, res_len, res->name);
- if (!res)
- return -EBUSY;
-
- vpif_base = ioremap(res->start, res_len);
- if (!vpif_base) {
- status = -EBUSY;
- goto fail;
- }
+ vpif_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vpif_base))
+ return PTR_ERR(vpif_base);
pm_runtime_enable(&pdev->dev);
pm_runtime_get(&pdev->dev);
@@ -447,17 +432,11 @@
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
return 0;
-
-fail:
- release_mem_region(res->start, res_len);
- return status;
}
static int vpif_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- iounmap(vpif_base);
- release_mem_region(res->start, res_len);
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 5f98df1..5514175 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -18,28 +18,16 @@
* TODO : add support for VBI & HBI data service
* add static buffer allocation
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include "vpif_capture.h"
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+
#include "vpif.h"
+#include "vpif_capture.h"
MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
MODULE_LICENSE("GPL");
@@ -1874,66 +1862,6 @@
}
/*
- * vpif_g_chip_ident() - Identify the chip
- * @file: file ptr
- * @priv: file handle
- * @chip: chip identity
- *
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
- vpif_dbg(2, debug, "match_type is invalid.\n");
- return -EINVAL;
- }
-
- return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/*
- * vpif_dbg_g_register() - Read register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be read
- *
- * Debugging only
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(ch->sd, core, g_register, reg);
-}
-
-/*
- * vpif_dbg_s_register() - Write to register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be modified
- *
- * Debugging only
- * Returns zero or -EINVAL if write operations fails.
- */
-static int vpif_dbg_s_register(struct file *file, void *priv,
- const struct v4l2_dbg_register *reg)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(ch->sd, core, s_register, reg);
-}
-#endif
-
-/*
* vpif_log_status() - Status information
* @file: file ptr
* @priv: file handle
@@ -1974,11 +1902,6 @@
.vidioc_query_dv_timings = vpif_query_dv_timings,
.vidioc_s_dv_timings = vpif_s_dv_timings,
.vidioc_g_dv_timings = vpif_g_dv_timings,
- .vidioc_g_chip_ident = vpif_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpif_dbg_g_register,
- .vidioc_s_register = vpif_dbg_s_register,
-#endif
.vidioc_log_status = vpif_log_status,
};
@@ -2092,16 +2015,13 @@
}
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
- "VPIF_Capture", (void *)
- (&vpif_obj.dev[res_idx]->channel_id))) {
- err = -EBUSY;
- for (j = 0; j < i; j++)
- free_irq(j, (void *)
- (&vpif_obj.dev[res_idx]->channel_id));
- goto vpif_int_err;
- }
+ err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
+ IRQF_SHARED, "VPIF_Capture",
+ (void *)(&vpif_obj.dev[res_idx]->
+ channel_id));
+ if (err) {
+ err = -EINVAL;
+ goto vpif_unregister;
}
res_idx++;
}
@@ -2117,7 +2037,7 @@
video_device_release(ch->video_dev);
}
err = -ENOMEM;
- goto vpif_int_err;
+ goto vpif_unregister;
}
/* Initialize field of video device */
@@ -2170,6 +2090,7 @@
if (!vpif_obj.sd[i]) {
vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENODEV;
goto probe_subdev_out;
}
v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
@@ -2217,13 +2138,9 @@
/* Note: does nothing if ch->video_dev == NULL */
video_device_release(ch->video_dev);
}
-vpif_int_err:
+vpif_unregister:
v4l2_device_unregister(&vpif_obj.v4l2_dev);
- for (i = 0; i < res_idx; i++) {
- res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
- for (j = res->start; j <= res->end; j++)
- free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id));
- }
+
return err;
}
@@ -2235,17 +2152,19 @@
*/
static int vpif_remove(struct platform_device *device)
{
- int i;
struct channel_obj *ch;
+ int i;
v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ kfree(vpif_obj.sd);
/* un-register device */
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
/* Unregister video device */
video_unregister_device(ch->video_dev);
+ kfree(vpif_obj.dev[i]);
}
return 0;
}
@@ -2336,47 +2255,4 @@
.remove = vpif_remove,
};
-/**
- * vpif_init: initialize the vpif driver
- *
- * This function registers device and driver to the kernel, requests irq
- * handler and allocates memory
- * for channel objects
- */
-static __init int vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-
-/**
- * vpif_cleanup : This function clean up the vpif capture resources
- *
- * This will un-registers device and driver to the kernel, frees
- * requested irq handler and de-allocates memory allocated for channel
- * objects.
- */
-static void vpif_cleanup(void)
-{
- struct platform_device *pdev;
- struct resource *res;
- int irq_num;
- int i = 0;
-
- pdev = container_of(vpif_dev, struct platform_device, dev);
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
- for (irq_num = res->start; irq_num <= res->end; irq_num++)
- free_irq(irq_num,
- (void *)(&vpif_obj.dev[i]->channel_id));
- i++;
- }
-
- platform_driver_unregister(&vpif_driver);
-
- kfree(vpif_obj.sd);
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++)
- kfree(vpif_obj.dev[i]);
-}
-
-/* Function for module initialization and cleanup */
-module_init(vpif_init);
-module_exit(vpif_cleanup);
+module_platform_driver(vpif_driver);
diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h
index 3d3c1e5..0ebb312 100644
--- a/drivers/media/platform/davinci/vpif_capture.h
+++ b/drivers/media/platform/davinci/vpif_capture.h
@@ -22,11 +22,8 @@
#ifdef __KERNEL__
/* Header files */
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/davinci/vpif_types.h>
+#include <media/v4l2-device.h>
#include "vpif.h"
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 1b3fb5c..e6e5736 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -14,33 +14,15 @@
* GNU General Public License for more details.
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
#include <linux/slab.h>
-#include <asm/irq.h>
-#include <asm/page.h>
-
-#include <media/adv7343.h>
-#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include "vpif_display.h"
#include "vpif.h"
+#include "vpif_display.h"
MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
MODULE_LICENSE("GPL");
@@ -1518,66 +1500,6 @@
}
/*
- * vpif_g_chip_ident() - Identify the chip
- * @file: file ptr
- * @priv: file handle
- * @chip: chip identity
- *
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
- vpif_dbg(2, debug, "match_type is invalid.\n");
- return -EINVAL;
- }
-
- return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/*
- * vpif_dbg_g_register() - Read register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be read
- *
- * Debugging only
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(ch->sd, core, g_register, reg);
-}
-
-/*
- * vpif_dbg_s_register() - Write to register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be modified
- *
- * Debugging only
- * Returns zero or -EINVAL if write operations fails.
- */
-static int vpif_dbg_s_register(struct file *file, void *priv,
- const struct v4l2_dbg_register *reg)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(ch->sd, core, s_register, reg);
-}
-#endif
-
-/*
* vpif_log_status() - Status information
* @file: file ptr
* @priv: file handle
@@ -1616,11 +1538,6 @@
.vidioc_enum_dv_timings = vpif_enum_dv_timings,
.vidioc_s_dv_timings = vpif_s_dv_timings,
.vidioc_g_dv_timings = vpif_g_dv_timings,
- .vidioc_g_chip_ident = vpif_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpif_dbg_g_register,
- .vidioc_s_register = vpif_dbg_s_register,
-#endif
.vidioc_log_status = vpif_log_status,
};
@@ -1734,16 +1651,14 @@
}
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
- "VPIF_Display", (void *)
- (&vpif_obj.dev[res_idx]->channel_id))) {
- err = -EBUSY;
- for (j = 0; j < i; j++)
- free_irq(j, (void *)
- (&vpif_obj.dev[res_idx]->channel_id));
- goto vpif_int_err;
- }
+ err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
+ IRQF_SHARED, "VPIF_Display",
+ (void *)(&vpif_obj.dev[res_idx]->
+ channel_id));
+ if (err) {
+ err = -EINVAL;
+ vpif_err("VPIF IRQ request failed\n");
+ goto vpif_unregister;
}
res_idx++;
}
@@ -1760,7 +1675,7 @@
video_device_release(ch->video_dev);
}
err = -ENOMEM;
- goto vpif_int_err;
+ goto vpif_unregister;
}
/* Initialize field of video device */
@@ -1813,6 +1728,7 @@
NULL);
if (!vpif_obj.sd[i]) {
vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENODEV;
goto probe_subdev_out;
}
@@ -1893,14 +1809,8 @@
/* Note: does nothing if ch->video_dev == NULL */
video_device_release(ch->video_dev);
}
-vpif_int_err:
+vpif_unregister:
v4l2_device_unregister(&vpif_obj.v4l2_dev);
- vpif_err("VPIF IRQ request failed\n");
- for (i = 0; i < res_idx; i++) {
- res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
- for (j = res->start; j <= res->end; j++)
- free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id));
- }
return err;
}
@@ -1915,6 +1825,7 @@
v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ kfree(vpif_obj.sd);
/* un-register device */
for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
/* Get the pointer to the channel object */
@@ -1923,6 +1834,7 @@
video_unregister_device(ch->video_dev);
ch->video_dev = NULL;
+ kfree(vpif_obj.dev[i]);
}
return 0;
@@ -2008,37 +1920,4 @@
.remove = vpif_remove,
};
-static __init int vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-
-/*
- * vpif_cleanup: This function un-registers device and driver to the kernel,
- * frees requested irq handler and de-allocates memory allocated for channel
- * objects.
- */
-static void vpif_cleanup(void)
-{
- struct platform_device *pdev;
- struct resource *res;
- int irq_num;
- int i = 0;
-
- pdev = container_of(vpif_dev, struct platform_device, dev);
-
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
- for (irq_num = res->start; irq_num <= res->end; irq_num++)
- free_irq(irq_num,
- (void *)(&vpif_obj.dev[i]->channel_id));
- i++;
- }
-
- platform_driver_unregister(&vpif_driver);
- kfree(vpif_obj.sd);
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++)
- kfree(vpif_obj.dev[i]);
-}
-
-module_init(vpif_init);
-module_exit(vpif_cleanup);
+module_platform_driver(vpif_driver);
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
index a5a18f7..5d87fc8 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -17,11 +17,8 @@
#define DAVINCIHD_DISPLAY_H
/* Header files */
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/davinci/vpif_types.h>
+#include <media/v4l2-device.h>
#include "vpif.h"
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 33b5ffc..559fab2 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -988,7 +988,7 @@
if (pdev->dev.of_node) {
const struct of_device_id *match;
- match = of_match_node(of_match_ptr(exynos_gsc_match),
+ match = of_match_node(exynos_gsc_match,
pdev->dev.of_node);
if (match)
driver_data = (struct gsc_driverdata *)match->data;
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 436a62a..53ad0f0 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -9,12 +9,16 @@
if VIDEO_SAMSUNG_EXYNOS4_IS
+config VIDEO_EXYNOS4_IS_COMMON
+ tristate
+
config VIDEO_S5P_FIMC
tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
depends on I2C
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select MFD_SYSCON if OF
+ select VIDEO_EXYNOS4_IS_COMMON
help
This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host
interface and video postprocessor (FIMC) devices.
@@ -39,6 +43,7 @@
tristate "EXYNOS FIMC-LITE camera interface driver"
depends on I2C
select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_EXYNOS4_IS_COMMON
help
This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera
host interface.
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile
index f25f463..c2ff29b 100644
--- a/drivers/media/platform/exynos4-is/Makefile
+++ b/drivers/media/platform/exynos4-is/Makefile
@@ -1,10 +1,13 @@
s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o
exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
+s5p-csis-objs := mipi-csis.o
+exynos4-is-common-objs := common.o
+
exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
-s5p-csis-objs := mipi-csis.o
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o
obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o
+obj-$(CONFIG_VIDEO_EXYNOS4_IS_COMMON) += exynos4-is-common.o
diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c
new file mode 100644
index 0000000..0ec210b
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/common.c
@@ -0,0 +1,53 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC Camera Subsystem driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.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/module.h>
+#include <media/s5p_fimc.h>
+#include "common.h"
+
+/* Called with the media graph mutex held or entity->stream_count > 0. */
+struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity)
+{
+ struct media_pad *pad = &entity->pads[0];
+ struct v4l2_subdev *sd;
+
+ while (pad->flags & MEDIA_PAD_FL_SINK) {
+ /* source pad */
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR ||
+ sd->grp_id == GRP_ID_SENSOR)
+ return sd;
+ /* sink pad */
+ pad = &sd->entity.pads[0];
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(fimc_find_remote_sensor);
+
+void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap,
+ unsigned int caps)
+{
+ strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strlcpy(cap->card, dev->driver->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev_name(dev));
+ cap->device_caps = caps;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+}
+EXPORT_SYMBOL(__fimc_vidioc_querycap);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/exynos4-is/common.h
new file mode 100644
index 0000000..75b9c71
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/common.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * 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/device.h>
+#include <linux/videodev2.h>
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity);
+void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap,
+ unsigned int caps);
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 528f413..fb27ff7 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -27,9 +27,10 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
-#include "media-dev.h"
+#include "common.h"
#include "fimc-core.h"
#include "fimc-reg.h"
+#include "media-dev.h"
static int fimc_capture_hw_init(struct fimc_dev *fimc)
{
@@ -119,8 +120,7 @@
spin_unlock_irqrestore(&fimc->slock, flags);
if (streaming)
- return fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 0);
+ return fimc_pipeline_call(&cap->ve, set_stream, 0);
else
return 0;
}
@@ -178,8 +178,9 @@
void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
{
- struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS];
struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_pipeline *p = to_fimc_pipeline(cap->ve.pipe);
+ struct v4l2_subdev *csis = p->subdevs[IDX_CSIS];
struct fimc_frame *f = &cap->ctx->d_frame;
struct fimc_vid_buffer *v_buf;
struct timeval *tv;
@@ -287,8 +288,7 @@
fimc_activate_capture(ctx);
if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- return fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ return fimc_pipeline_call(&vid_cap->ve, set_stream, 1);
}
return 0;
@@ -312,7 +312,7 @@
int ret = fimc_stop_capture(fimc, suspend);
if (ret)
return ret;
- return fimc_pipeline_call(fimc, close, &fimc->pipeline);
+ return fimc_pipeline_call(&fimc->vid_cap.ve, close);
}
static void buffer_queue(struct vb2_buffer *vb);
@@ -320,6 +320,7 @@
int fimc_capture_resume(struct fimc_dev *fimc)
{
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vid_cap->ve;
struct fimc_vid_buffer *buf;
int i;
@@ -328,8 +329,7 @@
INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
vid_cap->buf_index = 0;
- fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &vid_cap->vfd.entity, false);
+ fimc_pipeline_call(ve, open, &ve->vdev.entity, false);
fimc_capture_hw_init(fimc);
clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
@@ -397,7 +397,7 @@
unsigned long size = ctx->d_frame.payload[i];
if (vb2_plane_size(vb, i) < size) {
- v4l2_err(&ctx->fimc_dev->vid_cap.vfd,
+ v4l2_err(&ctx->fimc_dev->vid_cap.ve.vdev,
"User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
@@ -415,6 +415,7 @@
struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vid_cap->ve;
unsigned long flags;
int min_bufs;
@@ -452,9 +453,9 @@
if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
return;
- ret = fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 1);
+ ret = fimc_pipeline_call(ve, set_stream, 1);
if (ret < 0)
- v4l2_err(&vid_cap->vfd, "stream on failed: %d\n", ret);
+ v4l2_err(&ve->vdev, "stream on failed: %d\n", ret);
return;
}
spin_unlock_irqrestore(&fimc->slock, flags);
@@ -470,44 +471,17 @@
.stop_streaming = stop_streaming,
};
-/**
- * fimc_capture_ctrls_create - initialize the control handler
- * Initialize the capture video node control handler and fill it
- * with the FIMC controls. Inherit any sensor's controls if the
- * 'user_subdev_api' flag is false (default behaviour).
- * This function need to be called with the graph mutex held.
- */
-int fimc_capture_ctrls_create(struct fimc_dev *fimc)
-{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR];
- int ret;
-
- if (WARN_ON(vid_cap->ctx == NULL))
- return -ENXIO;
- if (vid_cap->ctx->ctrls.ready)
- return 0;
-
- ret = fimc_ctrls_create(vid_cap->ctx);
-
- if (ret || vid_cap->user_subdev_api || !sensor ||
- !vid_cap->ctx->ctrls.ready)
- return ret;
-
- return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler,
- sensor->ctrl_handler, NULL);
-}
-
static int fimc_capture_set_default_format(struct fimc_dev *fimc);
static int fimc_capture_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vc->ve;
int ret = -EBUSY;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
- fimc_md_graph_lock(fimc);
mutex_lock(&fimc->lock);
if (fimc_m2m_active(fimc))
@@ -520,31 +494,42 @@
ret = v4l2_fh_open(file);
if (ret) {
- pm_runtime_put(&fimc->pdev->dev);
+ pm_runtime_put_sync(&fimc->pdev->dev);
goto unlock;
}
if (v4l2_fh_is_singular_file(file)) {
- ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &fimc->vid_cap.vfd.entity, true);
+ fimc_md_graph_lock(ve);
- if (!ret && !fimc->vid_cap.user_subdev_api)
+ ret = fimc_pipeline_call(ve, open, &ve->vdev.entity, true);
+
+ if (ret == 0 && vc->user_subdev_api && vc->inh_sensor_ctrls) {
+ /*
+ * Recreate controls of the the video node to drop
+ * any controls inherited from the sensor subdev.
+ */
+ fimc_ctrls_delete(vc->ctx);
+
+ ret = fimc_ctrls_create(vc->ctx);
+ if (ret == 0)
+ vc->inh_sensor_ctrls = false;
+ }
+ if (ret == 0)
+ ve->vdev.entity.use_count++;
+
+ fimc_md_graph_unlock(ve);
+
+ if (ret == 0)
ret = fimc_capture_set_default_format(fimc);
- if (!ret)
- ret = fimc_capture_ctrls_create(fimc);
-
if (ret < 0) {
clear_bit(ST_CAPT_BUSY, &fimc->state);
pm_runtime_put_sync(&fimc->pdev->dev);
v4l2_fh_release(file);
- } else {
- fimc->vid_cap.refcnt++;
}
}
unlock:
mutex_unlock(&fimc->lock);
- fimc_md_graph_unlock(fimc);
return ret;
}
@@ -552,30 +537,31 @@
{
struct fimc_dev *fimc = video_drvdata(file);
struct fimc_vid_cap *vc = &fimc->vid_cap;
+ bool close = v4l2_fh_is_singular_file(file);
int ret;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
mutex_lock(&fimc->lock);
- if (v4l2_fh_is_singular_file(file)) {
- if (vc->streaming) {
- media_entity_pipeline_stop(&vc->vfd.entity);
- vc->streaming = false;
- }
- clear_bit(ST_CAPT_BUSY, &fimc->state);
- fimc_stop_capture(fimc, false);
- fimc_pipeline_call(fimc, close, &fimc->pipeline);
- clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
- fimc->vid_cap.refcnt--;
+ if (close && vc->streaming) {
+ media_entity_pipeline_stop(&vc->ve.vdev.entity);
+ vc->streaming = false;
}
- pm_runtime_put(&fimc->pdev->dev);
-
- if (v4l2_fh_is_singular_file(file))
- fimc_ctrls_delete(fimc->vid_cap.ctx);
-
ret = vb2_fop_release(file);
+
+ if (close) {
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_pipeline_call(&vc->ve, close);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ fimc_md_graph_lock(&vc->ve);
+ vc->ve.vdev.entity.use_count--;
+ fimc_md_graph_unlock(&vc->ve);
+ }
+
+ pm_runtime_put_sync(&fimc->pdev->dev);
mutex_unlock(&fimc->lock);
return ret;
@@ -773,7 +759,7 @@
struct media_pad *pad = &me->pads[0];
while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) {
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (!pad)
break;
me = pad->entity;
@@ -797,7 +783,8 @@
bool set)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct fimc_pipeline *p = to_fimc_pipeline(fimc->vid_cap.ve.pipe);
+ struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR];
struct v4l2_subdev_format sfmt;
struct v4l2_mbus_framefmt *mf = &sfmt.format;
struct media_entity *me;
@@ -845,7 +832,7 @@
return ret;
}
- pad = media_entity_remote_source(&me->pads[sfmt.pad]);
+ pad = media_entity_remote_pad(&me->pads[sfmt.pad]);
if (!pad)
return -EINVAL;
me = pad->entity;
@@ -929,57 +916,101 @@
return 0;
}
-static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
+/*
+ * Try or set format on the fimc.X.capture video node and additionally
+ * on the whole pipeline if @try is false.
+ * Locking: the caller must _not_ hold the graph mutex.
+ */
+static int __video_try_or_set_format(struct fimc_dev *fimc,
+ struct v4l2_format *f, bool try,
+ struct fimc_fmt **inp_fmt,
+ struct fimc_fmt **out_fmt)
{
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_mbus_framefmt mf;
- struct fimc_fmt *ffmt = NULL;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vc->ve;
+ struct fimc_ctx *ctx = vc->ctx;
+ unsigned int width = 0, height = 0;
int ret = 0;
- fimc_md_graph_lock(fimc);
- mutex_lock(&fimc->lock);
-
+ /* Pre-configure format at the camera input interface, for JPEG only */
if (fimc_jpeg_fourcc(pix->pixelformat)) {
fimc_capture_try_format(ctx, &pix->width, &pix->height,
NULL, &pix->pixelformat,
FIMC_SD_PAD_SINK_CAM);
- ctx->s_frame.f_width = pix->width;
- ctx->s_frame.f_height = pix->height;
- }
- ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SOURCE);
- if (!ffmt) {
- ret = -EINVAL;
- goto unlock;
+ if (try) {
+ width = pix->width;
+ height = pix->height;
+ } else {
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
}
- if (!fimc->vid_cap.user_subdev_api) {
- mf.width = pix->width;
- mf.height = pix->height;
- mf.code = ffmt->mbus_code;
- fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
- pix->width = mf.width;
- pix->height = mf.height;
- if (ffmt)
- pix->pixelformat = ffmt->fourcc;
+ /* Try the format at the scaler and the DMA output */
+ *out_fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (*out_fmt == NULL)
+ return -EINVAL;
+
+ /* Restore image width/height for JPEG (no resizing supported). */
+ if (try && fimc_jpeg_fourcc(pix->pixelformat)) {
+ pix->width = width;
+ pix->height = height;
}
- fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
+ /* Try to match format at the host and the sensor */
+ if (!vc->user_subdev_api) {
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_mbus_framefmt *mf;
- if (ffmt->flags & FMT_FLAGS_COMPRESSED)
- fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR],
- pix->plane_fmt, ffmt->memplanes, true);
-unlock:
- mutex_unlock(&fimc->lock);
- fimc_md_graph_unlock(fimc);
+ mf = try ? &mbus_fmt : &fimc->vid_cap.ci_fmt;
+
+ mf->code = (*out_fmt)->mbus_code;
+ mf->width = pix->width;
+ mf->height = pix->height;
+
+ fimc_md_graph_lock(ve);
+ ret = fimc_pipeline_try_format(ctx, mf, inp_fmt, try);
+ fimc_md_graph_unlock(ve);
+
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ }
+
+ fimc_adjust_mplane_format(*out_fmt, pix->width, pix->height, pix);
+
+ if ((*out_fmt)->flags & FMT_FLAGS_COMPRESSED) {
+ struct v4l2_subdev *sensor;
+
+ fimc_md_graph_lock(ve);
+
+ sensor = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR);
+ if (sensor)
+ fimc_get_sensor_frame_desc(sensor, pix->plane_fmt,
+ (*out_fmt)->memplanes, try);
+ else
+ ret = -EPIPE;
+
+ fimc_md_graph_unlock(ve);
+ }
return ret;
}
+static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_fmt *out_fmt = NULL, *inp_fmt = NULL;
+
+ return __video_try_or_set_format(fimc, f, true, &inp_fmt, &out_fmt);
+}
+
static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx,
enum fimc_color_fmt color)
{
@@ -997,57 +1028,23 @@
static int __fimc_capture_set_format(struct fimc_dev *fimc,
struct v4l2_format *f)
{
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct fimc_ctx *ctx = vc->ctx;
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.ci_fmt;
struct fimc_frame *ff = &ctx->d_frame;
- struct fimc_fmt *s_fmt = NULL;
+ struct fimc_fmt *inp_fmt = NULL;
int ret, i;
if (vb2_is_busy(&fimc->vid_cap.vbq))
return -EBUSY;
- /* Pre-configure format at camera interface input, for JPEG only */
- if (fimc_jpeg_fourcc(pix->pixelformat)) {
- fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK_CAM);
- ctx->s_frame.f_width = pix->width;
- ctx->s_frame.f_height = pix->height;
- }
- /* Try the format at the scaler and the DMA output */
- ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SOURCE);
- if (!ff->fmt)
- return -EINVAL;
+ ret = __video_try_or_set_format(fimc, f, false, &inp_fmt, &ff->fmt);
+ if (ret < 0)
+ return ret;
/* Update RGB Alpha control state and value range */
fimc_alpha_ctrl_update(ctx);
- /* Try to match format at the host and the sensor */
- if (!fimc->vid_cap.user_subdev_api) {
- mf->code = ff->fmt->mbus_code;
- mf->width = pix->width;
- mf->height = pix->height;
- ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
- if (ret)
- return ret;
-
- pix->width = mf->width;
- pix->height = mf->height;
- }
-
- fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
-
- if (ff->fmt->flags & FMT_FLAGS_COMPRESSED) {
- ret = fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR],
- pix->plane_fmt, ff->fmt->memplanes,
- true);
- if (ret < 0)
- return ret;
- }
-
for (i = 0; i < ff->fmt->memplanes; i++) {
ff->bytesperline[i] = pix->plane_fmt[i].bytesperline;
ff->payload[i] = pix->plane_fmt[i].sizeimage;
@@ -1061,8 +1058,8 @@
fimc_capture_mark_jpeg_xfer(ctx, ff->fmt->color);
/* Reset cropping and set format at the camera interface input */
- if (!fimc->vid_cap.user_subdev_api) {
- ctx->s_frame.fmt = s_fmt;
+ if (!vc->user_subdev_api) {
+ ctx->s_frame.fmt = inp_fmt;
set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
}
@@ -1074,37 +1071,28 @@
struct v4l2_format *f)
{
struct fimc_dev *fimc = video_drvdata(file);
- int ret;
- fimc_md_graph_lock(fimc);
- mutex_lock(&fimc->lock);
- /*
- * The graph is walked within __fimc_capture_set_format() to set
- * the format at subdevs thus the graph mutex needs to be held at
- * this point and acquired before the video mutex, to avoid AB-BA
- * deadlock when fimc_md_link_notify() is called by other thread.
- * Ideally the graph walking and setting format at the whole pipeline
- * should be removed from this driver and handled in userspace only.
- */
- ret = __fimc_capture_set_format(fimc, f);
-
- mutex_unlock(&fimc->lock);
- fimc_md_graph_unlock(fimc);
- return ret;
+ return __fimc_capture_set_format(fimc, f);
}
static int fimc_cap_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct exynos_video_entity *ve = &fimc->vid_cap.ve;
+ struct v4l2_subdev *sd;
if (i->index != 0)
return -EINVAL;
i->type = V4L2_INPUT_TYPE_CAMERA;
+ fimc_md_graph_lock(ve);
+ sd = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR);
+ fimc_md_graph_unlock(ve);
+
if (sd)
strlcpy(i->name, sd->name, sizeof(i->name));
+
return 0;
}
@@ -1130,6 +1118,7 @@
struct v4l2_subdev_format sink_fmt, src_fmt;
struct fimc_vid_cap *vc = &fimc->vid_cap;
struct v4l2_subdev *sd = &vc->subdev;
+ struct fimc_pipeline *p = to_fimc_pipeline(vc->ve.pipe);
struct media_pad *sink_pad, *src_pad;
int i, ret;
@@ -1146,7 +1135,7 @@
if (p->flags & MEDIA_PAD_FL_SINK) {
sink_pad = p;
- src_pad = media_entity_remote_source(sink_pad);
+ src_pad = media_entity_remote_pad(sink_pad);
if (src_pad)
break;
}
@@ -1183,7 +1172,7 @@
src_fmt.format.code != sink_fmt.format.code)
return -EPIPE;
- if (sd == fimc->pipeline.subdevs[IDX_SENSOR] &&
+ if (sd == p->subdevs[IDX_SENSOR] &&
fimc_user_defined_mbus_fmt(src_fmt.format.code)) {
struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES];
struct fimc_frame *frame = &vc->ctx->d_frame;
@@ -1207,9 +1196,8 @@
enum v4l2_buf_type type)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_pipeline *p = &fimc->pipeline;
struct fimc_vid_cap *vc = &fimc->vid_cap;
- struct media_entity *entity = &vc->vfd.entity;
+ struct media_entity *entity = &vc->ve.vdev.entity;
struct fimc_source_info *si = NULL;
struct v4l2_subdev *sd;
int ret;
@@ -1217,11 +1205,11 @@
if (fimc_capture_active(fimc))
return -EBUSY;
- ret = media_entity_pipeline_start(entity, p->m_pipeline);
+ ret = media_entity_pipeline_start(entity, &vc->ve.pipe->mp);
if (ret < 0)
return ret;
- sd = p->subdevs[IDX_SENSOR];
+ sd = __fimc_md_get_subdev(vc->ve.pipe, IDX_SENSOR);
if (sd)
si = v4l2_get_subdev_hostdata(sd);
@@ -1259,14 +1247,15 @@
enum v4l2_buf_type type)
{
struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
int ret;
ret = vb2_ioctl_streamoff(file, priv, type);
if (ret < 0)
return ret;
- media_entity_pipeline_stop(&fimc->vid_cap.vfd.entity);
- fimc->vid_cap.streaming = false;
+ media_entity_pipeline_stop(&vc->ve.vdev.entity);
+ vc->streaming = false;
return 0;
}
@@ -1405,6 +1394,8 @@
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct v4l2_subdev *sensor;
if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
return -EINVAL;
@@ -1416,15 +1407,26 @@
local->entity->name, remote->entity->name, flags,
fimc->vid_cap.input);
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (fimc->vid_cap.input != 0)
- return -EBUSY;
- fimc->vid_cap.input = sd->grp_id;
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ fimc->vid_cap.input = 0;
return 0;
}
- fimc->vid_cap.input = 0;
- return 0;
+ if (vc->input != 0)
+ return -EBUSY;
+
+ vc->input = sd->grp_id;
+
+ if (vc->user_subdev_api || vc->inh_sensor_ctrls)
+ return 0;
+
+ /* Inherit V4L2 controls from the image sensor subdev. */
+ sensor = fimc_find_remote_sensor(&vc->subdev.entity);
+ if (sensor == NULL)
+ return 0;
+
+ return v4l2_ctrl_add_handler(&vc->ctx->ctrls.handler,
+ sensor->ctrl_handler, NULL);
}
static const struct media_entity_operations fimc_sd_media_ops = {
@@ -1720,8 +1722,8 @@
struct v4l2_format fmt = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
.fmt.pix_mp = {
- .width = 640,
- .height = 480,
+ .width = FIMC_DEFAULT_WIDTH,
+ .height = FIMC_DEFAULT_HEIGHT,
.pixelformat = V4L2_PIX_FMT_YUYV,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_JPEG,
@@ -1735,10 +1737,11 @@
static int fimc_register_capture_device(struct fimc_dev *fimc,
struct v4l2_device *v4l2_dev)
{
- struct video_device *vfd = &fimc->vid_cap.vfd;
+ struct video_device *vfd = &fimc->vid_cap.ve.vdev;
struct vb2_queue *q = &fimc->vid_cap.vbq;
struct fimc_ctx *ctx;
struct fimc_vid_cap *vid_cap;
+ struct fimc_fmt *fmt;
int ret = -ENOMEM;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -1784,22 +1787,34 @@
ret = vb2_queue_init(q);
if (ret)
- goto err_ent;
+ goto err_free_ctx;
+
+ /* Default format configuration */
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ vid_cap->ci_fmt.width = FIMC_DEFAULT_WIDTH;
+ vid_cap->ci_fmt.height = FIMC_DEFAULT_HEIGHT;
+ vid_cap->ci_fmt.code = fmt->mbus_code;
+
+ ctx->s_frame.width = FIMC_DEFAULT_WIDTH;
+ ctx->s_frame.height = FIMC_DEFAULT_HEIGHT;
+ ctx->s_frame.fmt = fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_WRITEBACK, 0);
+ vid_cap->wb_fmt = vid_cap->ci_fmt;
+ vid_cap->wb_fmt.code = fmt->mbus_code;
vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0);
if (ret)
- goto err_ent;
- /*
- * For proper order of acquiring/releasing the video
- * and the graph mutex.
- */
- v4l2_disable_ioctl_locking(vfd, VIDIOC_TRY_FMT);
- v4l2_disable_ioctl_locking(vfd, VIDIOC_S_FMT);
+ goto err_free_ctx;
+
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto err_me_cleanup;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
if (ret)
- goto err_vd;
+ goto err_ctrl_free;
v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
vfd->name, video_device_node_name(vfd));
@@ -1807,9 +1822,11 @@
vfd->ctrl_handler = &ctx->ctrls.handler;
return 0;
-err_vd:
+err_ctrl_free:
+ fimc_ctrls_delete(ctx);
+err_me_cleanup:
media_entity_cleanup(&vfd->entity);
-err_ent:
+err_free_ctx:
kfree(ctx);
return ret;
}
@@ -1826,12 +1843,12 @@
if (ret)
return ret;
- fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd);
+ fimc->vid_cap.ve.pipe = v4l2_get_subdev_hostdata(sd);
ret = fimc_register_capture_device(fimc, sd->v4l2_dev);
if (ret) {
fimc_unregister_m2m_device(fimc);
- fimc->pipeline_ops = NULL;
+ fimc->vid_cap.ve.pipe = NULL;
}
return ret;
@@ -1840,19 +1857,26 @@
static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd)
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct video_device *vdev;
if (fimc == NULL)
return;
- fimc_unregister_m2m_device(fimc);
+ mutex_lock(&fimc->lock);
- if (video_is_registered(&fimc->vid_cap.vfd)) {
- video_unregister_device(&fimc->vid_cap.vfd);
- media_entity_cleanup(&fimc->vid_cap.vfd.entity);
- fimc->pipeline_ops = NULL;
+ fimc_unregister_m2m_device(fimc);
+ vdev = &fimc->vid_cap.ve.vdev;
+
+ if (video_is_registered(vdev)) {
+ video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ fimc->vid_cap.ve.pipe = NULL;
}
kfree(fimc->vid_cap.ctx);
fimc->vid_cap.ctx = NULL;
+
+ mutex_unlock(&fimc->lock);
}
static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = {
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index 379a5e9..6489c51 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -213,17 +213,6 @@
return &fimc_formats[index];
}
-void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap,
- unsigned int caps)
-{
- strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver));
- strlcpy(cap->card, dev->driver->name, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "platform:%s", dev_name(dev));
- cap->device_caps = caps;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-}
-
int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
int dw, int dh, int rotation)
{
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index 539a3f7..3d376fa 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -48,6 +48,8 @@
#define FIMC_DEF_MIN_SIZE 16
#define FIMC_DEF_HEIGHT_ALIGN 2
#define FIMC_DEF_HOR_OFFS_ALIGN 1
+#define FIMC_DEFAULT_WIDTH 640
+#define FIMC_DEFAULT_HEIGHT 480
/* indices to the clocks array */
enum {
@@ -283,8 +285,8 @@
/**
* struct fimc_vid_cap - camera capture device information
* @ctx: hardware context data
- * @vfd: video device node for camera capture mode
* @subdev: subdev exposing the FIMC processing block
+ * @ve: exynos video device entity structure
* @vd_pad: fimc video capture node pad
* @sd_pads: fimc video processing block pads
* @ci_fmt: image format at the FIMC camera input (and the scaler output)
@@ -298,15 +300,16 @@
* @frame_count: the frame counter for statistics
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
* @input_index: input (camera sensor) index
- * @refcnt: driver's private reference counter
* @input: capture input type, grp_id of the attached subdev
* @user_subdev_api: true if subdevs are not configured by the host driver
+ * @inh_sensor_ctrls: a flag indicating v4l2 controls are inherited from
+ * an image sensor subdev
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
struct vb2_alloc_ctx *alloc_ctx;
- struct video_device vfd;
struct v4l2_subdev subdev;
+ struct exynos_video_entity ve;
struct media_pad vd_pad;
struct media_pad sd_pads[FIMC_SD_PADS_NUM];
struct v4l2_mbus_framefmt ci_fmt;
@@ -321,9 +324,9 @@
unsigned int reqbufs_count;
bool streaming;
int input_index;
- int refcnt;
u32 input;
bool user_subdev_api;
+ bool inh_sensor_ctrls;
};
/**
@@ -434,8 +437,6 @@
struct fimc_vid_cap vid_cap;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
- struct fimc_pipeline pipeline;
- const struct fimc_pipeline_ops *pipeline_ops;
};
/**
@@ -620,8 +621,6 @@
/* fimc-core.c */
int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f);
-void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap,
- unsigned int caps);
int fimc_ctrls_create(struct fimc_ctx *ctx);
void fimc_ctrls_delete(struct fimc_ctx *ctx);
void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
index c397777..617a798 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
@@ -96,7 +96,7 @@
return clk_prepare_enable(isp_i2c->clock);
}
-UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend,
+static UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend,
fimc_is_i2c_resume, NULL);
static const struct of_device_id fimc_is_i2c_of_match[] = {
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c
index 53fe2a2..c7e7f69 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.c
@@ -38,7 +38,7 @@
memcpy(dst, src, FIMC_IS_PARAM_MAX_SIZE);
}
-void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is)
+static void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is)
{
struct param_global_shotmode *dst, *src;
@@ -47,7 +47,7 @@
__hw_param_copy(dst, src);
}
-void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
+static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
{
struct param_sensor_framerate *dst, *src;
@@ -168,8 +168,8 @@
unsigned int count;
spin_lock_irqsave(&is->slock, flags);
- count = hweight32(config->p_region_index1);
- count += hweight32(config->p_region_index2);
+ count = hweight32(config->p_region_index[0]);
+ count += hweight32(config->p_region_index[1]);
spin_unlock_irqrestore(&is->slock, flags);
return count;
@@ -177,31 +177,30 @@
int __is_hw_update_params(struct fimc_is *is)
{
- unsigned long *p_index1, *p_index2;
+ unsigned long *p_index;
int i, id, ret = 0;
id = is->config_index;
- p_index1 = &is->config[id].p_region_index1;
- p_index2 = &is->config[id].p_region_index2;
+ p_index = &is->config[id].p_region_index[0];
- if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index1))
+ if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index))
__fimc_is_hw_update_param_global_shotmode(is);
- if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index1))
+ if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index))
__fimc_is_hw_update_param_sensor_framerate(is);
for (i = PARAM_ISP_CONTROL; i < PARAM_DRC_CONTROL; i++) {
- if (test_bit(i, p_index1))
+ if (test_bit(i, p_index))
ret = __fimc_is_hw_update_param(is, i);
}
for (i = PARAM_DRC_CONTROL; i < PARAM_SCALERC_CONTROL; i++) {
- if (test_bit(i, p_index1))
+ if (test_bit(i, p_index))
ret = __fimc_is_hw_update_param(is, i);
}
for (i = PARAM_FD_CONTROL; i <= PARAM_FD_CONFIG; i++) {
- if (test_bit((i - 32), p_index2))
+ if (test_bit(i, p_index))
ret = __fimc_is_hw_update_param(is, i);
}
@@ -243,7 +242,7 @@
fd->otf_input.height = mf->height;
if (test_bit(PARAM_ISP_OTF_INPUT,
- &is->config[index].p_region_index1))
+ &is->config[index].p_region_index[0]))
return;
/* Update field */
@@ -368,7 +367,7 @@
unsigned long *p_index;
struct isp_param *isp;
- p_index = &is->config[index].p_region_index1;
+ p_index = &is->config[index].p_region_index[0];
isp = &is->config[index].isp;
switch (cmd) {
@@ -415,7 +414,7 @@
struct isp_param *isp;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index1;
+ p_index = &is->config[index].p_region_index[0];
isp = &is->config[index].isp;
switch (id) {
@@ -476,7 +475,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->control.cmd = val;
@@ -491,7 +490,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.max_number = val;
@@ -511,7 +510,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.roll_angle = val;
@@ -531,7 +530,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.yaw_angle = val;
@@ -551,7 +550,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.smile_mode = val;
@@ -571,7 +570,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.blink_mode = val;
@@ -591,7 +590,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.eye_detect = val;
@@ -611,7 +610,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.mouth_detect = val;
@@ -631,7 +630,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.orientation = val;
@@ -651,7 +650,7 @@
struct fd_param *fd;
unsigned long *p_index;
- p_index = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[1];
fd = &is->config[index].fd;
fd->config.orientation_value = val;
@@ -672,7 +671,7 @@
struct isp_param *isp;
struct drc_param *drc;
struct fd_param *fd;
- unsigned long *p_index1, *p_index2;
+ unsigned long *p_index;
unsigned int index;
index = is->config_index;
@@ -681,8 +680,7 @@
isp = &is->config[index].isp;
drc = &is->config[index].drc;
fd = &is->config[index].fd;
- p_index1 = &is->config[index].p_region_index1;
- p_index2 = &is->config[index].p_region_index2;
+ p_index = &is->config[index].p_region_index[0];
/* Global */
global->shotmode.cmd = 1;
@@ -695,7 +693,7 @@
fimc_is_set_param_bit(is, PARAM_ISP_CONTROL);
isp->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
- if (!test_bit(PARAM_ISP_OTF_INPUT, p_index1)) {
+ if (!test_bit(PARAM_ISP_OTF_INPUT, p_index)) {
isp->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH;
isp->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT;
fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT);
@@ -738,20 +736,20 @@
isp->aa.target = ISP_AA_TARGET_AE | ISP_AA_TARGET_AWB;
fimc_is_set_param_bit(is, PARAM_ISP_AA);
- if (!test_bit(PARAM_ISP_FLASH, p_index1))
+ if (!test_bit(PARAM_ISP_FLASH, p_index))
__is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE,
ISP_FLASH_REDEYE_DISABLE);
- if (!test_bit(PARAM_ISP_AWB, p_index1))
+ if (!test_bit(PARAM_ISP_AWB, p_index))
__is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0);
- if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index1))
+ if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index))
__is_set_isp_effect(is, ISP_IMAGE_EFFECT_DISABLE);
- if (!test_bit(PARAM_ISP_ISO, p_index1))
+ if (!test_bit(PARAM_ISP_ISO, p_index))
__is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0);
- if (!test_bit(PARAM_ISP_ADJUST, p_index1)) {
+ if (!test_bit(PARAM_ISP_ADJUST, p_index)) {
__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 0);
__is_set_isp_adjust(is,
ISP_ADJUST_COMMAND_MANUAL_SATURATION, 0);
@@ -762,7 +760,7 @@
__is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 0);
}
- if (!test_bit(PARAM_ISP_METERING, p_index1)) {
+ if (!test_bit(PARAM_ISP_METERING, p_index)) {
__is_set_isp_metering(is, 0, ISP_METERING_COMMAND_CENTER);
__is_set_isp_metering(is, 1, 0);
__is_set_isp_metering(is, 2, 0);
@@ -770,11 +768,11 @@
__is_set_isp_metering(is, 4, 0);
}
- if (!test_bit(PARAM_ISP_AFC, p_index1))
+ if (!test_bit(PARAM_ISP_AFC, p_index))
__is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0);
isp->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
- if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index1)) {
+ if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index)) {
isp->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH;
isp->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT;
fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT);
@@ -784,7 +782,7 @@
isp->otf_output.order = 0;
isp->otf_output.err = OTF_OUTPUT_ERROR_NONE;
- if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index1)) {
+ if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index)) {
isp->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE;
isp->dma1_output.width = 0;
isp->dma1_output.height = 0;
@@ -800,7 +798,7 @@
fimc_is_set_param_bit(is, PARAM_ISP_DMA1_OUTPUT);
}
- if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index1)) {
+ if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index)) {
isp->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE;
isp->dma2_output.width = 0;
isp->dma2_output.height = 0;
@@ -817,7 +815,7 @@
}
/* Sensor */
- if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) {
+ if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index)) {
if (is->config_index == 0)
__is_set_sensor(is, 0);
}
@@ -827,7 +825,7 @@
__is_set_drc_control(is, CONTROL_BYPASS_ENABLE);
drc->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
- if (!test_bit(PARAM_DRC_OTF_INPUT, p_index1)) {
+ if (!test_bit(PARAM_DRC_OTF_INPUT, p_index)) {
drc->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH;
drc->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT;
fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT);
@@ -850,7 +848,7 @@
fimc_is_set_param_bit(is, PARAM_DRC_DMA_INPUT);
drc->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
- if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index1)) {
+ if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index)) {
drc->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH;
drc->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT;
fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT);
@@ -865,7 +863,7 @@
fd->control.bypass = CONTROL_BYPASS_DISABLE;
fd->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
- if (!test_bit((PARAM_FD_OTF_INPUT - 32), p_index2)) {
+ if (!test_bit(PARAM_FD_OTF_INPUT, p_index)) {
fd->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH;
fd->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT;
fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index d05eaa2..63c68ec 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -89,8 +89,8 @@
mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2));
mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3));
- mcuctl_write(config->p_region_index1, is, MCUCTL_REG_ISSR(4));
- mcuctl_write(config->p_region_index2, is, MCUCTL_REG_ISSR(5));
+ mcuctl_write(config->p_region_index[0], is, MCUCTL_REG_ISSR(4));
+ mcuctl_write(config->p_region_index[1], is, MCUCTL_REG_ISSR(5));
fimc_is_hw_set_intgr0_gd0(is);
return 0;
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 0741945..967f6a9 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -129,7 +129,7 @@
ATCLK_MCUISP_FREQUENCY);
}
-int fimc_is_enable_clocks(struct fimc_is *is)
+static int fimc_is_enable_clocks(struct fimc_is *is)
{
int i, ret;
@@ -149,7 +149,7 @@
return 0;
}
-void fimc_is_disable_clocks(struct fimc_is *is)
+static void fimc_is_disable_clocks(struct fimc_is *is)
{
int i;
@@ -527,8 +527,8 @@
break;
case HIC_SET_PARAMETER:
- is->config[is->config_index].p_region_index1 = 0;
- is->config[is->config_index].p_region_index2 = 0;
+ is->config[is->config_index].p_region_index[0] = 0;
+ is->config[is->config_index].p_region_index[1] = 0;
set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
pr_debug("HIC_SET_PARAMETER\n");
break;
@@ -587,8 +587,8 @@
switch (is->i2h_cmd.args[0]) {
case HIC_SET_PARAMETER:
- is->config[is->config_index].p_region_index1 = 0;
- is->config[is->config_index].p_region_index2 = 0;
+ is->config[is->config_index].p_region_index[0] = 0;
+ is->config[is->config_index].p_region_index[1] = 0;
set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
break;
}
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
index d7db133..61bb012 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -33,8 +33,8 @@
#define FIMC_IS_DRV_NAME "exynos4-fimc-is"
-#define FIMC_IS_FW_FILENAME "fimc_is_fw.bin"
-#define FIMC_IS_SETFILE_6A3 "setfile.bin"
+#define FIMC_IS_FW_FILENAME "exynos4_fimc_is_fw.bin"
+#define FIMC_IS_SETFILE_6A3 "exynos4_s5k6a3_setfile.bin"
#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */
#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */
@@ -225,8 +225,7 @@
struct drc_param drc;
struct fd_param fd;
- unsigned long p_region_index1;
- unsigned long p_region_index2;
+ unsigned long p_region_index[2];
};
/**
@@ -302,10 +301,7 @@
{
struct chain_config *cfg = &is->config[is->config_index];
- if (num >= 32)
- set_bit(num - 32, &cfg->p_region_index2);
- else
- set_bit(num, &cfg->p_region_index1);
+ set_bit(num, &cfg->p_region_index[0]);
}
static inline void fimc_is_set_param_ctrl_cmd(struct fimc_is *is, int cmd)
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index 7ede30b..cf520a7 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -30,8 +30,8 @@
#include "fimc-is-regs.h"
#include "fimc-is.h"
-static int debug;
-module_param_named(debug_isp, debug, int, S_IRUGO | S_IWUSR);
+int fimc_isp_debug;
+module_param_named(debug_isp, fimc_isp_debug, int, S_IRUGO | S_IWUSR);
static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = {
{
@@ -128,57 +128,70 @@
struct v4l2_subdev_format *fmt)
{
struct fimc_isp *isp = v4l2_get_subdevdata(sd);
- struct fimc_is *is = fimc_isp_to_is(isp);
struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct v4l2_mbus_framefmt cur_fmt;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
- fmt->format = *mf;
+ *mf = *v4l2_subdev_get_try_format(fh, fmt->pad);
return 0;
}
mf->colorspace = V4L2_COLORSPACE_SRGB;
mutex_lock(&isp->subdev_lock);
- __is_get_frame_size(is, &cur_fmt);
if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
- /* full camera input frame size */
- mf->width = cur_fmt.width + FIMC_ISP_CAC_MARGIN_WIDTH;
- mf->height = cur_fmt.height + FIMC_ISP_CAC_MARGIN_HEIGHT;
- mf->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ /* ISP OTF input image format */
+ *mf = isp->sink_fmt;
} else {
- /* crop size */
- mf->width = cur_fmt.width;
- mf->height = cur_fmt.height;
- mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ /* ISP OTF output image format */
+ *mf = isp->src_fmt;
+
+ if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) {
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ }
}
mutex_unlock(&isp->subdev_lock);
- v4l2_dbg(1, debug, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n",
- __func__, fmt->pad, mf->code, mf->width, mf->height);
+ isp_dbg(1, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", __func__,
+ fmt->pad, mf->code, mf->width, mf->height);
return 0;
}
static void __isp_subdev_try_format(struct fimc_isp *isp,
- struct v4l2_subdev_format *fmt)
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
{
struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct v4l2_mbus_framefmt *format;
+
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN,
FIMC_ISP_SINK_WIDTH_MAX, 0,
&mf->height, FIMC_ISP_SINK_HEIGHT_MIN,
FIMC_ISP_SINK_HEIGHT_MAX, 0, 0);
- isp->subdev_fmt = *mf;
+ mf->code = V4L2_MBUS_FMT_SGRBG10_1X10;
} else {
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ format = v4l2_subdev_get_try_format(fh,
+ FIMC_ISP_SD_PAD_SINK);
+ else
+ format = &isp->sink_fmt;
+
/* Allow changing format only on sink pad */
- mf->width = isp->subdev_fmt.width - FIMC_ISP_CAC_MARGIN_WIDTH;
- mf->height = isp->subdev_fmt.height - FIMC_ISP_CAC_MARGIN_HEIGHT;
- mf->code = isp->subdev_fmt.code;
+ mf->width = format->width - FIMC_ISP_CAC_MARGIN_WIDTH;
+ mf->height = format->height - FIMC_ISP_CAC_MARGIN_HEIGHT;
+
+ if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) {
+ mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->code = format->code;
+ }
}
}
@@ -191,27 +204,50 @@
struct v4l2_mbus_framefmt *mf = &fmt->format;
int ret = 0;
- v4l2_dbg(1, debug, sd, "%s: pad%d: code: 0x%x, %dx%d\n",
+ isp_dbg(1, sd, "%s: pad%d: code: 0x%x, %dx%d\n",
__func__, fmt->pad, mf->code, mf->width, mf->height);
- mf->colorspace = V4L2_COLORSPACE_SRGB;
-
mutex_lock(&isp->subdev_lock);
- __isp_subdev_try_format(isp, fmt);
+ __isp_subdev_try_format(isp, fh, fmt);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
mf = v4l2_subdev_get_try_format(fh, fmt->pad);
*mf = fmt->format;
- mutex_unlock(&isp->subdev_lock);
- return 0;
+
+ /* Propagate format to the source pads */
+ if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
+ struct v4l2_subdev_format format = *fmt;
+ unsigned int pad;
+
+ for (pad = FIMC_ISP_SD_PAD_SRC_FIFO;
+ pad < FIMC_ISP_SD_PADS_NUM; pad++) {
+ format.pad = pad;
+ __isp_subdev_try_format(isp, fh, &format);
+ mf = v4l2_subdev_get_try_format(fh, pad);
+ *mf = format.format;
+ }
+ }
+ } else {
+ if (sd->entity.stream_count == 0) {
+ if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
+ struct v4l2_subdev_format format = *fmt;
+
+ isp->sink_fmt = *mf;
+
+ format.pad = FIMC_ISP_SD_PAD_SRC_DMA;
+ __isp_subdev_try_format(isp, fh, &format);
+
+ isp->src_fmt = format.format;
+ __is_set_frame_size(is, &isp->src_fmt);
+ } else {
+ isp->src_fmt = *mf;
+ }
+ } else {
+ ret = -EBUSY;
+ }
}
- if (sd->entity.stream_count == 0)
- __is_set_frame_size(is, mf);
- else
- ret = -EBUSY;
mutex_unlock(&isp->subdev_lock);
-
return ret;
}
@@ -221,7 +257,7 @@
struct fimc_is *is = fimc_isp_to_is(isp);
int ret;
- v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on);
+ isp_dbg(1, sd, "%s: on: %d\n", __func__, on);
if (!test_bit(IS_ST_INIT_DONE, &is->state))
return -EBUSY;
@@ -235,8 +271,8 @@
return ret;
}
- v4l2_dbg(1, debug, sd, "changing mode to %d\n",
- is->config_index);
+ isp_dbg(1, sd, "changing mode to %d\n", is->config_index);
+
ret = fimc_is_itf_mode_change(is);
if (ret)
return -EINVAL;
@@ -317,8 +353,8 @@
clear_bit(IS_ST_PWR_ON, &is->state);
clear_bit(IS_ST_INIT_DONE, &is->state);
is->state = 0;
- is->config[is->config_index].p_region_index1 = 0;
- is->config[is->config_index].p_region_index2 = 0;
+ is->config[is->config_index].p_region_index[0] = 0;
+ is->config[is->config_index].p_region_index[1] = 0;
set_bit(IS_ST_IDLE, &is->state);
wmb();
}
@@ -609,6 +645,22 @@
.s_ctrl = fimc_is_s_ctrl,
};
+static void __isp_subdev_set_default_format(struct fimc_isp *isp)
+{
+ struct fimc_is *is = fimc_isp_to_is(isp);
+
+ isp->sink_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH +
+ FIMC_ISP_CAC_MARGIN_WIDTH;
+ isp->sink_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT +
+ FIMC_ISP_CAC_MARGIN_HEIGHT;
+ isp->sink_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ isp->src_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ isp->src_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ isp->src_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ __is_set_frame_size(is, &isp->src_fmt);
+}
+
int fimc_isp_subdev_create(struct fimc_isp *isp)
{
const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops;
@@ -689,6 +741,8 @@
sd->entity.ops = &fimc_is_subdev_media_ops;
v4l2_set_subdevdata(sd, isp);
+ __isp_subdev_set_default_format(isp);
+
return 0;
}
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h
index 800aba7..03bf95a 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.h
+++ b/drivers/media/platform/exynos4-is/fimc-isp.h
@@ -26,6 +26,11 @@
#include <media/v4l2-mediabus.h>
#include <media/s5p_fimc.h>
+extern int fimc_isp_debug;
+
+#define isp_dbg(level, dev, fmt, arg...) \
+ v4l2_dbg(level, fimc_isp_debug, dev, fmt, ## arg)
+
/* FIXME: revisit these constraints */
#define FIMC_ISP_SINK_WIDTH_MIN (16 + 8)
#define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8)
@@ -118,7 +123,6 @@
unsigned int frame_count;
unsigned int reqbufs_count;
int streaming;
- unsigned long payload[FIMC_ISP_MAX_PLANES];
const struct fimc_fmt *format;
};
@@ -128,15 +132,9 @@
* @alloc_ctx: videobuf2 memory allocator context
* @subdev: ISP v4l2_subdev
* @subdev_pads: the ISP subdev media pads
- * @ctrl_handler: v4l2 controls handler
* @test_pattern: test pattern controls
- * @pipeline: video capture pipeline data structure
+ * @ctrls: v4l2 controls structure
* @video_lock: mutex serializing video device and the subdev operations
- * @fmt: pointer to color format description structure
- * @payload: image size in bytes (w x h x bpp)
- * @inp_frame: camera input frame structure
- * @out_frame: DMA output frame structure
- * @source_subdev_grp_id: group id of remote source subdev
* @cac_margin_x: horizontal CAC margin in pixels
* @cac_margin_y: vertical CAC margin in pixels
* @state: driver state flags
@@ -147,17 +145,14 @@
struct vb2_alloc_ctx *alloc_ctx;
struct v4l2_subdev subdev;
struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM];
- struct v4l2_mbus_framefmt subdev_fmt;
+ struct v4l2_mbus_framefmt src_fmt;
+ struct v4l2_mbus_framefmt sink_fmt;
struct v4l2_ctrl *test_pattern;
struct fimc_isp_ctrls ctrls;
struct mutex video_lock;
struct mutex subdev_lock;
- struct fimc_isp_frame inp_frame;
- struct fimc_isp_frame out_frame;
- unsigned int source_subdev_grp_id;
-
unsigned int cac_margin_x;
unsigned int cac_margin_y;
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
index 8cc0d39..72a343e3b 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
@@ -2,15 +2,16 @@
* Register interface file for EXYNOS FIMC-LITE (camera interface) driver
*
* Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.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/io.h>
+#include <linux/bitops.h>
#include <linux/delay.h>
+#include <linux/io.h>
#include <media/s5p_fimc.h>
#include "fimc-lite-reg.h"
@@ -68,7 +69,8 @@
if (atomic_read(&dev->out_path) == FIMC_IO_DMA) {
intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
FLITE_REG_CIGCTRL_IRQ_LASTEN |
- FLITE_REG_CIGCTRL_IRQ_STARTEN;
+ FLITE_REG_CIGCTRL_IRQ_STARTEN |
+ FLITE_REG_CIGCTRL_IRQ_ENDEN;
} else {
/* An output to the FIMC-IS */
intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
@@ -137,7 +139,7 @@
}
if (i == 0 && src_pixfmt_map[i][0] != pixelcode) {
- v4l2_err(&dev->vfd,
+ v4l2_err(&dev->ve.vdev,
"Unsupported pixel code, falling back to %#08x\n",
src_pixfmt_map[i][0]);
}
@@ -215,6 +217,18 @@
flite_hw_set_camera_port(dev, si->mux_id);
}
+static void flite_hw_set_pack12(struct fimc_lite *dev, int on)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
+
+ cfg &= ~FLITE_REG_CIODMAFMT_PACK12;
+
+ if (on)
+ cfg |= FLITE_REG_CIODMAFMT_PACK12;
+
+ writel(cfg, dev->regs + FLITE_REG_CIODMAFMT);
+}
+
static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
{
static const u32 pixcode[4][2] = {
@@ -250,6 +264,38 @@
writel(cfg, dev->regs + FLITE_REG_CIOOFF);
}
+void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf)
+{
+ unsigned int index;
+ u32 cfg;
+
+ if (dev->dd->max_dma_bufs == 1)
+ index = 0;
+ else
+ index = buf->index;
+
+ if (index == 0)
+ writel(buf->paddr, dev->regs + FLITE_REG_CIOSA);
+ else
+ writel(buf->paddr, dev->regs + FLITE_REG_CIOSAN(index - 1));
+
+ cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ);
+ cfg |= BIT(index);
+ writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ);
+}
+
+void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index)
+{
+ u32 cfg;
+
+ if (dev->dd->max_dma_bufs == 1)
+ index = 0;
+
+ cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ);
+ cfg &= ~BIT(index);
+ writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ);
+}
+
/* Enable/disable output DMA, set output pixel size and offsets (composition) */
void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
bool enable)
@@ -267,6 +313,7 @@
flite_hw_set_out_order(dev, f);
flite_hw_set_dma_window(dev, f);
+ flite_hw_set_pack12(dev, 0);
}
void flite_hw_dump_regs(struct fimc_lite *dev, const char *label)
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.h b/drivers/media/platform/exynos4-is/fimc-lite-reg.h
index 3903839..10a7d7b 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.h
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.h
@@ -120,6 +120,9 @@
/* b0: 1 - camera B, 0 - camera A */
#define FLITE_REG_CIGENERAL_CAM_B (1 << 0)
+#define FLITE_REG_CIFCNTSEQ 0x100
+#define FLITE_REG_CIOSAN(x) (0x200 + (4 * (x)))
+
/* ----------------------------------------------------------------------------
* Function declarations
*/
@@ -142,9 +145,12 @@
void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f);
void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on);
void flite_hw_dump_regs(struct fimc_lite *dev, const char *label);
+void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf);
+void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index);
-static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr)
+static inline void flite_hw_set_dma_buf_mask(struct fimc_lite *dev, u32 mask)
{
- writel(paddr, dev->regs + FLITE_REG_CIOSA);
+ writel(mask, dev->regs + FLITE_REG_CIFCNTSEQ);
}
+
#endif /* FIMC_LITE_REG_H */
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index 14bb7bc..08fbfed 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -1,8 +1,8 @@
/*
* Samsung EXYNOS FIMC-LITE (camera host interface) driver
*
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.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
@@ -32,6 +32,7 @@
#include <media/videobuf2-dma-contig.h>
#include <media/s5p_fimc.h>
+#include "common.h"
#include "fimc-core.h"
#include "fimc-lite.h"
#include "fimc-lite-reg.h"
@@ -43,6 +44,7 @@
{
.name = "YUV 4:2:2 packed, YCbYCr",
.fourcc = V4L2_PIX_FMT_YUYV,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
@@ -51,6 +53,7 @@
}, {
.name = "YUV 4:2:2 packed, CbYCrY",
.fourcc = V4L2_PIX_FMT_UYVY,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_CBYCRY422,
.memplanes = 1,
@@ -59,6 +62,7 @@
}, {
.name = "YUV 4:2:2 packed, CrYCbY",
.fourcc = V4L2_PIX_FMT_VYUY,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_CRYCBY422,
.memplanes = 1,
@@ -67,6 +71,7 @@
}, {
.name = "YUV 4:2:2 packed, YCrYCb",
.fourcc = V4L2_PIX_FMT_YVYU,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_YCRYCB422,
.memplanes = 1,
@@ -75,6 +80,7 @@
}, {
.name = "RAW8 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.depth = { 8 },
.color = FIMC_FMT_RAW8,
.memplanes = 1,
@@ -83,6 +89,7 @@
}, {
.name = "RAW10 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.depth = { 10 },
.color = FIMC_FMT_RAW10,
.memplanes = 1,
@@ -91,6 +98,7 @@
}, {
.name = "RAW12 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG12,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.depth = { 12 },
.color = FIMC_FMT_RAW12,
.memplanes = 1,
@@ -131,30 +139,6 @@
return def_fmt;
}
-/* Called with the media graph mutex held or @me stream_count > 0. */
-static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me)
-{
- struct media_pad *pad = &me->pads[0];
- struct v4l2_subdev *sd;
-
- while (pad->flags & MEDIA_PAD_FL_SINK) {
- /* source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR ||
- sd->grp_id == GRP_ID_SENSOR)
- return sd;
- /* sink pad */
- pad = &sd->entity.pads[0];
- }
- return NULL;
-}
-
static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output)
{
struct fimc_source_info *si;
@@ -176,6 +160,7 @@
flite_hw_set_camera_bus(fimc, si);
flite_hw_set_source_format(fimc, &fimc->inp_frame);
flite_hw_set_window_offset(fimc, &fimc->inp_frame);
+ flite_hw_set_dma_buf_mask(fimc, 0);
flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output);
flite_hw_set_interrupt_mask(fimc);
flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
@@ -233,7 +218,7 @@
if (!streaming)
return 0;
- return fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 0);
+ return fimc_pipeline_call(&fimc->ve, set_stream, 0);
}
static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend)
@@ -299,19 +284,23 @@
if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) &&
test_bit(ST_FLITE_RUN, &fimc->state) &&
- !list_empty(&fimc->active_buf_q) &&
!list_empty(&fimc->pending_buf_q)) {
+ vbuf = fimc_lite_pending_queue_pop(fimc);
+ flite_hw_set_dma_buffer(fimc, vbuf);
+ fimc_lite_active_queue_add(fimc, vbuf);
+ }
+
+ if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMEND) &&
+ test_bit(ST_FLITE_RUN, &fimc->state) &&
+ !list_empty(&fimc->active_buf_q)) {
vbuf = fimc_lite_active_queue_pop(fimc);
ktime_get_ts(&ts);
tv = &vbuf->vb.v4l2_buf.timestamp;
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
vbuf->vb.v4l2_buf.sequence = fimc->frame_count++;
+ flite_hw_mask_dma_buffer(fimc, vbuf->index);
vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE);
-
- vbuf = fimc_lite_pending_queue_pop(fimc);
- flite_hw_set_output_addr(fimc, vbuf->paddr);
- fimc_lite_active_queue_add(fimc, vbuf);
}
if (test_bit(ST_FLITE_CONFIG, &fimc->state))
@@ -330,10 +319,16 @@
static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct fimc_lite *fimc = q->drv_priv;
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ fimc->buf_index = 0;
fimc->frame_count = 0;
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
ret = fimc_lite_hw_init(fimc, false);
if (ret) {
fimc_lite_reinit(fimc, false);
@@ -347,8 +342,7 @@
flite_hw_capture_start(fimc);
if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
- fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ fimc_pipeline_call(&fimc->ve, set_stream, 1);
}
if (debug > 0)
flite_hw_dump_regs(fimc, __func__);
@@ -415,7 +409,7 @@
unsigned long size = fimc->payload[i];
if (vb2_plane_size(vb, i) < size) {
- v4l2_err(&fimc->vfd,
+ v4l2_err(&fimc->ve.vdev,
"User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
@@ -436,10 +430,14 @@
spin_lock_irqsave(&fimc->slock, flags);
buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf->index = fimc->buf_index++;
+ if (fimc->buf_index >= fimc->reqbufs_count)
+ fimc->buf_index = 0;
+
if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) &&
!test_bit(ST_FLITE_STREAM, &fimc->state) &&
list_empty(&fimc->active_buf_q)) {
- flite_hw_set_output_addr(fimc, buf->paddr);
+ flite_hw_set_dma_buffer(fimc, buf);
fimc_lite_active_queue_add(fimc, buf);
} else {
fimc_lite_pending_queue_add(fimc, buf);
@@ -452,8 +450,7 @@
spin_unlock_irqrestore(&fimc->slock, flags);
if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
- fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ fimc_pipeline_call(&fimc->ve, set_stream, 1);
return;
}
spin_unlock_irqrestore(&fimc->slock, flags);
@@ -481,11 +478,9 @@
static int fimc_lite_open(struct file *file)
{
struct fimc_lite *fimc = video_drvdata(file);
- struct media_entity *me = &fimc->vfd.entity;
+ struct media_entity *me = &fimc->ve.vdev.entity;
int ret;
- mutex_lock(&me->parent->graph_mutex);
-
mutex_lock(&fimc->lock);
if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) {
ret = -EBUSY;
@@ -505,11 +500,18 @@
atomic_read(&fimc->out_path) != FIMC_IO_DMA)
goto unlock;
- ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
- me, true);
+ mutex_lock(&me->parent->graph_mutex);
+
+ ret = fimc_pipeline_call(&fimc->ve, open, me, true);
+
+ /* Mark video pipeline ending at this video node as in use. */
+ if (ret == 0)
+ me->use_count++;
+
+ mutex_unlock(&me->parent->graph_mutex);
+
if (!ret) {
fimc_lite_clear_event_counters(fimc);
- fimc->ref_count++;
goto unlock;
}
@@ -519,26 +521,29 @@
clear_bit(ST_FLITE_IN_USE, &fimc->state);
unlock:
mutex_unlock(&fimc->lock);
- mutex_unlock(&me->parent->graph_mutex);
return ret;
}
static int fimc_lite_release(struct file *file)
{
struct fimc_lite *fimc = video_drvdata(file);
+ struct media_entity *entity = &fimc->ve.vdev.entity;
mutex_lock(&fimc->lock);
if (v4l2_fh_is_singular_file(file) &&
atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
if (fimc->streaming) {
- media_entity_pipeline_stop(&fimc->vfd.entity);
+ media_entity_pipeline_stop(entity);
fimc->streaming = false;
}
- clear_bit(ST_FLITE_IN_USE, &fimc->state);
fimc_lite_stop_capture(fimc, false);
- fimc_pipeline_call(fimc, close, &fimc->pipeline);
- fimc->ref_count--;
+ fimc_pipeline_call(&fimc->ve, close);
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
+
+ mutex_lock(&entity->parent->graph_mutex);
+ entity->use_count--;
+ mutex_unlock(&entity->parent->graph_mutex);
}
vb2_fop_release(file);
@@ -562,37 +567,54 @@
* Format and crop negotiation helpers
*/
-static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc,
- u32 *width, u32 *height,
- u32 *code, u32 *fourcc, int pad)
+static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
{
struct flite_drvdata *dd = fimc->dd;
- const struct fimc_fmt *fmt;
- unsigned int flags = 0;
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ const struct fimc_fmt *fmt = NULL;
- if (pad == FLITE_SD_PAD_SINK) {
- v4l_bound_align_image(width, 8, dd->max_width,
- ffs(dd->out_width_align) - 1,
- height, 0, dd->max_height, 0, 0);
+ if (format->pad == FLITE_SD_PAD_SINK) {
+ v4l_bound_align_image(&mf->width, 8, dd->max_width,
+ ffs(dd->out_width_align) - 1,
+ &mf->height, 0, dd->max_height, 0, 0);
+
+ fmt = fimc_lite_find_format(NULL, &mf->code, 0, 0);
+ if (WARN_ON(!fmt))
+ return NULL;
+
+ mf->colorspace = fmt->colorspace;
+ mf->code = fmt->mbus_code;
} else {
- v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width,
- ffs(dd->out_width_align) - 1,
- height, 0, fimc->inp_frame.rect.height,
- 0, 0);
- flags = fimc->inp_frame.fmt->flags;
+ struct flite_frame *sink = &fimc->inp_frame;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *rect;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sink_fmt = v4l2_subdev_get_try_format(fh,
+ FLITE_SD_PAD_SINK);
+
+ mf->code = sink_fmt->code;
+ mf->colorspace = sink_fmt->colorspace;
+
+ rect = v4l2_subdev_get_try_crop(fh,
+ FLITE_SD_PAD_SINK);
+ } else {
+ mf->code = sink->fmt->mbus_code;
+ mf->colorspace = sink->fmt->colorspace;
+ rect = &sink->rect;
+ }
+
+ /* Allow changing format only on sink pad */
+ mf->width = rect->width;
+ mf->height = rect->height;
}
- fmt = fimc_lite_find_format(fourcc, code, flags, 0);
- if (WARN_ON(!fmt))
- return NULL;
+ mf->field = V4L2_FIELD_NONE;
- if (code)
- *code = fmt->mbus_code;
- if (fourcc)
- *fourcc = fmt->fourcc;
-
- v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n",
- code ? *code : 0, *width, *height);
+ v4l2_dbg(1, debug, &fimc->subdev, "code: %#x (%d), %dx%d\n",
+ mf->code, mf->colorspace, mf->width, mf->height);
return fmt;
}
@@ -637,13 +659,18 @@
/*
* Video node ioctl operations
*/
-static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+static int fimc_lite_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
+ struct fimc_lite *fimc = video_drvdata(file);
+
strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver));
- cap->bus_info[0] = 0;
- cap->card[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING;
+ strlcpy(cap->card, FIMC_LITE_DRV_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(&fimc->pdev->dev));
+
+ cap->device_caps = V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -679,7 +706,7 @@
pixm->width = frame->f_width;
pixm->height = frame->f_height;
pixm->field = V4L2_FIELD_NONE;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->colorspace = fmt->colorspace;
return 0;
}
@@ -722,7 +749,7 @@
fmt->depth[0]) / 8;
pixm->num_planes = fmt->memplanes;
pixm->pixelformat = fmt->fourcc;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->colorspace = fmt->colorspace;
pixm->field = V4L2_FIELD_NONE;
return 0;
}
@@ -786,7 +813,7 @@
return -EPIPE;
}
/* Retrieve format at the source pad */
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -810,14 +837,13 @@
enum v4l2_buf_type type)
{
struct fimc_lite *fimc = video_drvdata(file);
- struct media_entity *entity = &fimc->vfd.entity;
- struct fimc_pipeline *p = &fimc->pipeline;
+ struct media_entity *entity = &fimc->ve.vdev.entity;
int ret;
if (fimc_lite_active(fimc))
return -EBUSY;
- ret = media_entity_pipeline_start(entity, p->m_pipeline);
+ ret = media_entity_pipeline_start(entity, &fimc->ve.pipe->mp);
if (ret < 0)
return ret;
@@ -825,7 +851,7 @@
if (ret < 0)
goto err_p_stop;
- fimc->sensor = __find_remote_sensor(&fimc->subdev.entity);
+ fimc->sensor = fimc_find_remote_sensor(&fimc->subdev.entity);
ret = vb2_ioctl_streamon(file, priv, type);
if (!ret) {
@@ -848,7 +874,7 @@
if (ret < 0)
return ret;
- media_entity_pipeline_stop(&fimc->vfd.entity);
+ media_entity_pipeline_stop(&fimc->ve.vdev.entity);
fimc->streaming = false;
return 0;
}
@@ -938,7 +964,7 @@
}
static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
- .vidioc_querycap = fimc_vidioc_querycap_capture,
+ .vidioc_querycap = fimc_lite_querycap,
.vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane,
.vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane,
@@ -972,8 +998,6 @@
__func__, remote->entity->name, local->entity->name,
flags, fimc->source_subdev_grp_id);
- mutex_lock(&fimc->lock);
-
switch (local->index) {
case FLITE_SD_PAD_SINK:
if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) {
@@ -1015,7 +1039,6 @@
}
mb();
- mutex_unlock(&fimc->lock);
return ret;
}
@@ -1036,6 +1059,15 @@
return 0;
}
+static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt(
+ struct v4l2_subdev_fh *fh, unsigned int pad)
+{
+ if (pad != FLITE_SD_PAD_SINK)
+ pad = FLITE_SD_PAD_SOURCE_DMA;
+
+ return v4l2_subdev_get_try_format(fh, pad);
+}
+
static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
@@ -1045,13 +1077,13 @@
struct flite_frame *f = &fimc->inp_frame;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad);
fmt->format = *mf;
return 0;
}
- mf->colorspace = V4L2_COLORSPACE_JPEG;
mutex_lock(&fimc->lock);
+ mf->colorspace = f->fmt->colorspace;
mf->code = f->fmt->mbus_code;
if (fmt->pad == FLITE_SD_PAD_SINK) {
@@ -1080,7 +1112,6 @@
v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n",
fmt->pad, mf->code, mf->width, mf->height);
- mf->colorspace = V4L2_COLORSPACE_JPEG;
mutex_lock(&fimc->lock);
if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP &&
@@ -1091,12 +1122,20 @@
return -EBUSY;
}
- ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height,
- &mf->code, NULL, fmt->pad);
+ ffmt = fimc_lite_subdev_try_fmt(fimc, fh, fmt);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad);
*mf = fmt->format;
+
+ if (fmt->pad == FLITE_SD_PAD_SINK) {
+ unsigned int pad = FLITE_SD_PAD_SOURCE_DMA;
+ src_fmt = __fimc_lite_subdev_get_try_fmt(fh, pad);
+ *src_fmt = *mf;
+ }
+
mutex_unlock(&fimc->lock);
return 0;
}
@@ -1114,11 +1153,6 @@
source->rect = sink->rect;
source->f_width = mf->width;
source->f_height = mf->height;
- } else {
- /* Allow changing format only on sink pad */
- mf->code = sink->fmt->mbus_code;
- mf->width = sink->rect.width;
- mf->height = sink->rect.height;
}
mutex_unlock(&fimc->lock);
@@ -1207,7 +1241,7 @@
* The pipeline links are protected through entity.stream_count
* so there is no need to take the media graph mutex here.
*/
- fimc->sensor = __find_remote_sensor(&sd->entity);
+ fimc->sensor = fimc_find_remote_sensor(&sd->entity);
if (atomic_read(&fimc->out_path) != FIMC_IO_ISP)
return -ENOIOCTLCMD;
@@ -1252,13 +1286,10 @@
{
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
struct vb2_queue *q = &fimc->vb_queue;
- struct video_device *vfd = &fimc->vfd;
+ struct video_device *vfd = &fimc->ve.vdev;
int ret;
memset(vfd, 0, sizeof(*vfd));
-
- fimc->inp_frame.fmt = &fimc_lite_formats[0];
- fimc->out_frame.fmt = &fimc_lite_formats[0];
atomic_set(&fimc->out_path, FIMC_IO_DMA);
snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture",
@@ -1295,12 +1326,12 @@
return ret;
video_set_drvdata(vfd, fimc);
- fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd);
+ fimc->ve.pipe = v4l2_get_subdev_hostdata(sd);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
media_entity_cleanup(&vfd->entity);
- fimc->pipeline_ops = NULL;
+ fimc->ve.pipe = NULL;
return ret;
}
@@ -1316,11 +1347,15 @@
if (fimc == NULL)
return;
- if (video_is_registered(&fimc->vfd)) {
- video_unregister_device(&fimc->vfd);
- media_entity_cleanup(&fimc->vfd.entity);
- fimc->pipeline_ops = NULL;
+ mutex_lock(&fimc->lock);
+
+ if (video_is_registered(&fimc->ve.vdev)) {
+ video_unregister_device(&fimc->ve.vdev);
+ media_entity_cleanup(&fimc->ve.vdev.entity);
+ fimc->ve.pipe = NULL;
}
+
+ mutex_unlock(&fimc->lock);
}
static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = {
@@ -1370,6 +1405,23 @@
.step = 1,
};
+static void fimc_lite_set_default_config(struct fimc_lite *fimc)
+{
+ struct flite_frame *sink = &fimc->inp_frame;
+ struct flite_frame *source = &fimc->out_frame;
+
+ sink->fmt = &fimc_lite_formats[0];
+ sink->f_width = FLITE_DEFAULT_WIDTH;
+ sink->f_height = FLITE_DEFAULT_HEIGHT;
+
+ sink->rect.width = FLITE_DEFAULT_WIDTH;
+ sink->rect.height = FLITE_DEFAULT_HEIGHT;
+ sink->rect.left = 0;
+ sink->rect.top = 0;
+
+ *source = *sink;
+}
+
static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
{
struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler;
@@ -1417,12 +1469,12 @@
static void fimc_lite_clk_put(struct fimc_lite *fimc)
{
- if (IS_ERR_OR_NULL(fimc->clock))
+ if (IS_ERR(fimc->clock))
return;
clk_unprepare(fimc->clock);
clk_put(fimc->clock);
- fimc->clock = NULL;
+ fimc->clock = ERR_PTR(-EINVAL);
}
static int fimc_lite_clk_get(struct fimc_lite *fimc)
@@ -1436,7 +1488,7 @@
ret = clk_prepare(fimc->clock);
if (ret < 0) {
clk_put(fimc->clock);
- fimc->clock = NULL;
+ fimc->clock = ERR_PTR(-EINVAL);
}
return ret;
}
@@ -1461,13 +1513,14 @@
if (of_id)
drv_data = (struct flite_drvdata *)of_id->data;
fimc->index = of_alias_get_id(dev->of_node, "fimc-lite");
- } else {
- drv_data = fimc_lite_get_drvdata(pdev);
- fimc->index = pdev->id;
}
- if (!drv_data || fimc->index < 0 || fimc->index >= FIMC_LITE_MAX_DEVS)
+ if (!drv_data || fimc->index >= drv_data->num_instances ||
+ fimc->index < 0) {
+ dev_err(dev, "Wrong %s node alias\n",
+ dev->of_node->full_name);
return -EINVAL;
+ }
fimc->dd = drv_data;
fimc->pdev = pdev;
@@ -1514,8 +1567,11 @@
ret = PTR_ERR(fimc->alloc_ctx);
goto err_pm;
}
+
pm_runtime_put(dev);
+ fimc_lite_set_default_config(fimc);
+
dev_dbg(dev, "FIMC-LITE.%d registered successfully\n",
fimc->index);
return 0;
@@ -1565,8 +1621,8 @@
return 0;
INIT_LIST_HEAD(&fimc->active_buf_q);
- fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &fimc->vfd.entity, false);
+ fimc_pipeline_call(&fimc->ve, open,
+ &fimc->ve.vdev.entity, false);
fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP);
clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
@@ -1592,7 +1648,7 @@
if (ret < 0 || !fimc_lite_active(fimc))
return ret;
- return fimc_pipeline_call(fimc, close, &fimc->pipeline);
+ return fimc_pipeline_call(&fimc->ve, close);
}
#endif /* CONFIG_PM_SLEEP */
@@ -1624,22 +1680,30 @@
.out_width_align = 8,
.win_hor_offs_align = 2,
.out_hor_offs_align = 8,
+ .max_dma_bufs = 1,
+ .num_instances = 2,
};
-static struct platform_device_id fimc_lite_driver_ids[] = {
- {
- .name = "exynos-fimc-lite",
- .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4,
- },
- { /* sentinel */ },
+/* EXYNOS5250 */
+static struct flite_drvdata fimc_lite_drvdata_exynos5 = {
+ .max_width = 8192,
+ .max_height = 8192,
+ .out_width_align = 8,
+ .win_hor_offs_align = 2,
+ .out_hor_offs_align = 8,
+ .max_dma_bufs = 32,
+ .num_instances = 3,
};
-MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids);
static const struct of_device_id flite_of_match[] = {
{
.compatible = "samsung,exynos4212-fimc-lite",
.data = &fimc_lite_drvdata_exynos4,
},
+ {
+ .compatible = "samsung,exynos5250-fimc-lite",
+ .data = &fimc_lite_drvdata_exynos5,
+ },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, flite_of_match);
@@ -1647,7 +1711,6 @@
static struct platform_driver fimc_lite_driver = {
.probe = fimc_lite_probe,
.remove = fimc_lite_remove,
- .id_table = fimc_lite_driver_ids,
.driver = {
.of_match_table = flite_of_match,
.name = FIMC_LITE_DRV_NAME,
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h
index 47da5e0..7428b2d 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.h
+++ b/drivers/media/platform/exynos4-is/fimc-lite.h
@@ -27,8 +27,10 @@
#define FIMC_LITE_DRV_NAME "exynos-fimc-lite"
#define FLITE_CLK_NAME "flite"
-#define FIMC_LITE_MAX_DEVS 2
+#define FIMC_LITE_MAX_DEVS 3
#define FLITE_REQ_BUFS_MIN 2
+#define FLITE_DEFAULT_WIDTH 640
+#define FLITE_DEFAULT_HEIGHT 480
/* Bit index definitions for struct fimc_lite::state */
enum {
@@ -48,17 +50,28 @@
#define FLITE_SD_PAD_SOURCE_ISP 2
#define FLITE_SD_PADS_NUM 3
+/**
+ * struct flite_drvdata - FIMC-LITE IP variant data structure
+ * @max_width: maximum camera interface input width in pixels
+ * @max_height: maximum camera interface input height in pixels
+ * @out_width_align: minimum output width alignment in pixels
+ * @win_hor_offs_align: minimum camera interface crop window horizontal
+ * offset alignment in pixels
+ * @out_hor_offs_align: minimum output DMA compose rectangle horizontal
+ * offset alignment in pixels
+ * @max_dma_bufs: number of output DMA buffer start address registers
+ * @num_instances: total number of FIMC-LITE IP instances available
+ */
struct flite_drvdata {
unsigned short max_width;
unsigned short max_height;
unsigned short out_width_align;
unsigned short win_hor_offs_align;
unsigned short out_hor_offs_align;
+ unsigned short max_dma_bufs;
+ unsigned short num_instances;
};
-#define fimc_lite_get_drvdata(_pdev) \
- ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data)
-
struct fimc_lite_events {
unsigned int data_overflow;
};
@@ -83,20 +96,22 @@
* struct flite_buffer - video buffer structure
* @vb: vb2 buffer
* @list: list head for the buffers queue
- * @paddr: precalculated physical address
+ * @paddr: DMA buffer start address
+ * @index: DMA start address register's index
*/
struct flite_buffer {
struct vb2_buffer vb;
struct list_head list;
dma_addr_t paddr;
+ unsigned short index;
};
/**
* struct fimc_lite - fimc lite structure
* @pdev: pointer to FIMC-LITE platform device
* @dd: SoC specific driver data structure
+ * @ve: exynos video device entity structure
* @v4l2_dev: pointer to top the level v4l2_device
- * @vfd: video device node
* @fh: v4l2 file handle
* @alloc_ctx: videobuf2 memory allocator context
* @subdev: FIMC-LITE subdev
@@ -122,16 +137,16 @@
* @pending_buf_q: pending buffers queue head
* @active_buf_q: the queue head of buffers scheduled in hardware
* @vb_queue: vb2 buffers queue
+ * @buf_index: helps to keep track of the DMA start address register index
* @active_buf_count: number of video buffers scheduled in hardware
* @frame_count: the captured frames counter
* @reqbufs_count: the number of buffers requested with REQBUFS ioctl
- * @ref_count: driver's private reference counter
*/
struct fimc_lite {
struct platform_device *pdev;
struct flite_drvdata *dd;
+ struct exynos_video_entity ve;
struct v4l2_device *v4l2_dev;
- struct video_device vfd;
struct v4l2_fh fh;
struct vb2_alloc_ctx *alloc_ctx;
struct v4l2_subdev subdev;
@@ -141,8 +156,6 @@
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *test_pattern;
int index;
- struct fimc_pipeline pipeline;
- const struct fimc_pipeline_ops *pipeline_ops;
struct mutex lock;
spinlock_t slock;
@@ -161,9 +174,9 @@
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vb_queue;
+ unsigned short buf_index;
unsigned int frame_count;
unsigned int reqbufs_count;
- int ref_count;
struct fimc_lite_events events;
bool streaming;
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index bde1f47..8d33b68 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -27,6 +27,7 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
+#include "common.h"
#include "fimc-core.h"
#include "fimc-reg.h"
#include "media-dev.h"
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c
index f079f36..1db8cb4 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-reg.c
@@ -618,7 +618,7 @@
}
if (i == ARRAY_SIZE(pix_desc)) {
- v4l2_err(&vc->vfd,
+ v4l2_err(&vc->ve.vdev,
"Camera color format not supported: %d\n",
vc->ci_fmt.code);
return -EINVAL;
@@ -698,7 +698,7 @@
cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
break;
default:
- v4l2_err(&vid_cap->vfd,
+ v4l2_err(&vid_cap->ve.vdev,
"Not supported camera pixel format: %#x\n",
vid_cap->ci_fmt.code);
return -EINVAL;
@@ -721,7 +721,8 @@
WARN_ONCE(1, "ISP Writeback input is not supported\n");
break;
default:
- v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n",
+ v4l2_err(&vid_cap->ve.vdev,
+ "Invalid FIMC bus type selected: %d\n",
source->fimc_bus_type);
return -EINVAL;
}
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 15ef8f2..19f556c 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -1,8 +1,8 @@
/*
* S5P/EXYNOS4 SoC series camera host interface media device driver
*
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@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
@@ -39,6 +39,26 @@
static int __fimc_md_set_camclk(struct fimc_md *fmd,
struct fimc_source_info *si,
bool on);
+
+/* Set up image sensor subdev -> FIMC capture node notifications. */
+static void __setup_sensor_notification(struct fimc_md *fmd,
+ struct v4l2_subdev *sensor,
+ struct v4l2_subdev *fimc_sd)
+{
+ struct fimc_source_info *src_inf;
+ struct fimc_sensor_info *md_si;
+ unsigned long flags;
+
+ src_inf = v4l2_get_subdev_hostdata(sensor);
+ if (!src_inf || WARN_ON(fmd == NULL))
+ return;
+
+ md_si = source_to_sensor_info(src_inf);
+ spin_lock_irqsave(&fmd->slock, flags);
+ md_si->host = v4l2_get_subdevdata(fimc_sd);
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
/**
* fimc_pipeline_prepare - update pipeline information with subdevice pointers
* @me: media entity terminating the pipeline
@@ -46,9 +66,11 @@
* Caller holds the graph mutex.
*/
static void fimc_pipeline_prepare(struct fimc_pipeline *p,
- struct media_entity *me)
+ struct media_entity *me)
{
+ struct fimc_md *fmd = entity_to_fimc_mdev(me);
struct v4l2_subdev *sd;
+ struct v4l2_subdev *sensor = NULL;
int i;
for (i = 0; i < IDX_MAX; i++)
@@ -62,7 +84,7 @@
struct media_pad *spad = &me->pads[i];
if (!(spad->flags & MEDIA_PAD_FL_SINK))
continue;
- pad = media_entity_remote_source(spad);
+ pad = media_entity_remote_pad(spad);
if (pad)
break;
}
@@ -73,8 +95,10 @@
sd = media_entity_to_v4l2_subdev(pad->entity);
switch (sd->grp_id) {
- case GRP_ID_FIMC_IS_SENSOR:
case GRP_ID_SENSOR:
+ sensor = sd;
+ /* fall through */
+ case GRP_ID_FIMC_IS_SENSOR:
p->subdevs[IDX_SENSOR] = sd;
break;
case GRP_ID_CSIS:
@@ -84,7 +108,7 @@
p->subdevs[IDX_FLITE] = sd;
break;
case GRP_ID_FIMC:
- /* No need to control FIMC subdev through subdev ops */
+ p->subdevs[IDX_FIMC] = sd;
break;
case GRP_ID_FIMC_IS:
p->subdevs[IDX_IS_ISP] = sd;
@@ -96,6 +120,9 @@
if (me->num_pads == 1)
break;
}
+
+ if (sensor && p->subdevs[IDX_FIMC])
+ __setup_sensor_notification(fmd, sensor, p->subdevs[IDX_FIMC]);
}
/**
@@ -168,10 +195,11 @@
*
* Called with the graph mutex held.
*/
-static int __fimc_pipeline_open(struct fimc_pipeline *p,
+static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
struct media_entity *me, bool prepare)
{
struct fimc_md *fmd = entity_to_fimc_mdev(me);
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
struct v4l2_subdev *sd;
int ret;
@@ -214,20 +242,21 @@
*
* Disable power of all subdevs and turn the external sensor clock off.
*/
-static int __fimc_pipeline_close(struct fimc_pipeline *p)
+static int __fimc_pipeline_close(struct exynos_media_pipeline *ep)
{
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL;
struct fimc_md *fmd;
- int ret = 0;
+ int ret;
- if (WARN_ON(sd == NULL))
- return -EINVAL;
-
- if (p->subdevs[IDX_SENSOR]) {
- ret = fimc_pipeline_s_power(p, 0);
- fimc_md_set_camclk(sd, false);
+ if (sd == NULL) {
+ pr_warn("%s(): No sensor subdev\n", __func__);
+ return 0;
}
+ ret = fimc_pipeline_s_power(p, 0);
+ fimc_md_set_camclk(sd, false);
+
fmd = entity_to_fimc_mdev(&sd->entity);
/* Disable PXLASYNC clock if this pipeline includes FIMC-IS */
@@ -242,12 +271,13 @@
* @pipeline: video pipeline structure
* @on: passed as the s_stream() callback argument
*/
-static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
+static int __fimc_pipeline_s_stream(struct exynos_media_pipeline *ep, bool on)
{
static const u8 seq[2][IDX_MAX] = {
{ IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE },
{ IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP },
};
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
int i, ret = 0;
if (p->subdevs[IDX_SENSOR] == NULL)
@@ -271,12 +301,38 @@
}
/* Media pipeline operations for the FIMC/FIMC-LITE video device driver */
-static const struct fimc_pipeline_ops fimc_pipeline_ops = {
+static const struct exynos_media_pipeline_ops fimc_pipeline_ops = {
.open = __fimc_pipeline_open,
.close = __fimc_pipeline_close,
.set_stream = __fimc_pipeline_s_stream,
};
+static struct exynos_media_pipeline *fimc_md_pipeline_create(
+ struct fimc_md *fmd)
+{
+ struct fimc_pipeline *p;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ list_add_tail(&p->list, &fmd->pipelines);
+
+ p->ep.ops = &fimc_pipeline_ops;
+ return &p->ep;
+}
+
+static void fimc_md_pipelines_free(struct fimc_md *fmd)
+{
+ while (!list_empty(&fmd->pipelines)) {
+ struct fimc_pipeline *p;
+
+ p = list_entry(fmd->pipelines.next, typeof(*p), list);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
/*
* Sensor subdevice helper functions
*/
@@ -592,6 +648,7 @@
struct fimc_lite *fimc_lite)
{
struct v4l2_subdev *sd;
+ struct exynos_media_pipeline *ep;
int ret;
if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS ||
@@ -600,7 +657,12 @@
sd = &fimc_lite->subdev;
sd->grp_id = GRP_ID_FLITE;
- v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
+
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
if (!ret)
@@ -614,6 +676,7 @@
static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc)
{
struct v4l2_subdev *sd;
+ struct exynos_media_pipeline *ep;
int ret;
if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id]))
@@ -621,7 +684,12 @@
sd = &fimc->vid_cap.subdev;
sd->grp_id = GRP_ID_FIMC;
- v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
+
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
if (!ret) {
@@ -736,8 +804,6 @@
if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) {
plat_entity = IDX_CSIS;
- } else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) {
- plat_entity = IDX_FLITE;
} else {
p = strstr(pdev->name, "fimc");
if (p && *(p + 4) == 0)
@@ -797,17 +863,19 @@
int i;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (fmd->fimc[i] == NULL)
+ struct fimc_dev *dev = fmd->fimc[i];
+ if (dev == NULL)
continue;
- v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev);
- fmd->fimc[i]->pipeline_ops = NULL;
+ v4l2_device_unregister_subdev(&dev->vid_cap.subdev);
+ dev->vid_cap.ve.pipe = NULL;
fmd->fimc[i] = NULL;
}
for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- if (fmd->fimc_lite[i] == NULL)
+ struct fimc_lite *dev = fmd->fimc_lite[i];
+ if (dev == NULL)
continue;
- v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev);
- fmd->fimc_lite[i]->pipeline_ops = NULL;
+ v4l2_device_unregister_subdev(&dev->subdev);
+ dev->ve.pipe = NULL;
fmd->fimc_lite[i] = NULL;
}
for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
@@ -880,18 +948,6 @@
v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n",
source->name, flags ? '=' : '-', sink->name);
-
- if (flags == 0 || sensor == NULL)
- continue;
-
- if (!WARN_ON(si == NULL)) {
- unsigned long irq_flags;
- struct fimc_sensor_info *inf = source_to_sensor_info(si);
-
- spin_lock_irqsave(&fmd->slock, irq_flags);
- inf->host = fmd->fimc[i];
- spin_unlock_irqrestore(&fmd->slock, irq_flags);
- }
}
for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
@@ -929,7 +985,7 @@
continue;
source = &fimc->subdev.entity;
- sink = &fimc->vfd.entity;
+ sink = &fimc->ve.vdev.entity;
/* FIMC-LITE's subdev and video node */
ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA,
sink, 0, 0);
@@ -1066,7 +1122,7 @@
continue;
source = &fmd->fimc[i]->vid_cap.subdev.entity;
- sink = &fmd->fimc[i]->vid_cap.vfd.entity;
+ sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity;
ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
sink, 0, flags);
@@ -1231,66 +1287,98 @@
return __fimc_md_set_camclk(fmd, si, on);
}
-static int fimc_md_link_notify(struct media_pad *source,
- struct media_pad *sink, u32 flags)
+static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable)
{
- struct fimc_lite *fimc_lite = NULL;
- struct fimc_dev *fimc = NULL;
- struct fimc_pipeline *pipeline;
- struct v4l2_subdev *sd;
- struct mutex *lock;
- int i, ret = 0;
- int ref_count;
+ struct exynos_video_entity *ve;
+ struct fimc_pipeline *p;
+ struct video_device *vdev;
+ int ret;
- if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ vdev = media_entity_to_video_device(entity);
+ if (vdev->entity.use_count == 0)
return 0;
- sd = media_entity_to_v4l2_subdev(sink->entity);
-
- switch (sd->grp_id) {
- case GRP_ID_FLITE:
- fimc_lite = v4l2_get_subdevdata(sd);
- if (WARN_ON(fimc_lite == NULL))
- return 0;
- pipeline = &fimc_lite->pipeline;
- lock = &fimc_lite->lock;
- break;
- case GRP_ID_FIMC:
- fimc = v4l2_get_subdevdata(sd);
- if (WARN_ON(fimc == NULL))
- return 0;
- pipeline = &fimc->pipeline;
- lock = &fimc->lock;
- break;
- default:
+ ve = vdev_to_exynos_video_entity(vdev);
+ p = to_fimc_pipeline(ve->pipe);
+ /*
+ * Nothing to do if we are disabling the pipeline, some link
+ * has been disconnected and p->subdevs array is cleared now.
+ */
+ if (!enable && p->subdevs[IDX_SENSOR] == NULL)
return 0;
+
+ if (enable)
+ ret = __fimc_pipeline_open(ve->pipe, entity, true);
+ else
+ ret = __fimc_pipeline_close(ve->pipe);
+
+ if (ret == 0 && !enable)
+ memset(p->subdevs, 0, sizeof(p->subdevs));
+
+ return ret;
+}
+
+/* Locking: called with entity->parent->graph_mutex mutex held. */
+static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
+{
+ struct media_entity *entity_err = entity;
+ struct media_entity_graph graph;
+ int ret;
+
+ /*
+ * Walk current graph and call the pipeline open/close routine for each
+ * opened video node that belongs to the graph of entities connected
+ * through active links. This is needed as we cannot power on/off the
+ * subdevs in random order.
+ */
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ ret = __fimc_md_modify_pipeline(entity, enable);
+
+ if (ret < 0)
+ goto err;
}
- mutex_lock(lock);
- ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count;
+ return 0;
+ err:
+ media_entity_graph_walk_start(&graph, entity_err);
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- if (ref_count > 0) {
- ret = __fimc_pipeline_close(pipeline);
- if (!ret && fimc)
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- }
- for (i = 0; i < IDX_MAX; i++)
- pipeline->subdevs[i] = NULL;
- } else if (ref_count > 0) {
- /*
- * Link activation. Enable power of pipeline elements only if
- * the pipeline is already in use, i.e. its video node is open.
- * Recreate the controls destroyed during the link deactivation.
- */
- ret = __fimc_pipeline_open(pipeline,
- source->entity, true);
- if (!ret && fimc)
- ret = fimc_capture_ctrls_create(fimc);
+ while ((entity_err = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ __fimc_md_modify_pipeline(entity_err, !enable);
+
+ if (entity_err == entity)
+ break;
}
- mutex_unlock(lock);
- return ret ? -EPIPE : ret;
+ return ret;
+}
+
+static int fimc_md_link_notify(struct media_link *link, unsigned int flags,
+ unsigned int notification)
+{
+ struct media_entity *sink = link->sink->entity;
+ int ret = 0;
+
+ /* Before link disconnection */
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
+ if (!(flags & MEDIA_LNK_FL_ENABLED))
+ ret = __fimc_md_modify_pipelines(sink, false);
+ else
+ ; /* TODO: Link state change validation */
+ /* After link activation */
+ } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ (link->flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_md_modify_pipelines(sink, true);
+ }
+
+ return ret ? -EPIPE : 0;
}
static ssize_t fimc_md_sysfs_show(struct device *dev,
@@ -1370,6 +1458,7 @@
spin_lock_init(&fmd->slock);
fmd->pdev = pdev;
+ INIT_LIST_HEAD(&fmd->pipelines);
strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
sizeof(fmd->media_dev.model));
@@ -1457,6 +1546,7 @@
return 0;
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
+ fimc_md_pipelines_free(fmd);
media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
return 0;
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 44d86b6..62599fd 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -18,6 +18,7 @@
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
+#include <media/s5p_fimc.h>
#include "fimc-core.h"
#include "fimc-lite.h"
@@ -40,6 +41,29 @@
FIMC_MAX_WBCLKS
};
+enum fimc_subdev_index {
+ IDX_SENSOR,
+ IDX_CSIS,
+ IDX_FLITE,
+ IDX_IS_ISP,
+ IDX_FIMC,
+ IDX_MAX,
+};
+
+/*
+ * This structure represents a chain of media entities, including a data
+ * source entity (e.g. an image sensor subdevice), a data capture entity
+ * - a video capture device node and any remaining entities.
+ */
+struct fimc_pipeline {
+ struct exynos_media_pipeline ep;
+ struct list_head list;
+ struct media_entity *vdev_entity;
+ struct v4l2_subdev *subdevs[IDX_MAX];
+};
+
+#define to_fimc_pipeline(_ep) container_of(_ep, struct fimc_pipeline, ep)
+
struct fimc_csis_info {
struct v4l2_subdev *sd;
int id;
@@ -104,17 +128,11 @@
struct pinctrl_state *state_idle;
} pinctl;
bool user_subdev_api;
+
spinlock_t slock;
+ struct list_head pipelines;
};
-#define is_subdev_pad(pad) (pad == NULL || \
- media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
-
-#define me_subtype(me) \
- ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
-
-#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
-
static inline
struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si)
{
@@ -127,14 +145,14 @@
container_of(me->parent, struct fimc_md, media_dev);
}
-static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
+static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
{
- mutex_lock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
+ mutex_lock(&ve->vdev.entity.parent->graph_mutex);
}
-static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
+static inline void fimc_md_graph_unlock(struct exynos_video_entity *ve)
{
- mutex_unlock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
+ mutex_unlock(&ve->vdev.entity.parent->graph_mutex);
}
int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
@@ -149,4 +167,16 @@
#define fimc_md_is_isp_available(node) (false)
#endif /* CONFIG_OF */
+static inline struct v4l2_subdev *__fimc_md_get_subdev(
+ struct exynos_media_pipeline *ep,
+ unsigned int index)
+{
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
+
+ if (!p || index >= IDX_MAX)
+ return NULL;
+ else
+ return p->subdevs[index];
+}
+
#endif
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 254d70f..0914230 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -1,8 +1,8 @@
/*
- * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+ * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
*
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.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
@@ -66,11 +66,12 @@
/* Interrupt mask */
#define S5PCSIS_INTMSK 0x10
-#define S5PCSIS_INTMSK_EN_ALL 0xf000103f
#define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31)
#define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30)
#define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29)
#define S5PCSIS_INTMSK_ODD_AFTER (1 << 28)
+#define S5PCSIS_INTMSK_FRAME_START (1 << 27)
+#define S5PCSIS_INTMSK_FRAME_END (1 << 26)
#define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12)
#define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5)
#define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4)
@@ -78,6 +79,8 @@
#define S5PCSIS_INTMSK_ERR_ECC (1 << 2)
#define S5PCSIS_INTMSK_ERR_CRC (1 << 1)
#define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0)
+#define S5PCSIS_INTMSK_EXYNOS4_EN_ALL 0xf000103f
+#define S5PCSIS_INTMSK_EXYNOS5_EN_ALL 0xfc00103f
/* Interrupt source */
#define S5PCSIS_INTSRC 0x14
@@ -88,6 +91,8 @@
#define S5PCSIS_INTSRC_ODD_AFTER (1 << 28)
#define S5PCSIS_INTSRC_ODD (0x3 << 28)
#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28)
+#define S5PCSIS_INTSRC_FRAME_START (1 << 27)
+#define S5PCSIS_INTSRC_FRAME_END (1 << 26)
#define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12)
#define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5)
#define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4)
@@ -151,6 +156,9 @@
{ S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
{ S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
{ S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
+ /* Frame start/end */
+ { S5PCSIS_INTSRC_FRAME_START, "Frame Start" },
+ { S5PCSIS_INTSRC_FRAME_END, "Frame End" },
};
#define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
@@ -159,6 +167,11 @@
unsigned int len;
};
+struct csis_drvdata {
+ /* Mask of all used interrupts in S5PCSIS_INTMSK register */
+ u32 interrupt_mask;
+};
+
/**
* struct csis_state - the driver's internal state data structure
* @lock: mutex serializing the subdev and power management operations,
@@ -171,6 +184,7 @@
* @supplies: CSIS regulator supplies
* @clock: CSIS clocks
* @irq: requested s5p-mipi-csis irq number
+ * @interrupt_mask: interrupt mask of the all used interrupts
* @flags: the state variable for power and streaming control
* @clock_frequency: device bus clock frequency
* @hs_settle: HS-RX settle time
@@ -193,6 +207,7 @@
struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
int irq;
+ u32 interrupt_mask;
u32 flags;
u32 clk_frequency;
@@ -274,9 +289,10 @@
static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
{
u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
-
- val = on ? val | S5PCSIS_INTMSK_EN_ALL :
- val & ~S5PCSIS_INTMSK_EN_ALL;
+ if (on)
+ val |= state->interrupt_mask;
+ else
+ val &= ~state->interrupt_mask;
s5pcsis_write(state, S5PCSIS_INTMSK, val);
}
@@ -771,8 +787,12 @@
#define s5pcsis_parse_dt(pdev, state) (-ENOSYS)
#endif
+static const struct of_device_id s5pcsis_of_match[];
+
static int s5pcsis_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id;
+ const struct csis_drvdata *drv_data;
struct device *dev = &pdev->dev;
struct resource *mem_res;
struct csis_state *state;
@@ -787,10 +807,19 @@
spin_lock_init(&state->slock);
state->pdev = pdev;
- if (dev->of_node)
+ if (dev->of_node) {
+ of_id = of_match_node(s5pcsis_of_match, dev->of_node);
+ if (WARN_ON(of_id == NULL))
+ return -EINVAL;
+
+ drv_data = of_id->data;
+ state->interrupt_mask = drv_data->interrupt_mask;
+
ret = s5pcsis_parse_dt(pdev, state);
- else
+ } else {
ret = s5pcsis_get_platform_data(pdev, state);
+ }
+
if (ret < 0)
return ret;
@@ -994,9 +1023,25 @@
SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
};
+static const struct csis_drvdata exynos4_csis_drvdata = {
+ .interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL,
+};
+
+static const struct csis_drvdata exynos5_csis_drvdata = {
+ .interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL,
+};
+
static const struct of_device_id s5pcsis_of_match[] = {
- { .compatible = "samsung,s5pv210-csis" },
- { .compatible = "samsung,exynos4210-csis" },
+ {
+ .compatible = "samsung,s5pv210-csis",
+ .data = &exynos4_csis_drvdata,
+ }, {
+ .compatible = "samsung,exynos4210-csis",
+ .data = &exynos4_csis_drvdata,
+ }, {
+ .compatible = "samsung,exynos5250-csis",
+ .data = &exynos5_csis_drvdata,
+ },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, s5pcsis_of_match);
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 3a6a0dc..221ec42 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -1475,7 +1475,6 @@
.release = video_device_release,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
- .current_norm = V4L2_STD_NTSC_M,
};
static int viu_of_probe(struct platform_device *op)
@@ -1546,6 +1545,7 @@
viu_dev->vidq.timeout.function = viu_vid_timeout;
viu_dev->vidq.timeout.data = (unsigned long)viu_dev;
init_timer(&viu_dev->vidq.timeout);
+ viu_dev->std = V4L2_STD_NTSC_M;
viu_dev->first = 1;
/* Allocate memory for video device */
diff --git a/drivers/media/platform/indycam.c b/drivers/media/platform/indycam.c
index 5482363..f1d192b 100644
--- a/drivers/media/platform/indycam.c
+++ b/drivers/media/platform/indycam.c
@@ -23,7 +23,6 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include "indycam.h"
@@ -283,20 +282,9 @@
/* I2C-interface */
-static int indycam_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct indycam *camera = to_indycam(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM,
- camera->version);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops indycam_core_ops = {
- .g_chip_ident = indycam_g_chip_ident,
.g_ctrl = indycam_g_ctrl,
.s_ctrl = indycam_s_ctrl,
};
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 7585646..540516c 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -1033,6 +1033,7 @@
*vfd = deinterlace_videodev;
vfd->lock = &pcdev->dev_mutex;
+ vfd->v4l2_dev = &pcdev->v4l2_dev;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index d030f9b..1f079ff 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
@@ -27,7 +27,6 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/delay.h>
@@ -469,7 +468,7 @@
goto out;
cam->pdev = pdev;
mcam = &cam->mcam;
- mcam->chip_id = V4L2_IDENT_CAFE;
+ mcam->chip_id = MCAM_CAFE;
spin_lock_init(&mcam->dev_lock);
init_waitqueue_head(&cam->smbus_wait);
mcam->plat_power_up = cafe_ctlr_power_up;
@@ -501,6 +500,7 @@
printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
goto out_disable;
}
+ mcam->regs_size = pci_resource_len(pdev, 0);
ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
if (ret)
goto out_iounmap;
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 64ab91e..0821ed0 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -23,7 +23,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <media/ov7670.h>
#include <media/videobuf2-vmalloc.h>
#include <media/videobuf2-dma-contig.h>
@@ -336,7 +335,7 @@
mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
} else
mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
- if (cam->chip_id == V4L2_IDENT_CAFE)
+ if (cam->chip_id == MCAM_CAFE)
mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
}
@@ -796,7 +795,6 @@
*/
static int mcam_cam_init(struct mcam_camera *cam)
{
- struct v4l2_dbg_chip_ident chip;
int ret;
mutex_lock(&cam->s_mutex);
@@ -804,24 +802,8 @@
cam_warn(cam, "Cam init with device in funky state %d",
cam->state);
ret = __mcam_cam_reset(cam);
- if (ret)
- goto out;
- chip.ident = V4L2_IDENT_NONE;
- chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
- chip.match.addr = cam->sensor_addr;
- ret = sensor_call(cam, core, g_chip_ident, &chip);
- if (ret)
- goto out;
- cam->sensor_type = chip.ident;
- if (cam->sensor_type != V4L2_IDENT_OV7670) {
- cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
- ret = -EINVAL;
- goto out;
- }
-/* Get/set parameters? */
- ret = 0;
+ /* Get/set parameters? */
cam->state = S_IDLE;
-out:
mcam_ctlr_power_down(cam);
mutex_unlock(&cam->s_mutex);
return ret;
@@ -1362,6 +1344,12 @@
return 0;
}
+static int mcam_vidioc_g_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+ *a = V4L2_STD_NTSC_M;
+ return 0;
+}
+
/*
* G/S_PARM. Most of this is done by the sensor, but we are
* the level which controls the number of read buffers.
@@ -1392,20 +1380,6 @@
return ret;
}
-static int mcam_vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct mcam_camera *cam = priv;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = cam->chip_id;
- return 0;
- }
- return sensor_call(cam, core, g_chip_ident, chip);
-}
-
static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv,
struct v4l2_frmsizeenum *sizes)
{
@@ -1436,12 +1410,11 @@
{
struct mcam_camera *cam = priv;
- if (v4l2_chip_match_host(®->match)) {
- reg->val = mcam_reg_read(cam, reg->reg);
- reg->size = 4;
- return 0;
- }
- return sensor_call(cam, core, g_register, reg);
+ if (reg->reg > cam->regs_size - 4)
+ return -EINVAL;
+ reg->val = mcam_reg_read(cam, reg->reg);
+ reg->size = 4;
+ return 0;
}
static int mcam_vidioc_s_register(struct file *file, void *priv,
@@ -1449,11 +1422,10 @@
{
struct mcam_camera *cam = priv;
- if (v4l2_chip_match_host(®->match)) {
- mcam_reg_write(cam, reg->reg, reg->val);
- return 0;
- }
- return sensor_call(cam, core, s_register, reg);
+ if (reg->reg > cam->regs_size - 4)
+ return -EINVAL;
+ mcam_reg_write(cam, reg->reg, reg->val);
+ return 0;
}
#endif
@@ -1467,6 +1439,7 @@
.vidioc_g_input = mcam_vidioc_g_input,
.vidioc_s_input = mcam_vidioc_s_input,
.vidioc_s_std = mcam_vidioc_s_std,
+ .vidioc_g_std = mcam_vidioc_g_std,
.vidioc_reqbufs = mcam_vidioc_reqbufs,
.vidioc_querybuf = mcam_vidioc_querybuf,
.vidioc_qbuf = mcam_vidioc_qbuf,
@@ -1477,7 +1450,6 @@
.vidioc_s_parm = mcam_vidioc_s_parm,
.vidioc_enum_framesizes = mcam_vidioc_enum_framesizes,
.vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals,
- .vidioc_g_chip_ident = mcam_vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = mcam_vidioc_g_register,
.vidioc_s_register = mcam_vidioc_s_register,
@@ -1593,7 +1565,6 @@
static struct video_device mcam_v4l_template = {
.name = "mcam",
.tvnorms = V4L2_STD_NTSC_M,
- .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
.fops = &mcam_v4l_fops,
.ioctl_ops = &mcam_v4l_ioctl_ops,
@@ -1695,7 +1666,7 @@
if (buffer_mode >= 0)
cam->buffer_mode = buffer_mode;
if (cam->buffer_mode == B_DMA_sg &&
- cam->chip_id == V4L2_IDENT_CAFE) {
+ cam->chip_id == MCAM_CAFE) {
printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, "
"attempting vmalloc mode instead\n");
cam->buffer_mode = B_vmalloc;
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index 01dec9e..520c8de 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -53,6 +53,11 @@
B_DMA_sg = 2
};
+enum mcam_chip_id {
+ MCAM_CAFE,
+ MCAM_ARMADA610,
+};
+
/*
* Is a given buffer mode supported by the current kernel configuration?
*/
@@ -96,9 +101,10 @@
*/
struct i2c_adapter *i2c_adapter;
unsigned char __iomem *regs;
+ unsigned regs_size; /* size in bytes of the register space */
spinlock_t dev_lock;
struct device *dev; /* For messages, dma alloc */
- unsigned int chip_id;
+ enum mcam_chip_id chip_id;
short int clock_speed; /* Sensor clock speed, default 30 */
short int use_smbus; /* SMBUS or straight I2c? */
enum mcam_buffer_mode buffer_mode;
@@ -152,7 +158,6 @@
void (*frame_complete)(struct mcam_camera *cam, int frame);
/* Current operating parameters */
- u32 sensor_type; /* Currently ov7670 only */
struct v4l2_pix_format pix_format;
enum v4l2_mbus_pixelcode mbus_code;
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index c4c17fe..a634888 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/mmp-camera.h>
#include <linux/device.h>
#include <linux/platform_device.h>
@@ -185,7 +184,7 @@
mcam->plat_power_down = mmpcam_power_down;
mcam->dev = &pdev->dev;
mcam->use_smbus = 0;
- mcam->chip_id = V4L2_IDENT_ARMADA610;
+ mcam->chip_id = MCAM_ARMADA610;
mcam->buffer_mode = B_DMA_sg;
spin_lock_init(&mcam->dev_lock);
/*
@@ -203,6 +202,7 @@
ret = -ENODEV;
goto out_free;
}
+ mcam->regs_size = resource_size(res);
/*
* Power/clock memory is elsewhere; get it too. Perhaps this
* should really be managed outside of this driver?
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c
index 4cc7f65d..6a17676 100644
--- a/drivers/media/platform/mem2mem_testdev.c
+++ b/drivers/media/platform/mem2mem_testdev.c
@@ -1051,6 +1051,7 @@
*vfd = m2mtest_videodev;
vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
@@ -1061,7 +1062,7 @@
video_set_drvdata(vfd, dev);
snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
dev->vfd = vfd;
- v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
+ v4l2_info(&dev->v4l2_dev,
"Device registered as /dev/video%d\n", vfd->num);
setup_timer(&dev->timer, device_isr, (long)dev);
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index f7440e5..c690435 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -937,6 +937,7 @@
*vfd = emmaprp_videodev;
vfd->lock = &pcdev->dev_mutex;
+ vfd->v4l2_dev = &pcdev->v4l2_dev;
video_set_drvdata(vfd, pcdev);
snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index d338b19..dfd0a21 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -335,8 +335,6 @@
ovl = ovid->overlays[0];
switch (pix->pixelformat) {
- case 0:
- break;
case V4L2_PIX_FMT_YUYV:
mode = OMAP_DSS_COLOR_YUV2;
break;
@@ -358,6 +356,7 @@
break;
default:
mode = -EINVAL;
+ break;
}
return mode;
}
diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c
index debb44c..d2b440c 100644
--- a/drivers/media/platform/omap24xxcam.c
+++ b/drivers/media/platform/omap24xxcam.c
@@ -1656,7 +1656,7 @@
}
vfd->release = video_device_release;
- vfd->parent = cam->dev;
+ vfd->v4l2_dev = &cam->v4l2_dev;
strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
vfd->fops = &omap24xxcam_fops;
@@ -1752,6 +1752,11 @@
cam->dev = &pdev->dev;
+ if (v4l2_device_register(&pdev->dev, &cam->v4l2_dev)) {
+ dev_err(&pdev->dev, "v4l2_device_register failed\n");
+ goto err;
+ }
+
/*
* Impose a lower limit on the amount of memory allocated for
* capture. We require at least enough memory to double-buffer
@@ -1849,6 +1854,8 @@
cam->mmio_base_phys = 0;
}
+ v4l2_device_unregister(&cam->v4l2_dev);
+
kfree(cam);
return 0;
diff --git a/drivers/media/platform/omap24xxcam.h b/drivers/media/platform/omap24xxcam.h
index c439595..7f6f791 100644
--- a/drivers/media/platform/omap24xxcam.h
+++ b/drivers/media/platform/omap24xxcam.h
@@ -29,6 +29,7 @@
#include <media/videobuf-dma-sg.h>
#include <media/v4l2-int-device.h>
+#include <media/v4l2-device.h>
/*
*
@@ -462,6 +463,8 @@
*/
struct mutex mutex;
+ struct v4l2_device v4l2_dev;
+
/*** general driver state information ***/
atomic_t users;
/*
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 1d7dbd5..df3a0ec 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -792,9 +792,9 @@
/*
* isp_pipeline_link_notify - Link management notification callback
- * @source: Pad at the start of the link
- * @sink: Pad at the end of the link
+ * @link: The link
* @flags: New link flags that will be applied
+ * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*)
*
* React to link management on powered pipelines by updating the use count of
* all entities in the source and sink sides of the link. Entities are powered
@@ -804,29 +804,38 @@
* off is assumed to never fail. This function will not fail for disconnection
* events.
*/
-static int isp_pipeline_link_notify(struct media_pad *source,
- struct media_pad *sink, u32 flags)
+static int isp_pipeline_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
{
- int source_use = isp_pipeline_pm_use_count(source->entity);
- int sink_use = isp_pipeline_pm_use_count(sink->entity);
+ struct media_entity *source = link->source->entity;
+ struct media_entity *sink = link->sink->entity;
+ int source_use = isp_pipeline_pm_use_count(source);
+ int sink_use = isp_pipeline_pm_use_count(sink);
int ret;
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ !(link->flags & MEDIA_LNK_FL_ENABLED)) {
/* Powering off entities is assumed to never fail. */
- isp_pipeline_pm_power(source->entity, -sink_use);
- isp_pipeline_pm_power(sink->entity, -source_use);
+ isp_pipeline_pm_power(source, -sink_use);
+ isp_pipeline_pm_power(sink, -source_use);
return 0;
}
- ret = isp_pipeline_pm_power(source->entity, sink_use);
- if (ret < 0)
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ (flags & MEDIA_LNK_FL_ENABLED)) {
+
+ ret = isp_pipeline_pm_power(source, sink_use);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_pipeline_pm_power(sink, source_use);
+ if (ret < 0)
+ isp_pipeline_pm_power(source, -sink_use);
+
return ret;
+ }
- ret = isp_pipeline_pm_power(sink->entity, source_use);
- if (ret < 0)
- isp_pipeline_pm_power(source->entity, -sink_use);
-
- return ret;
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -877,7 +886,7 @@
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -967,7 +976,7 @@
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -1083,7 +1092,7 @@
pipe = to_isp_pipeline(me);
if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
return 0;
- pad = media_entity_remote_source(&pipe->output->pad);
+ pad = media_entity_remote_pad(&pipe->output->pad);
return pad->entity == me;
}
@@ -2249,6 +2258,7 @@
ret = iommu_attach_device(isp->domain, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+ ret = -EPROBE_DEFER;
goto free_domain;
}
@@ -2287,12 +2297,11 @@
iommu_detach_device(isp->domain, &pdev->dev);
free_domain:
iommu_domain_free(isp->domain);
+ isp->domain = NULL;
error_isp:
isp_xclk_cleanup(isp);
omap3isp_put(isp);
error:
- platform_set_drvdata(pdev, NULL);
-
mutex_destroy(&isp->isp_mutex);
return ret;
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 60e60aa..907a205 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -1120,7 +1120,7 @@
u32 syn_mode;
u32 ccdc_pattern;
- pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
+ pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
if (ccdc->input == CCDC_INPUT_PARALLEL)
pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index c5d84c9..e716514 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -158,13 +158,17 @@
* @ccp2: pointer to ISP CCP2 device
* @enable: enable/disable flag
*/
-static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
+static int ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
{
struct isp_device *isp = to_isp_device(ccp2);
+ int ret;
int i;
- if (enable && ccp2->vdds_csib)
- regulator_enable(ccp2->vdds_csib);
+ if (enable && ccp2->vdds_csib) {
+ ret = regulator_enable(ccp2->vdds_csib);
+ if (ret < 0)
+ return ret;
+ }
/* Enable/Disable all the LCx channels */
for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
@@ -179,6 +183,8 @@
if (!enable && ccp2->vdds_csib)
regulator_disable(ccp2->vdds_csib);
+
+ return 0;
}
/*
@@ -360,7 +366,7 @@
ccp2_pwr_cfg(ccp2);
- pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]);
+ pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
pdata = sensor->host_priv;
@@ -851,7 +857,12 @@
ccp2_print_status(ccp2);
/* Enable CSI1/CCP2 interface */
- ccp2_if_enable(ccp2, 1);
+ ret = ccp2_if_enable(ccp2, 1);
+ if (ret < 0) {
+ if (ccp2->phy)
+ omap3isp_csiphy_release(ccp2->phy);
+ return ret;
+ }
break;
case ISP_PIPELINE_STREAM_SINGLESHOT:
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 783f4b0..6db245d 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -573,7 +573,7 @@
if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
return -EBUSY;
- pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
+ pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
pdata = sensor->host_priv;
diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h
index 908dfd7..3e048ad 100644
--- a/drivers/media/platform/omap3isp/ispqueue.h
+++ b/drivers/media/platform/omap3isp/ispqueue.h
@@ -28,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/mm_types.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/wait.h>
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index 8dac175..a908d00 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -219,7 +219,7 @@
{
struct media_pad *remote;
- remote = media_entity_remote_source(&video->pad);
+ remote = media_entity_remote_pad(&video->pad);
if (remote == NULL ||
media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
@@ -314,7 +314,7 @@
* entity can be found, and stop checking the pipeline if the
* source entity isn't a subdev.
*/
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL)
return -EPIPE;
@@ -901,7 +901,7 @@
continue;
/* ISP entities have always sink pad == 0. Find source. */
- source_pad = media_entity_remote_source(&ents[i]->pads[0]);
+ source_pad = media_entity_remote_pad(&ents[i]->pads[0]);
if (source_pad == NULL)
continue;
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 70438a0..40b298a 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -845,7 +845,7 @@
int ret;
/* Retrieve format at the sensor subdev source pad */
- pad = media_entity_remote_source(&camif->pads[0]);
+ pad = media_entity_remote_pad(&camif->pads[0]);
if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
return -EPIPE;
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
index 0d0fab1..b385747 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -341,10 +341,11 @@
int i;
for (i = 0; i < CLK_MAX_NUM; i++) {
- if (IS_ERR_OR_NULL(camif->clock[i]))
+ if (IS_ERR(camif->clock[i]))
continue;
clk_unprepare(camif->clock[i]);
clk_put(camif->clock[i]);
+ camif->clock[i] = ERR_PTR(-EINVAL);
}
}
@@ -352,6 +353,9 @@
{
int ret, i;
+ for (i = 1; i < CLK_MAX_NUM; i++)
+ camif->clock[i] = ERR_PTR(-EINVAL);
+
for (i = 0; i < CLK_MAX_NUM; i++) {
camif->clock[i] = clk_get(camif->dev, camif_clocks[i]);
if (IS_ERR(camif->clock[i])) {
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
index 1a3b4fc..a9e3b16 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -379,7 +379,7 @@
camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg);
}
-void camif_s3c244x_hw_set_scaler(struct camif_vp *vp)
+static void camif_s3c244x_hw_set_scaler(struct camif_vp *vp)
{
struct camif_dev *camif = vp->camif;
struct camif_scaler *scaler = &vp->scaler;
@@ -426,7 +426,7 @@
scaler->main_h_ratio, scaler->main_v_ratio);
}
-void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp)
+static void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp)
{
struct camif_dev *camif = vp->camif;
struct camif_scaler *scaler = &vp->scaler;
@@ -601,6 +601,6 @@
pr_info("--- %s ---\n", label);
for (i = 0; i < ARRAY_SIZE(registers); i++) {
u32 cfg = readl(camif->io_base + registers[i].offset);
- printk(KERN_INFO "%s:\t0x%08x\n", registers[i].name, cfg);
+ dev_info(camif->dev, "%s:\t0x%08x\n", registers[i].name, cfg);
}
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index d12faa6..a130dcd 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -1424,7 +1424,7 @@
if (pdev->dev.of_node) {
const struct of_device_id *match;
- match = of_match_node(of_match_ptr(exynos_mfc_match),
+ match = of_match_node(exynos_mfc_match,
pdev->dev.of_node);
if (match)
driver_data = (struct s5p_mfc_variant *)match->data;
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 4e86626..1b34c36 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -576,16 +576,22 @@
return hdmi_streamoff(hdev);
}
-static void hdmi_resource_poweron(struct hdmi_resources *res)
+static int hdmi_resource_poweron(struct hdmi_resources *res)
{
+ int ret;
+
/* turn HDMI power on */
- regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ ret = regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ if (ret < 0)
+ return ret;
/* power-on hdmi physical interface */
clk_enable(res->hdmiphy);
/* use VPP as parent clock; HDMIPHY is not working yet */
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
/* turn clocks on */
clk_enable(res->sclk_hdmi);
+
+ return 0;
}
static void hdmi_resource_poweroff(struct hdmi_resources *res)
@@ -728,11 +734,13 @@
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- int ret = 0;
+ int ret;
dev_dbg(dev, "%s\n", __func__);
- hdmi_resource_poweron(&hdev->res);
+ ret = hdmi_resource_poweron(&hdev->res);
+ if (ret < 0)
+ return ret;
/* starting MHL */
ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
@@ -755,6 +763,15 @@
.runtime_resume = hdmi_runtime_resume,
};
+static void hdmi_resource_clear_clocks(struct hdmi_resources *res)
+{
+ res->hdmi = ERR_PTR(-EINVAL);
+ res->sclk_hdmi = ERR_PTR(-EINVAL);
+ res->sclk_pixel = ERR_PTR(-EINVAL);
+ res->sclk_hdmiphy = ERR_PTR(-EINVAL);
+ res->hdmiphy = ERR_PTR(-EINVAL);
+}
+
static void hdmi_resources_cleanup(struct hdmi_device *hdev)
{
struct hdmi_resources *res = &hdev->res;
@@ -765,17 +782,18 @@
regulator_bulk_free(res->regul_count, res->regul_bulk);
/* kfree is NULL-safe */
kfree(res->regul_bulk);
- if (!IS_ERR_OR_NULL(res->hdmiphy))
+ if (!IS_ERR(res->hdmiphy))
clk_put(res->hdmiphy);
- if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
+ if (!IS_ERR(res->sclk_hdmiphy))
clk_put(res->sclk_hdmiphy);
- if (!IS_ERR_OR_NULL(res->sclk_pixel))
+ if (!IS_ERR(res->sclk_pixel))
clk_put(res->sclk_pixel);
- if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ if (!IS_ERR(res->sclk_hdmi))
clk_put(res->sclk_hdmi);
- if (!IS_ERR_OR_NULL(res->hdmi))
+ if (!IS_ERR(res->hdmi))
clk_put(res->hdmi);
memset(res, 0, sizeof(*res));
+ hdmi_resource_clear_clocks(res);
}
static int hdmi_resources_init(struct hdmi_device *hdev)
@@ -793,8 +811,9 @@
dev_dbg(dev, "HDMI resource init\n");
memset(res, 0, sizeof(*res));
- /* get clocks, power */
+ hdmi_resource_clear_clocks(res);
+ /* get clocks, power */
res->hdmi = clk_get(dev, "hdmi");
if (IS_ERR(res->hdmi)) {
dev_err(dev, "failed to get clock 'hdmi'\n");
diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c
index 5733033..51805a5 100644
--- a/drivers/media/platform/s5p-tv/mixer_drv.c
+++ b/drivers/media/platform/s5p-tv/mixer_drv.c
@@ -211,6 +211,15 @@
return ret;
}
+static void mxr_resource_clear_clocks(struct mxr_resources *res)
+{
+ res->mixer = ERR_PTR(-EINVAL);
+ res->vp = ERR_PTR(-EINVAL);
+ res->sclk_mixer = ERR_PTR(-EINVAL);
+ res->sclk_hdmi = ERR_PTR(-EINVAL);
+ res->sclk_dac = ERR_PTR(-EINVAL);
+}
+
static void mxr_release_plat_resources(struct mxr_device *mdev)
{
free_irq(mdev->res.irq, mdev);
@@ -222,15 +231,15 @@
{
struct mxr_resources *res = &mdev->res;
- if (!IS_ERR_OR_NULL(res->sclk_dac))
+ if (!IS_ERR(res->sclk_dac))
clk_put(res->sclk_dac);
- if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ if (!IS_ERR(res->sclk_hdmi))
clk_put(res->sclk_hdmi);
- if (!IS_ERR_OR_NULL(res->sclk_mixer))
+ if (!IS_ERR(res->sclk_mixer))
clk_put(res->sclk_mixer);
- if (!IS_ERR_OR_NULL(res->vp))
+ if (!IS_ERR(res->vp))
clk_put(res->vp);
- if (!IS_ERR_OR_NULL(res->mixer))
+ if (!IS_ERR(res->mixer))
clk_put(res->mixer);
}
@@ -239,6 +248,8 @@
struct mxr_resources *res = &mdev->res;
struct device *dev = mdev->dev;
+ mxr_resource_clear_clocks(res);
+
res->mixer = clk_get(dev, "mixer");
if (IS_ERR(res->mixer)) {
mxr_err(mdev, "failed to get clock 'mixer'\n");
@@ -299,6 +310,7 @@
mxr_release_clocks(mdev);
mxr_release_plat_resources(mdev);
memset(&mdev->res, 0, sizeof(mdev->res));
+ mxr_resource_clear_clocks(&mdev->res);
}
static void mxr_release_layers(struct mxr_device *mdev)
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index ef0efdf..641b1f0 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -81,8 +81,9 @@
}
mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
- if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
+ if (IS_ERR(mdev->alloc_ctx)) {
mxr_err(mdev, "could not acquire vb2 allocator\n");
+ ret = PTR_ERR(mdev->alloc_ctx);
goto fail_v4l2_dev;
}
diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c
index ab6f9ef..0afa90f 100644
--- a/drivers/media/platform/s5p-tv/sdo_drv.c
+++ b/drivers/media/platform/s5p-tv/sdo_drv.c
@@ -262,11 +262,21 @@
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct sdo_device *sdev = sd_to_sdev(sd);
+ int ret;
dev_info(dev, "resume\n");
- clk_enable(sdev->sclk_dac);
- regulator_enable(sdev->vdac);
- regulator_enable(sdev->vdet);
+
+ ret = clk_enable(sdev->sclk_dac);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(sdev->vdac);
+ if (ret < 0)
+ goto dac_clk_dis;
+
+ ret = regulator_enable(sdev->vdet);
+ if (ret < 0)
+ goto vdac_r_dis;
/* software reset */
sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
@@ -285,6 +295,12 @@
SDO_COMPENSATION_CVBS_COMP_OFF);
sdo_reg_debug(sdev);
return 0;
+
+vdac_r_dis:
+ regulator_disable(sdev->vdac);
+dac_clk_dis:
+ clk_disable(sdev->sclk_dac);
+ return ret;
}
static const struct dev_pm_ops sdo_pm_ops = {
diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c
index 39b77d2..3dd762e 100644
--- a/drivers/media/platform/s5p-tv/sii9234_drv.c
+++ b/drivers/media/platform/s5p-tv/sii9234_drv.c
@@ -249,7 +249,9 @@
int ret;
dev_info(dev, "resume start\n");
- regulator_enable(ctx->power);
+ ret = regulator_enable(ctx->power);
+ if (ret < 0)
+ return ret;
ret = sii9234_reset(ctx);
if (ret)
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index 59a9dee..aa4cca3 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -359,10 +359,7 @@
veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu,
sh_veu_queue_init);
- if (IS_ERR(veu->m2m_ctx))
- return PTR_ERR(veu->m2m_ctx);
-
- return 0;
+ return PTR_RET(veu->m2m_ctx);
}
static int sh_veu_querycap(struct file *file, void *priv,
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index 7d02350..7a9c5e9 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -1248,32 +1248,6 @@
return res;
}
-static int sh_vou_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *id)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int sh_vou_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg);
-}
-
-static int sh_vou_s_register(struct file *file, void *fh,
- const struct v4l2_dbg_register *reg)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg);
-}
-#endif
-
/* sh_vou display ioctl operations */
static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
.vidioc_querycap = sh_vou_querycap,
@@ -1292,11 +1266,6 @@
.vidioc_cropcap = sh_vou_cropcap,
.vidioc_g_crop = sh_vou_g_crop,
.vidioc_s_crop = sh_vou_s_crop,
- .vidioc_g_chip_ident = sh_vou_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = sh_vou_g_register,
- .vidioc_s_register = sh_vou_s_register,
-#endif
};
static const struct v4l2_file_operations sh_vou_fops = {
@@ -1313,7 +1282,6 @@
.fops = &sh_vou_fops,
.ioctl_ops = &sh_vou_ioctl_ops,
.tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
- .current_norm = V4L2_STD_NTSC_M,
.vfl_dir = VFL_DIR_TX,
};
@@ -1352,7 +1320,7 @@
pix = &vou_dev->pix;
/* Fill in defaults */
- vou_dev->std = sh_vou_video_template.current_norm;
+ vou_dev->std = V4L2_STD_NTSC_M;
rect->left = 0;
rect->top = 0;
rect->width = VOU_MAX_IMAGE_WIDTH;
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index b139b52..626dccc 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -8,6 +8,9 @@
over a bus like PCI or USB. For example some i2c camera connected
directly to the data bus of an SoC.
+config SOC_CAMERA_SCALE_CROP
+ tristate
+
config SOC_CAMERA_PLATFORM
tristate "platform camera support"
depends on SOC_CAMERA
@@ -27,14 +30,10 @@
---help---
This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
-config MX3_VIDEO
- bool
-
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
select VIDEOBUF2_DMA_CONTIG
- select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
@@ -55,6 +54,7 @@
tristate "SuperH Mobile CEU Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
select VIDEOBUF2_DMA_CONTIG
+ select SOC_CAMERA_SCALE_CROP
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
@@ -66,14 +66,10 @@
---help---
This is a v4l2 driver for the TI OMAP1 camera interface
-config VIDEO_MX2_HOSTSUPPORT
- bool
-
config VIDEO_MX2
tristate "i.MX27 Camera Sensor Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27
select VIDEOBUF2_DMA_CONTIG
- select VIDEO_MX2_HOSTSUPPORT
---help---
This is a v4l2 driver for the i.MX27 Camera Sensor Interface
diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile
index 136b7f8..3918622 100644
--- a/drivers/media/platform/soc_camera/Makefile
+++ b/drivers/media/platform/soc_camera/Makefile
@@ -1,4 +1,8 @@
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
+obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o
+
+# a platform subdevice driver stub, allowing to support cameras by adding a
+# couple of callback functions to the board code
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
# soc-camera host drivers have to be linked after camera drivers
@@ -10,5 +14,3 @@
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
-
-ccflags-y += -I$(srctree)/drivers/media/i2c/soc_camera
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 1abbb36..1044856 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -102,7 +102,6 @@
struct list_head video_buffer_list;
struct frame_buffer *active;
- struct soc_camera_device *icd;
struct soc_camera_host soc_host;
};
@@ -367,7 +366,7 @@
/* Check if already in a frame */
if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
- dev_err(isi->icd->parent, "Already in frame handling.\n");
+ dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n");
return;
}
@@ -746,16 +745,26 @@
return formats;
}
-/* Called with .host_lock held */
static int isi_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int isi_camera_clock_start(struct soc_camera_host *ici)
+{
struct atmel_isi *isi = ici->priv;
int ret;
- if (isi->icd)
- return -EBUSY;
-
ret = clk_enable(isi->pclk);
if (ret)
return ret;
@@ -766,25 +775,16 @@
return ret;
}
- isi->icd = icd;
- dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
- icd->devnum);
return 0;
}
-/* Called with .host_lock held */
-static void isi_camera_remove_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct atmel_isi *isi = ici->priv;
- BUG_ON(icd != isi->icd);
+/* Called with .host_lock held */
+static void isi_camera_clock_stop(struct soc_camera_host *ici)
+{
+ struct atmel_isi *isi = ici->priv;
clk_disable(isi->mck);
clk_disable(isi->pclk);
- isi->icd = NULL;
-
- dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
- icd->devnum);
}
static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
@@ -888,6 +888,8 @@
.owner = THIS_MODULE,
.add = isi_camera_add_device,
.remove = isi_camera_remove_device,
+ .clock_start = isi_camera_clock_start,
+ .clock_stop = isi_camera_clock_stop,
.set_fmt = isi_camera_set_fmt,
.try_fmt = isi_camera_try_fmt,
.get_formats = isi_camera_get_formats,
diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index a3fd8d6..fea3e61 100644
--- a/drivers/media/platform/soc_camera/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
@@ -104,7 +104,6 @@
*/
struct mx1_camera_dev {
struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
struct mx1_camera_pdata *pdata;
struct mx1_buffer *active;
struct resource *res;
@@ -220,7 +219,7 @@
static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
{
struct videobuf_buffer *vbuf = &pcdev->active->vb;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
int ret;
if (unlikely(!pcdev->active)) {
@@ -331,7 +330,7 @@
static void mx1_camera_dma_irq(int channel, void *data)
{
struct mx1_camera_dev *pcdev = data;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
struct mx1_buffer *buf;
struct videobuf_buffer *vb;
unsigned long flags;
@@ -389,7 +388,7 @@
*/
div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
- dev_dbg(pcdev->icd->parent,
+ dev_dbg(pcdev->soc_host.icd->parent,
"System clock %lukHz, target freq %dkHz, divisor %lu\n",
lcdclk / 1000, mclk / 1000, div);
@@ -400,7 +399,7 @@
{
unsigned int csicr1 = CSICR1_EN;
- dev_dbg(pcdev->icd->parent, "Activate device\n");
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Activate device\n");
clk_prepare_enable(pcdev->clk);
@@ -416,7 +415,7 @@
static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
{
- dev_dbg(pcdev->icd->parent, "Deactivate device\n");
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Deactivate device\n");
/* Disable all CSI interface */
__raw_writel(0x00, pcdev->base + CSICR1);
@@ -424,36 +423,38 @@
clk_disable_unprepare(pcdev->clk);
}
-/*
- * The following two functions absolutely depend on the fact, that
- * there can be only one camera on i.MX1/i.MXL camera sensor interface
- */
static int mx1_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx1_camera_dev *pcdev = ici->priv;
-
- if (pcdev->icd)
- return -EBUSY;
-
dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
icd->devnum);
- mx1_camera_activate(pcdev);
-
- pcdev->icd = icd;
-
return 0;
}
static void mx1_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on i.MX1/i.MXL camera sensor interface
+ */
+static int mx1_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx1_camera_dev *pcdev = ici->priv;
+
+ mx1_camera_activate(pcdev);
+
+ return 0;
+}
+
+static void mx1_camera_clock_stop(struct soc_camera_host *ici)
+{
struct mx1_camera_dev *pcdev = ici->priv;
unsigned int csicr1;
- BUG_ON(icd != pcdev->icd);
-
/* disable interrupts */
csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
__raw_writel(csicr1, pcdev->base + CSICR1);
@@ -461,12 +462,7 @@
/* Stop DMA engine */
imx_dma_disable(pcdev->dma_chan);
- dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
- icd->devnum);
-
mx1_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
}
static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
@@ -679,6 +675,8 @@
.owner = THIS_MODULE,
.add = mx1_camera_add_device,
.remove = mx1_camera_remove_device,
+ .clock_start = mx1_camera_clock_start,
+ .clock_stop = mx1_camera_clock_stop,
.set_bus_param = mx1_camera_set_bus_param,
.set_fmt = mx1_camera_set_fmt,
.try_fmt = mx1_camera_try_fmt,
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 5bbeb43..45a0276 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -236,7 +236,6 @@
struct mx2_camera_dev {
struct device *dev;
struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
struct clk *clk_emma_ahb, *clk_emma_ipg;
struct clk *clk_csi_ahb, *clk_csi_per;
@@ -394,8 +393,8 @@
writel(phys, pcdev->base_emma +
PRP_DEST_Y_PTR - 0x14 * bufnum);
if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
- u32 imgsize = pcdev->icd->user_height *
- pcdev->icd->user_width;
+ u32 imgsize = pcdev->soc_host.icd->user_height *
+ pcdev->soc_host.icd->user_width;
writel(phys + imgsize, pcdev->base_emma +
PRP_DEST_CB_PTR - 0x14 * bufnum);
@@ -413,20 +412,30 @@
writel(0, pcdev->base_emma + PRP_CNTL);
}
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on mx2 camera sensor interface
*/
-static int mx2_camera_add_device(struct soc_camera_device *icd)
+static int mx2_camera_clock_start(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
int ret;
u32 csicr1;
- if (pcdev->icd)
- return -EBUSY;
-
ret = clk_prepare_enable(pcdev->clk_csi_ahb);
if (ret < 0)
return ret;
@@ -441,12 +450,8 @@
pcdev->csicr1 = csicr1;
writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
- pcdev->icd = icd;
pcdev->frame_count = 0;
- dev_info(icd->parent, "Camera driver attached to camera %d\n",
- icd->devnum);
-
return 0;
exit_csi_ahb:
@@ -455,19 +460,11 @@
return ret;
}
-static void mx2_camera_remove_device(struct soc_camera_device *icd)
+static void mx2_camera_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
- BUG_ON(icd != pcdev->icd);
-
- dev_info(icd->parent, "Camera driver detached from camera %d\n",
- icd->devnum);
-
mx2_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
}
/*
@@ -1280,6 +1277,8 @@
.owner = THIS_MODULE,
.add = mx2_camera_add_device,
.remove = mx2_camera_remove_device,
+ .clock_start = mx2_camera_clock_start,
+ .clock_stop = mx2_camera_clock_stop,
.set_fmt = mx2_camera_set_fmt,
.set_crop = mx2_camera_set_crop,
.get_formats = mx2_camera_get_formats,
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 5da3377..1047e3e 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -94,7 +94,6 @@
* Interface. If anyone ever builds hardware to enable more than one
* camera _simultaneously_, they will have to modify this driver too
*/
- struct soc_camera_device *icd;
struct clk *clk;
void __iomem *base;
@@ -461,8 +460,7 @@
}
/* First part of ipu_csi_init_interface() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
- struct soc_camera_device *icd)
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
{
u32 conf;
long rate;
@@ -506,51 +504,49 @@
clk_prepare_enable(mx3_cam->clk);
rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
- dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
if (rate)
clk_set_rate(mx3_cam->clk, rate);
}
-/* Called with .host_lock held */
static int mx3_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- if (mx3_cam->icd)
- return -EBUSY;
-
- mx3_camera_activate(mx3_cam, icd);
-
- mx3_cam->buf_total = 0;
- mx3_cam->icd = icd;
-
dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
icd->devnum);
return 0;
}
-/* Called with .host_lock held */
static void mx3_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int mx3_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ mx3_camera_activate(mx3_cam);
+
+ mx3_cam->buf_total = 0;
+
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void mx3_camera_clock_stop(struct soc_camera_host *ici)
+{
struct mx3_camera_dev *mx3_cam = ici->priv;
struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
- BUG_ON(icd != mx3_cam->icd);
-
if (*ichan) {
dma_release_channel(&(*ichan)->dma_chan);
*ichan = NULL;
}
clk_disable_unprepare(mx3_cam->clk);
-
- mx3_cam->icd = NULL;
-
- dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
- icd->devnum);
}
static int test_platform_param(struct mx3_camera_dev *mx3_cam,
@@ -1133,6 +1129,8 @@
.owner = THIS_MODULE,
.add = mx3_camera_add_device,
.remove = mx3_camera_remove_device,
+ .clock_start = mx3_camera_clock_start,
+ .clock_stop = mx3_camera_clock_stop,
.set_crop = mx3_camera_set_crop,
.set_fmt = mx3_camera_set_fmt,
.try_fmt = mx3_camera_try_fmt,
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index 9689a6e..6769193 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -150,7 +150,6 @@
struct omap1_cam_dev {
struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
struct clk *clk;
unsigned int irq;
@@ -564,7 +563,7 @@
{
struct omap1_cam_buf *buf = pcdev->active;
struct videobuf_buffer *vb;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
if (WARN_ON(!buf)) {
suspend_capture(pcdev);
@@ -790,7 +789,7 @@
static irqreturn_t cam_isr(int irq, void *data)
{
struct omap1_cam_dev *pcdev = data;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
struct omap1_cam_buf *buf = pcdev->active;
u32 it_status;
unsigned long flags;
@@ -894,19 +893,29 @@
CAM_WRITE(pcdev, GPIO, !reset);
}
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent,
+ "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on OMAP1 camera sensor interface
*/
-static int omap1_cam_add_device(struct soc_camera_device *icd)
+static int omap1_cam_clock_start(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
- if (pcdev->icd)
- return -EBUSY;
-
clk_enable(pcdev->clk);
/* setup sensor clock */
@@ -941,21 +950,14 @@
sensor_reset(pcdev, false);
- pcdev->icd = icd;
-
- dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
- icd->devnum);
return 0;
}
-static void omap1_cam_remove_device(struct soc_camera_device *icd)
+static void omap1_cam_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
- BUG_ON(icd != pcdev->icd);
-
suspend_capture(pcdev);
disable_capture(pcdev);
@@ -973,11 +975,6 @@
CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
clk_disable(pcdev->clk);
-
- pcdev->icd = NULL;
-
- dev_dbg(icd->parent,
- "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
}
/* Duplicate standard formats based on host capability of byte swapping */
@@ -1535,6 +1532,8 @@
.owner = THIS_MODULE,
.add = omap1_cam_add_device,
.remove = omap1_cam_remove_device,
+ .clock_start = omap1_cam_clock_start,
+ .clock_stop = omap1_cam_clock_stop,
.get_formats = omap1_cam_get_formats,
.set_crop = omap1_cam_set_crop,
.set_fmt = omap1_cam_set_fmt,
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index d665242..d4df305 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -200,7 +200,6 @@
* interface. If anyone ever builds hardware to enable more than
* one camera, they will have to modify this driver too
*/
- struct soc_camera_device *icd;
struct clk *clk;
unsigned int irq;
@@ -956,39 +955,38 @@
return IRQ_HANDLED;
}
-/*
- * The following two functions absolutely depend on the fact, that
- * there can be only one camera on PXA quick capture interface
- * Called with .host_lock held
- */
static int pxa_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct pxa_camera_dev *pcdev = ici->priv;
-
- if (pcdev->icd)
- return -EBUSY;
-
- pxa_camera_activate(pcdev);
-
- pcdev->icd = icd;
-
dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
icd->devnum);
return 0;
}
-/* Called with .host_lock held */
static void pxa_camera_remove_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct pxa_camera_dev *pcdev = ici->priv;
-
- BUG_ON(icd != pcdev->icd);
-
dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
icd->devnum);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on PXA quick capture interface
+ * Called with .host_lock held
+ */
+static int pxa_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ pxa_camera_activate(pcdev);
+
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void pxa_camera_clock_stop(struct soc_camera_host *ici)
+{
+ struct pxa_camera_dev *pcdev = ici->priv;
/* disable capture, disable interrupts */
__raw_writel(0x3ff, pcdev->base + CICR0);
@@ -999,8 +997,6 @@
DCSR(pcdev->dma_chans[2]) = 0;
pxa_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
}
static int test_platform_param(struct pxa_camera_dev *pcdev,
@@ -1596,8 +1592,8 @@
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
- if (pcdev->icd) {
- struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ if (pcdev->soc_host.icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
ret = v4l2_subdev_call(sd, core, s_power, 0);
if (ret == -ENOIOCTLCMD)
ret = 0;
@@ -1622,8 +1618,8 @@
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
- if (pcdev->icd) {
- struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ if (pcdev->soc_host.icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
ret = v4l2_subdev_call(sd, core, s_power, 1);
if (ret == -ENOIOCTLCMD)
ret = 0;
@@ -1640,6 +1636,8 @@
.owner = THIS_MODULE,
.add = pxa_camera_add_device,
.remove = pxa_camera_remove_device,
+ .clock_start = pxa_camera_clock_start,
+ .clock_stop = pxa_camera_clock_stop,
.set_crop = pxa_camera_set_crop,
.get_formats = pxa_camera_get_formats,
.put_formats = pxa_camera_put_formats,
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 143d29fe..f2de006 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -27,6 +27,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/device.h>
@@ -35,6 +36,7 @@
#include <linux/pm_runtime.h>
#include <linux/sched.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/soc_camera.h>
@@ -44,6 +46,8 @@
#include <media/v4l2-mediabus.h>
#include <media/soc_mediabus.h>
+#include "soc_scale_crop.h"
+
/* register offsets for sh7722 / sh7723 */
#define CAPSR 0x00 /* Capture start register */
@@ -95,7 +99,10 @@
struct sh_mobile_ceu_dev {
struct soc_camera_host ici;
- struct soc_camera_device *icd;
+ /* Asynchronous CSI2 linking */
+ struct v4l2_async_subdev *csi2_asd;
+ struct v4l2_subdev *csi2_sd;
+ /* Synchronous probing compatibility */
struct platform_device *csi2_pdev;
unsigned int irq;
@@ -119,6 +126,7 @@
enum v4l2_field field;
int sequence;
+ unsigned long flags;
unsigned int image_mode:1;
unsigned int is_16bit:1;
@@ -163,7 +171,6 @@
static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
{
int i, success = 0;
- struct soc_camera_device *icd = pcdev->icd;
ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
@@ -185,9 +192,8 @@
udelay(1);
}
-
if (2 != success) {
- dev_warn(icd->pdev, "soft reset time out\n");
+ dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
return -EIO;
}
@@ -277,7 +283,7 @@
*/
static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
{
- struct soc_camera_device *icd = pcdev->icd;
+ struct soc_camera_device *icd = pcdev->ici.icd;
dma_addr_t phys_addr_top, phys_addr_bottom;
unsigned long top1, top2;
unsigned long bottom1, bottom2;
@@ -534,72 +540,92 @@
{
struct v4l2_subdev *sd;
- if (!pcdev->csi2_pdev)
- return NULL;
+ if (pcdev->csi2_sd)
+ return pcdev->csi2_sd;
- v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
- if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
- return sd;
+ if (pcdev->csi2_asd) {
+ char name[] = "sh-mobile-csi2";
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+ if (!strncmp(name, sd->name, sizeof(name) - 1)) {
+ pcdev->csi2_sd = sd;
+ return sd;
+ }
+ }
return NULL;
}
-/* Called with .host_lock held */
+static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev,
+ struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = pcdev->csi2_sd;
+
+ return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL;
+}
+
static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *csi2_sd;
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
int ret;
- if (pcdev->icd)
- return -EBUSY;
+ if (csi2_sd) {
+ csi2_sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(csi2_sd, icd);
+ }
+
+ ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ /*
+ * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
+ * has not found this soc-camera device among its clients
+ */
+ if (csi2_sd && ret == -ENODEV)
+ csi2_sd->grp_id = 0;
dev_info(icd->parent,
- "SuperH Mobile CEU driver attached to camera %d\n",
+ "SuperH Mobile CEU%s driver attached to camera %d\n",
+ csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum);
+
+ return 0;
+}
+
+static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+
+ dev_info(icd->parent,
+ "SuperH Mobile CEU driver detached from camera %d\n",
icd->devnum);
+ v4l2_subdev_call(csi2_sd, core, s_power, 0);
+}
+
+/* Called with .host_lock held */
+static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici)
+{
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ int ret;
+
pm_runtime_get_sync(ici->v4l2_dev.dev);
pcdev->buf_total = 0;
ret = sh_mobile_ceu_soft_reset(pcdev);
- csi2_sd = find_csi2(pcdev);
- if (csi2_sd) {
- csi2_sd->grp_id = soc_camera_grp_id(icd);
- v4l2_set_subdev_hostdata(csi2_sd, icd);
- }
-
- ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
- pm_runtime_put(ici->v4l2_dev.dev);
- return ret;
- }
-
- /*
- * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
- * has not found this soc-camera device among its clients
- */
- if (ret == -ENODEV && csi2_sd)
- csi2_sd->grp_id = 0;
- pcdev->icd = icd;
-
return 0;
}
/* Called with .host_lock held */
-static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
+static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
- BUG_ON(icd != pcdev->icd);
-
- v4l2_subdev_call(csi2_sd, core, s_power, 0);
- if (csi2_sd)
- csi2_sd->grp_id = 0;
/* disable capture, disable interrupts */
ceu_write(pcdev, CEIER, 0);
sh_mobile_ceu_soft_reset(pcdev);
@@ -614,12 +640,6 @@
spin_unlock_irq(&pcdev->lock);
pm_runtime_put(ici->v4l2_dev.dev);
-
- dev_info(icd->parent,
- "SuperH Mobile CEU driver detached from camera %d\n",
- icd->devnum);
-
- pcdev->icd = NULL;
}
/*
@@ -705,7 +725,7 @@
}
/* CSI2 special configuration */
- if (pcdev->pdata->csi2) {
+ if (csi2_subdev(pcdev, icd)) {
in_width = ((in_width - 2) * 2);
left_offset *= 2;
}
@@ -762,13 +782,7 @@
static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
struct soc_camera_device *icd)
{
- if (pcdev->csi2_pdev) {
- struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
- if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
- return csi2_sd;
- }
-
- return soc_camera_to_subdev(icd);
+ return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd);
}
#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
@@ -809,7 +823,7 @@
/* Make choises, based on platform preferences */
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+ if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW)
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
@@ -817,7 +831,7 @@
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+ if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW)
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
@@ -872,11 +886,11 @@
value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
- if (pcdev->pdata->csi2) /* CSI2 mode */
+ if (csi2_subdev(pcdev, icd)) /* CSI2 mode */
value |= 3 << 12;
else if (pcdev->is_16bit)
value |= 1 << 12;
- else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+ else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT)
value |= 2 << 12;
ceu_write(pcdev, CAMCR, value);
@@ -993,8 +1007,6 @@
fmt->packing == SOC_MBUS_PACKING_EXTEND16);
}
-static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
-
static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
{
return container_of(ctrl->handler, struct soc_camera_device,
@@ -1051,7 +1063,7 @@
return 0;
}
- if (!pcdev->pdata->csi2) {
+ if (!csi2_subdev(pcdev, icd)) {
/* Are there any restrictions in the CSI-2 case? */
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
@@ -1072,7 +1084,7 @@
/* FIXME: subwindow is lost between close / open */
/* Cache current client geometry */
- ret = client_g_rect(sd, &rect);
+ ret = soc_camera_client_g_rect(sd, &rect);
if (ret < 0)
return ret;
@@ -1182,334 +1194,8 @@
icd->host_priv = NULL;
}
-/* Check if any dimension of r1 is smaller than respective one of r2 */
-static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
-{
- return r1->width < r2->width || r1->height < r2->height;
-}
-
-/* Check if r1 fails to cover r2 */
-static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
-{
- return r1->left > r2->left || r1->top > r2->top ||
- r1->left + r1->width < r2->left + r2->width ||
- r1->top + r1->height < r2->top + r2->height;
-}
-
-static unsigned int scale_down(unsigned int size, unsigned int scale)
-{
- return (size * 4096 + scale / 2) / scale;
-}
-
-static unsigned int calc_generic_scale(unsigned int input, unsigned int output)
-{
- return (input * 4096 + output / 2) / output;
-}
-
-/* Get and store current client crop */
-static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
-{
- struct v4l2_crop crop;
- struct v4l2_cropcap cap;
- int ret;
-
- crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, g_crop, &crop);
- if (!ret) {
- *rect = crop.c;
- return ret;
- }
-
- /* Camera driver doesn't support .g_crop(), assume default rectangle */
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (!ret)
- *rect = cap.defrect;
-
- return ret;
-}
-
-/* Client crop has changed, update our sub-rectangle to remain within the area */
-static void update_subrect(struct sh_mobile_ceu_cam *cam)
-{
- struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect;
-
- if (rect->width < subrect->width)
- subrect->width = rect->width;
-
- if (rect->height < subrect->height)
- subrect->height = rect->height;
-
- if (rect->left > subrect->left)
- subrect->left = rect->left;
- else if (rect->left + rect->width >
- subrect->left + subrect->width)
- subrect->left = rect->left + rect->width -
- subrect->width;
-
- if (rect->top > subrect->top)
- subrect->top = rect->top;
- else if (rect->top + rect->height >
- subrect->top + subrect->height)
- subrect->top = rect->top + rect->height -
- subrect->height;
-}
-
-/*
- * The common for both scaling and cropping iterative approach is:
- * 1. try if the client can produce exactly what requested by the user
- * 2. if (1) failed, try to double the client image until we get one big enough
- * 3. if (2) failed, try to request the maximum image
- */
-static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop,
- struct v4l2_crop *cam_crop)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
- struct device *dev = sd->v4l2_dev->dev;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_cropcap cap;
- int ret;
- unsigned int width, height;
-
- v4l2_subdev_call(sd, video, s_crop, crop);
- ret = client_g_rect(sd, cam_rect);
- if (ret < 0)
- return ret;
-
- /*
- * Now cam_crop contains the current camera input rectangle, and it must
- * be within camera cropcap bounds
- */
- if (!memcmp(rect, cam_rect, sizeof(*rect))) {
- /* Even if camera S_CROP failed, but camera rectangle matches */
- dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
- rect->width, rect->height, rect->left, rect->top);
- cam->rect = *cam_rect;
- return 0;
- }
-
- /* Try to fix cropping, that camera hasn't managed to set */
- dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top,
- rect->width, rect->height, rect->left, rect->top);
-
- /* We need sensor maximum rectangle */
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (ret < 0)
- return ret;
-
- /* Put user requested rectangle within sensor bounds */
- soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
- cap.bounds.width);
- soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
- cap.bounds.height);
-
- /*
- * Popular special case - some cameras can only handle fixed sizes like
- * QVGA, VGA,... Take care to avoid infinite loop.
- */
- width = max(cam_rect->width, 2);
- height = max(cam_rect->height, 2);
-
- /*
- * Loop as long as sensor is not covering the requested rectangle and
- * is still within its bounds
- */
- while (!ret && (is_smaller(cam_rect, rect) ||
- is_inside(cam_rect, rect)) &&
- (cap.bounds.width > width || cap.bounds.height > height)) {
-
- width *= 2;
- height *= 2;
-
- cam_rect->width = width;
- cam_rect->height = height;
-
- /*
- * We do not know what capabilities the camera has to set up
- * left and top borders. We could try to be smarter in iterating
- * them, e.g., if camera current left is to the right of the
- * target left, set it to the middle point between the current
- * left and minimum left. But that would add too much
- * complexity: we would have to iterate each border separately.
- * Instead we just drop to the left and top bounds.
- */
- if (cam_rect->left > rect->left)
- cam_rect->left = cap.bounds.left;
-
- if (cam_rect->left + cam_rect->width < rect->left + rect->width)
- cam_rect->width = rect->left + rect->width -
- cam_rect->left;
-
- if (cam_rect->top > rect->top)
- cam_rect->top = cap.bounds.top;
-
- if (cam_rect->top + cam_rect->height < rect->top + rect->height)
- cam_rect->height = rect->top + rect->height -
- cam_rect->top;
-
- v4l2_subdev_call(sd, video, s_crop, cam_crop);
- ret = client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- /* S_CROP must not modify the rectangle */
- if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
- /*
- * The camera failed to configure a suitable cropping,
- * we cannot use the current rectangle, set to max
- */
- *cam_rect = cap.bounds;
- v4l2_subdev_call(sd, video, s_crop, cam_crop);
- ret = client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- if (!ret) {
- cam->rect = *cam_rect;
- update_subrect(cam);
- }
-
- return ret;
-}
-
-/* Iterative s_mbus_fmt, also updates cached client crop on success */
-static int client_s_fmt(struct soc_camera_device *icd,
- struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
- unsigned int max_width, max_height;
- struct v4l2_cropcap cap;
- bool ceu_1to1;
- int ret;
-
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, mf);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
-
- if (width == mf->width && height == mf->height) {
- /* Perfect! The client has done it all. */
- ceu_1to1 = true;
- goto update_cache;
- }
-
- ceu_1to1 = false;
- if (!ceu_can_scale)
- goto update_cache;
-
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (ret < 0)
- return ret;
-
- max_width = min(cap.bounds.width, pcdev->max_width);
- max_height = min(cap.bounds.height, pcdev->max_height);
-
- /* Camera set a format, but geometry is not precise, try to improve */
- tmp_w = mf->width;
- tmp_h = mf->height;
-
- /* width <= max_width && height <= max_height - guaranteed by try_fmt */
- while ((width > tmp_w || height > tmp_h) &&
- tmp_w < max_width && tmp_h < max_height) {
- tmp_w = min(2 * tmp_w, max_width);
- tmp_h = min(2 * tmp_h, max_height);
- mf->width = tmp_w;
- mf->height = tmp_h;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, mf);
- dev_geo(dev, "Camera scaled to %ux%u\n",
- mf->width, mf->height);
- if (ret < 0) {
- /* This shouldn't happen */
- dev_err(dev, "Client failed to set format: %d\n", ret);
- return ret;
- }
- }
-
-update_cache:
- /* Update cache */
- ret = client_g_rect(sd, &cam->rect);
- if (ret < 0)
- return ret;
-
- if (ceu_1to1)
- cam->subrect = cam->rect;
- else
- update_subrect(cam);
-
- return 0;
-}
-
-/**
- * @width - on output: user width, mapped back to input
- * @height - on output: user height, mapped back to input
- * @mf - in- / output camera output window
- */
-static int client_scale(struct soc_camera_device *icd,
- struct v4l2_mbus_framefmt *mf,
- unsigned int *width, unsigned int *height,
- bool ceu_can_scale)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->parent;
- struct v4l2_mbus_framefmt mf_tmp = *mf;
- unsigned int scale_h, scale_v;
- int ret;
-
- /*
- * 5. Apply iterative camera S_FMT for camera user window (also updates
- * client crop cache and the imaginary sub-rectangle).
- */
- ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "5: camera scaled to %ux%u\n",
- mf_tmp.width, mf_tmp.height);
-
- /* 6. Retrieve camera output window (g_fmt) */
-
- /* unneeded - it is already in "mf_tmp" */
-
- /* 7. Calculate new client scales. */
- scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width);
- scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height);
-
- mf->width = mf_tmp.width;
- mf->height = mf_tmp.height;
- mf->colorspace = mf_tmp.colorspace;
-
- /*
- * 8. Calculate new CEU crop - apply camera scales to previously
- * updated "effective" crop.
- */
- *width = scale_down(cam->subrect.width, scale_h);
- *height = scale_down(cam->subrect.height, scale_v);
-
- dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
-
- return 0;
-}
+#define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale)
+#define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out)
/*
* CEU can scale and crop, but we don't want to waste bandwidth and kill the
@@ -1547,7 +1233,8 @@
* 1. - 2. Apply iterative camera S_CROP for new input window, read back
* actual camera rectangle.
*/
- ret = client_s_crop(icd, &a_writable, &cam_crop);
+ ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop,
+ &cam->rect, &cam->subrect);
if (ret < 0)
return ret;
@@ -1666,55 +1353,6 @@
return 0;
}
-/*
- * Calculate real client output window by applying new scales to the current
- * client crop. New scales are calculated from the requested output format and
- * CEU crop, mapped backed onto the client input (subrect).
- */
-static void calculate_client_output(struct soc_camera_device *icd,
- const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->parent;
- struct v4l2_rect *cam_subrect = &cam->subrect;
- unsigned int scale_v, scale_h;
-
- if (cam_subrect->width == cam->rect.width &&
- cam_subrect->height == cam->rect.height) {
- /* No sub-cropping */
- mf->width = pix->width;
- mf->height = pix->height;
- return;
- }
-
- /* 1.-2. Current camera scales and subwin - cached. */
-
- dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
- cam_subrect->width, cam_subrect->height,
- cam_subrect->left, cam_subrect->top);
-
- /*
- * 3. Calculate new combined scales from input sub-window to requested
- * user window.
- */
-
- /*
- * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
- * (128x96) or larger than VGA
- */
- scale_h = calc_generic_scale(cam_subrect->width, pix->width);
- scale_v = calc_generic_scale(cam_subrect->height, pix->height);
-
- dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
-
- /*
- * 4. Calculate desired client output window by applying combined scales
- * to client (real) input window.
- */
- mf->width = scale_down(cam->rect.width, scale_h);
- mf->height = scale_down(cam->rect.height, scale_v);
-}
-
/* Similar to set_crop multistage iterative algorithm */
static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
@@ -1727,8 +1365,8 @@
struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
const struct soc_camera_format_xlate *xlate;
- /* Keep Compiler Happy */
- unsigned int ceu_sub_width = 0, ceu_sub_height = 0;
+ unsigned int ceu_sub_width = pcdev->max_width,
+ ceu_sub_height = pcdev->max_height;
u16 scale_v, scale_h;
int ret;
bool image_mode;
@@ -1755,7 +1393,7 @@
}
/* 1.-4. Calculate desired client output geometry */
- calculate_client_output(icd, pix, &mf);
+ soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, 12);
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
@@ -1777,8 +1415,9 @@
dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
/* 5. - 9. */
- ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height,
- image_mode && V4L2_FIELD_NONE == field);
+ ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect,
+ &mf, &ceu_sub_width, &ceu_sub_height,
+ image_mode && V4L2_FIELD_NONE == field, 12);
dev_geo(dev, "5-9: client scale return %d\n", ret);
@@ -2036,6 +1675,8 @@
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
.remove = sh_mobile_ceu_remove_device,
+ .clock_start = sh_mobile_ceu_clock_start,
+ .clock_stop = sh_mobile_ceu_clock_stop,
.get_formats = sh_mobile_ceu_get_formats,
.put_formats = sh_mobile_ceu_put_formats,
.get_crop = sh_mobile_ceu_get_crop,
@@ -2079,7 +1720,7 @@
struct resource *res;
void __iomem *base;
unsigned int irq;
- int err = 0;
+ int err, i;
struct bus_wait wait = {
.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
.notifier.notifier_call = bus_notify,
@@ -2104,13 +1745,36 @@
init_completion(&pcdev->complete);
pcdev->pdata = pdev->dev.platform_data;
- if (!pcdev->pdata) {
+ if (!pcdev->pdata && !pdev->dev.of_node) {
dev_err(&pdev->dev, "CEU platform data not set.\n");
return -EINVAL;
}
- pcdev->max_width = pcdev->pdata->max_width ? : 2560;
- pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+ /* TODO: implement per-device bus flags */
+ if (pcdev->pdata) {
+ pcdev->max_width = pcdev->pdata->max_width;
+ pcdev->max_height = pcdev->pdata->max_height;
+ pcdev->flags = pcdev->pdata->flags;
+ }
+
+ if (!pcdev->max_width) {
+ unsigned int v;
+ err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v);
+ if (!err)
+ pcdev->max_width = v;
+
+ if (!pcdev->max_width)
+ pcdev->max_width = 2560;
+ }
+ if (!pcdev->max_height) {
+ unsigned int v;
+ err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v);
+ if (!err)
+ pcdev->max_height = v;
+
+ if (!pcdev->max_height)
+ pcdev->max_height = 1920;
+ }
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
@@ -2160,31 +1824,60 @@
goto exit_free_clk;
}
- err = soc_camera_host_register(&pcdev->ici);
- if (err)
- goto exit_free_ctx;
+ if (pcdev->pdata && pcdev->pdata->asd_sizes) {
+ struct v4l2_async_subdev **asd;
+ char name[] = "sh-mobile-csi2";
+ int j;
- /* CSI2 interfacing */
- csi2 = pcdev->pdata->csi2;
+ /*
+ * CSI2 interfacing: several groups can use CSI2, pick up the
+ * first one
+ */
+ asd = pcdev->pdata->asd;
+ for (j = 0; pcdev->pdata->asd_sizes[j]; j++) {
+ for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) {
+ dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n",
+ __func__, i, (*asd)->bus_type);
+ if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
+ !strncmp(name, (*asd)->match.platform.name,
+ sizeof(name) - 1)) {
+ pcdev->csi2_asd = *asd;
+ break;
+ }
+ }
+ if (pcdev->csi2_asd)
+ break;
+ }
+
+ pcdev->ici.asd = pcdev->pdata->asd;
+ pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes;
+ }
+
+ /* Legacy CSI2 interfacing */
+ csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
if (csi2) {
+ /*
+ * TODO: remove this once all users are converted to
+ * asynchronous CSI2 probing. If it has to be kept, csi2
+ * platform device resources have to be added, using
+ * platform_device_add_resources()
+ */
struct platform_device *csi2_pdev =
platform_device_alloc("sh-mobile-csi2", csi2->id);
struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
if (!csi2_pdev) {
err = -ENOMEM;
- goto exit_host_unregister;
+ goto exit_free_ctx;
}
pcdev->csi2_pdev = csi2_pdev;
- err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+ err = platform_device_add_data(csi2_pdev, csi2_pdata,
+ sizeof(*csi2_pdata));
if (err < 0)
goto exit_pdev_put;
- csi2_pdata = csi2_pdev->dev.platform_data;
- csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
-
csi2_pdev->resource = csi2->resource;
csi2_pdev->num_resources = csi2->num_resources;
@@ -2226,17 +1919,38 @@
err = -ENODEV;
goto exit_pdev_unregister;
}
+
+ pcdev->csi2_sd = platform_get_drvdata(csi2_pdev);
+ }
+
+ err = soc_camera_host_register(&pcdev->ici);
+ if (err)
+ goto exit_csi2_unregister;
+
+ if (csi2) {
+ err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev,
+ pcdev->csi2_sd);
+ dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n",
+ __func__, err);
+ if (err < 0)
+ goto exit_host_unregister;
+ /* v4l2_device_register_subdev() took a reference too */
+ module_put(pcdev->csi2_sd->owner);
}
return 0;
-exit_pdev_unregister:
- platform_device_del(pcdev->csi2_pdev);
-exit_pdev_put:
- pcdev->csi2_pdev->resource = NULL;
- platform_device_put(pcdev->csi2_pdev);
exit_host_unregister:
soc_camera_host_unregister(&pcdev->ici);
+exit_csi2_unregister:
+ if (csi2) {
+ module_put(pcdev->csi2_pdev->dev.driver->owner);
+exit_pdev_unregister:
+ platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+ pcdev->csi2_pdev->resource = NULL;
+ platform_device_put(pcdev->csi2_pdev);
+ }
exit_free_ctx:
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
exit_free_clk:
@@ -2287,10 +2001,18 @@
.runtime_resume = sh_mobile_ceu_runtime_nop,
};
+static const struct of_device_id sh_mobile_ceu_of_match[] = {
+ { .compatible = "renesas,sh-mobile-ceu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
+
static struct platform_driver sh_mobile_ceu_driver = {
.driver = {
.name = "sh_mobile_ceu",
+ .owner = THIS_MODULE,
.pm = &sh_mobile_ceu_dev_pm_ops,
+ .of_match_table = sh_mobile_ceu_of_match,
},
.probe = sh_mobile_ceu_probe,
.remove = sh_mobile_ceu_remove,
@@ -2314,5 +2036,5 @@
MODULE_DESCRIPTION("SuperH Mobile CEU driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.6");
+MODULE_VERSION("0.1.0");
MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index 09cb4fc..05dd21a 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -36,7 +36,6 @@
struct sh_csi2 {
struct v4l2_subdev subdev;
- struct list_head list;
unsigned int irq;
unsigned long mipi_flags;
void __iomem *base;
@@ -44,6 +43,8 @@
struct sh_csi2_client_config *client;
};
+static void sh_csi2_hwinit(struct sh_csi2 *priv);
+
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
@@ -132,10 +133,58 @@
static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+ if (!priv->mipi_flags) {
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+ unsigned long common_flags, csi2_flags;
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+ int ret;
+
+ /* Check if we can support this camera */
+ csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
+ V4L2_MBUS_CSI2_1_LANE;
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (priv->client->lanes != 1)
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case SH_CSI2I:
+ switch (priv->client->lanes) {
+ default:
+ csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+ case 3:
+ csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+ case 2:
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ }
+ }
+
+ ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg);
+ if (ret == -ENOIOCTLCMD)
+ common_flags = csi2_flags;
+ else if (!ret)
+ common_flags = soc_mbus_config_compatible(&client_cfg,
+ csi2_flags);
+ else
+ common_flags = 0;
+
+ if (!common_flags)
+ return -EINVAL;
+
+ /* All good: camera MIPI configuration supported */
+ priv->mipi_flags = common_flags;
+ }
+
+ if (cfg) {
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ }
return 0;
}
@@ -146,8 +195,17 @@
struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
- struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
- .flags = priv->mipi_flags};
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+ int ret = sh_csi2_g_mbus_config(sd, NULL);
+
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_get_sync(&priv->pdev->dev);
+
+ sh_csi2_hwinit(priv);
+
+ client_cfg.flags = priv->mipi_flags;
return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
}
@@ -202,19 +260,19 @@
static int sh_csi2_client_connect(struct sh_csi2 *priv)
{
- struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
- struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
- struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
struct device *dev = v4l2_get_subdevdata(&priv->subdev);
- struct v4l2_mbus_config cfg;
- unsigned long common_flags, csi2_flags;
- int i, ret;
+ struct sh_csi2_pdata *pdata = dev->platform_data;
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
+ int i;
if (priv->client)
return -EBUSY;
for (i = 0; i < pdata->num_clients; i++)
- if (&pdata->clients[i].pdev->dev == icd->pdev)
+ if ((pdata->clients[i].pdev &&
+ &pdata->clients[i].pdev->dev == icd->pdev) ||
+ (icd->control &&
+ strcmp(pdata->clients[i].name, dev_name(icd->control))))
break;
dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
@@ -222,46 +280,8 @@
if (i == pdata->num_clients)
return -ENODEV;
- /* Check if we can support this camera */
- csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
-
- switch (pdata->type) {
- case SH_CSI2C:
- if (pdata->clients[i].lanes != 1)
- csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
- break;
- case SH_CSI2I:
- switch (pdata->clients[i].lanes) {
- default:
- csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
- case 3:
- csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
- case 2:
- csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
- }
- }
-
- cfg.type = V4L2_MBUS_CSI2;
- ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
- if (ret == -ENOIOCTLCMD)
- common_flags = csi2_flags;
- else if (!ret)
- common_flags = soc_mbus_config_compatible(&cfg,
- csi2_flags);
- else
- common_flags = 0;
-
- if (!common_flags)
- return -EINVAL;
-
- /* All good: camera MIPI configuration supported */
- priv->mipi_flags = common_flags;
priv->client = pdata->clients + i;
- pm_runtime_get_sync(dev);
-
- sh_csi2_hwinit(priv);
-
return 0;
}
@@ -304,11 +324,18 @@
/* Platform data specify the PHY, lanes, ECC, CRC */
struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* Interrupt unused so far */
irq = platform_get_irq(pdev, 0);
- if (!res || (int)irq <= 0 || !pdata) {
+ if (!res || (int)irq <= 0) {
dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
return -ENODEV;
}
@@ -319,10 +346,6 @@
return -EINVAL;
}
- priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
priv->irq = irq;
priv->base = devm_ioremap_resource(&pdev->dev, res);
@@ -330,37 +353,35 @@
return PTR_ERR(priv->base);
priv->pdev = pdev;
- platform_set_drvdata(pdev, priv);
+ priv->subdev.owner = THIS_MODULE;
+ priv->subdev.dev = &pdev->dev;
+ platform_set_drvdata(pdev, &priv->subdev);
v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
- dev_name(pdata->v4l2_dev->dev));
- ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
- dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+ dev_name(&pdev->dev));
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
if (ret < 0)
- goto esdreg;
+ return ret;
pm_runtime_enable(&pdev->dev);
dev_dbg(&pdev->dev, "CSI2 probed.\n");
return 0;
-
-esdreg:
- platform_set_drvdata(pdev, NULL);
-
- return ret;
}
static int sh_csi2_remove(struct platform_device *pdev)
{
- struct sh_csi2 *priv = platform_get_drvdata(pdev);
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev);
- v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_async_unregister_subdev(&priv->subdev);
+ v4l2_device_unregister_subdev(subdev);
pm_runtime_disable(&pdev->dev);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 3a4efbd..2dd0e52 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -21,21 +21,23 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/list.h>
-#include <linux/mutex.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <linux/pm_runtime.h>
#include <linux/vmalloc.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-core.h>
#include <media/videobuf2-core.h>
-#include <media/soc_mediabus.h>
/* Default to VGA resolution */
#define DEFAULT_WIDTH 640
@@ -46,17 +48,39 @@
(icd)->vb_vidq.streaming : \
vb2_is_streaming(&(icd)->vb2_vidq))
+#define MAP_MAX_NUM 32
+static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
-static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
+/*
+ * Protects lists and bitmaps of hosts and devices.
+ * Lock nesting: Ok to take ->host_lock under list_lock.
+ */
+static DEFINE_MUTEX(list_lock);
-int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+struct soc_camera_async_client {
+ struct v4l2_async_subdev *sensor;
+ struct v4l2_async_notifier notifier;
+ struct platform_device *pdev;
+ struct list_head list; /* needed for clean up */
+};
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
+
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+ struct v4l2_clk *clk)
{
- int ret = regulator_bulk_enable(ssdd->num_regulators,
+ int ret = clk ? v4l2_clk_enable(clk) : 0;
+ if (ret < 0) {
+ dev_err(dev, "Cannot enable clock: %d\n", ret);
+ return ret;
+ }
+ ret = regulator_bulk_enable(ssdd->num_regulators,
ssdd->regulators);
if (ret < 0) {
dev_err(dev, "Cannot enable regulators\n");
- return ret;
+ goto eregenable;
}
if (ssdd->power) {
@@ -64,16 +88,25 @@
if (ret < 0) {
dev_err(dev,
"Platform failed to power-on the camera.\n");
- regulator_bulk_disable(ssdd->num_regulators,
- ssdd->regulators);
+ goto epwron;
}
}
+ return 0;
+
+epwron:
+ regulator_bulk_disable(ssdd->num_regulators,
+ ssdd->regulators);
+eregenable:
+ if (clk)
+ v4l2_clk_disable(clk);
+
return ret;
}
EXPORT_SYMBOL(soc_camera_power_on);
-int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+ struct v4l2_clk *clk)
{
int ret = 0;
int err;
@@ -94,10 +127,21 @@
ret = ret ? : err;
}
+ if (clk)
+ v4l2_clk_disable(clk);
+
return ret;
}
EXPORT_SYMBOL(soc_camera_power_off);
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+{
+
+ return devm_regulator_bulk_get(dev, ssdd->num_regulators,
+ ssdd->regulators);
+}
+EXPORT_SYMBOL(soc_camera_power_init);
+
static int __soc_camera_power_on(struct soc_camera_device *icd)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -235,7 +279,6 @@
/* default is camera */
inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_UNKNOWN;
strcpy(inp->name, "Camera");
return 0;
@@ -505,6 +548,58 @@
return ici->ops->set_bus_param(icd);
}
+static int soc_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ if (ici->icd)
+ return -EBUSY;
+
+ if (!icd->clk) {
+ mutex_lock(&ici->clk_lock);
+ ret = ici->ops->clock_start(ici);
+ mutex_unlock(&ici->clk_lock);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ici->ops->add) {
+ ret = ici->ops->add(icd);
+ if (ret < 0)
+ goto eadd;
+ }
+
+ ici->icd = icd;
+
+ return 0;
+
+eadd:
+ if (!icd->clk) {
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+ }
+ return ret;
+}
+
+static void soc_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (WARN_ON(icd != ici->icd))
+ return;
+
+ if (ici->ops->remove)
+ ici->ops->remove(icd);
+ if (!icd->clk) {
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+ }
+ ici->icd = NULL;
+}
+
static int soc_camera_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
@@ -525,7 +620,7 @@
return -ENODEV;
}
- icd = dev_get_drvdata(vdev->parent);
+ icd = video_get_drvdata(vdev);
ici = to_soc_camera_host(icd->parent);
ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV;
@@ -568,7 +663,7 @@
if (sdesc->subdev_desc.reset)
sdesc->subdev_desc.reset(icd->pdev);
- ret = ici->ops->add(icd);
+ ret = soc_camera_add_device(icd);
if (ret < 0) {
dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
goto eiciadd;
@@ -610,8 +705,8 @@
return 0;
/*
- * First four errors are entered with the .host_lock held
- * and use_count == 1
+ * All errors are entered with the .host_lock held, first four also
+ * with use_count == 1
*/
einitvb:
esfmt:
@@ -619,7 +714,7 @@
eresume:
__soc_camera_power_off(icd);
epower:
- ici->ops->remove(icd);
+ soc_camera_remove_device(icd);
eiciadd:
icd->use_count--;
mutex_unlock(&ici->host_lock);
@@ -645,7 +740,7 @@
vb2_queue_release(&icd->vb2_vidq);
__soc_camera_power_off(icd);
- ici->ops->remove(icd);
+ soc_camera_remove_device(icd);
}
if (icd->streamer == file)
@@ -1036,36 +1131,8 @@
return -ENOIOCTLCMD;
}
-static int soc_camera_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *id)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, g_chip_ident, id);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int soc_camera_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, g_register, reg);
-}
-
-static int soc_camera_s_register(struct file *file, void *fh,
- const struct v4l2_dbg_register *reg)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, s_register, reg);
-}
-#endif
-
-static int soc_camera_probe(struct soc_camera_device *icd);
+static int soc_camera_probe(struct soc_camera_host *ici,
+ struct soc_camera_device *icd);
/* So far this function cannot fail */
static void scan_add_host(struct soc_camera_host *ici)
@@ -1074,38 +1141,215 @@
mutex_lock(&list_lock);
- list_for_each_entry(icd, &devices, list) {
+ list_for_each_entry(icd, &devices, list)
if (icd->iface == ici->nr) {
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+ struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
+
+ /* The camera could have been already on, try to reset */
+ if (ssdd->reset)
+ ssdd->reset(icd->pdev);
+
icd->parent = ici->v4l2_dev.dev;
- soc_camera_probe(icd);
+
+ /* Ignore errors */
+ soc_camera_probe(ici, icd);
}
- }
mutex_unlock(&list_lock);
}
+/*
+ * It is invalid to call v4l2_clk_enable() after a successful probing
+ * asynchronously outside of V4L2 operations, i.e. with .host_lock not held.
+ */
+static int soc_camera_clk_enable(struct v4l2_clk *clk)
+{
+ struct soc_camera_device *icd = clk->priv;
+ struct soc_camera_host *ici;
+ int ret;
+
+ if (!icd || !icd->parent)
+ return -ENODEV;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ if (!try_module_get(ici->ops->owner))
+ return -ENODEV;
+
+ /*
+ * If a different client is currently being probed, the host will tell
+ * you to go
+ */
+ mutex_lock(&ici->clk_lock);
+ ret = ici->ops->clock_start(ici);
+ mutex_unlock(&ici->clk_lock);
+ return ret;
+}
+
+static void soc_camera_clk_disable(struct v4l2_clk *clk)
+{
+ struct soc_camera_device *icd = clk->priv;
+ struct soc_camera_host *ici;
+
+ if (!icd || !icd->parent)
+ return;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+
+ module_put(ici->ops->owner);
+}
+
+/*
+ * Eventually, it would be more logical to make the respective host the clock
+ * owner, but then we would have to copy this struct for each ici. Besides, it
+ * would introduce the circular dependency problem, unless we port all client
+ * drivers to release the clock, when not in use.
+ */
+static const struct v4l2_clk_ops soc_camera_clk_ops = {
+ .owner = THIS_MODULE,
+ .enable = soc_camera_clk_enable,
+ .disable = soc_camera_clk_disable,
+};
+
+static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc,
+ struct soc_camera_async_client *sasc)
+{
+ struct platform_device *pdev;
+ int ret, i;
+
+ mutex_lock(&list_lock);
+ i = find_first_zero_bit(device_map, MAP_MAX_NUM);
+ if (i < MAP_MAX_NUM)
+ set_bit(i, device_map);
+ mutex_unlock(&list_lock);
+ if (i >= MAP_MAX_NUM)
+ return -ENOMEM;
+
+ pdev = platform_device_alloc("soc-camera-pdrv", i);
+ if (!pdev)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc));
+ if (ret < 0) {
+ platform_device_put(pdev);
+ return ret;
+ }
+
+ sasc->pdev = pdev;
+
+ return 0;
+}
+
+static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc)
+{
+ struct platform_device *pdev = sasc->pdev;
+ int ret;
+
+ ret = platform_device_add(pdev);
+ if (ret < 0 || !pdev->dev.driver)
+ return NULL;
+
+ return platform_get_drvdata(pdev);
+}
+
+/* Locking: called with .host_lock held */
+static int soc_camera_probe_finish(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(sd, icd);
+
+ ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = soc_camera_add_device(icd);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
+ return ret;
+ }
+
+ /* At this point client .probe() should have run already */
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eusrfmt;
+
+ icd->field = V4L2_FIELD_ANY;
+
+ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ goto evidstart;
+
+ /* Try to improve our guess of a reasonable window format */
+ if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+ icd->colorspace = mf.colorspace;
+ icd->field = mf.field;
+ }
+ soc_camera_remove_device(icd);
+
+ return 0;
+
+evidstart:
+ soc_camera_free_user_formats(icd);
+eusrfmt:
+ soc_camera_remove_device(icd);
+
+ return ret;
+}
+
#ifdef CONFIG_I2C_BOARDINFO
-static int soc_camera_init_i2c(struct soc_camera_device *icd,
+static int soc_camera_i2c_init(struct soc_camera_device *icd,
struct soc_camera_desc *sdesc)
{
struct i2c_client *client;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct soc_camera_host *ici;
struct soc_camera_host_desc *shd = &sdesc->host_desc;
- struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id);
+ struct i2c_adapter *adap;
struct v4l2_subdev *subdev;
+ char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ int ret;
+ /* First find out how we link the main client */
+ if (icd->sasc) {
+ /* Async non-OF probing handled by the subdevice list */
+ return -EPROBE_DEFER;
+ }
+
+ ici = to_soc_camera_host(icd->parent);
+ adap = i2c_get_adapter(shd->i2c_adapter_id);
if (!adap) {
dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
shd->i2c_adapter_id);
- goto ei2cga;
+ return -ENODEV;
}
shd->board_info->platform_data = &sdesc->subdev_desc;
+ snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+ shd->i2c_adapter_id, shd->board_info->addr);
+
+ icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+ if (IS_ERR(icd->clk)) {
+ ret = PTR_ERR(icd->clk);
+ goto eclkreg;
+ }
+
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
shd->board_info, NULL);
- if (!subdev)
+ if (!subdev) {
+ ret = -ENODEV;
goto ei2cnd;
+ }
client = v4l2_get_subdevdata(subdev);
@@ -1114,39 +1358,203 @@
return 0;
ei2cnd:
+ v4l2_clk_unregister(icd->clk);
+eclkreg:
+ icd->clk = NULL;
i2c_put_adapter(adap);
-ei2cga:
- return -ENODEV;
+ return ret;
}
-static void soc_camera_free_i2c(struct soc_camera_device *icd)
+static void soc_camera_i2c_free(struct soc_camera_device *icd)
{
struct i2c_client *client =
to_i2c_client(to_soc_camera_control(icd));
- struct i2c_adapter *adap = client->adapter;
+ struct i2c_adapter *adap;
icd->control = NULL;
+ if (icd->sasc)
+ return;
+
+ adap = client->adapter;
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
i2c_put_adapter(adap);
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+}
+
+/*
+ * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async
+ * internal global mutex, therefore cannot race against other asynchronous
+ * events. Until notifier->complete() (soc_camera_async_complete()) is called,
+ * the video device node is not registered and no V4L fops can occur. Unloading
+ * of the host driver also calls a v4l2-async function, so also there we're
+ * protected.
+ */
+static int soc_camera_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct soc_camera_async_client *sasc = container_of(notifier,
+ struct soc_camera_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (asd == sasc->sensor && !WARN_ON(icd->control)) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /*
+ * Only now we get subdevice-specific information like
+ * regulators, flags, callbacks, etc.
+ */
+ if (client) {
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+ struct soc_camera_subdev_desc *ssdd =
+ soc_camera_i2c_to_desc(client);
+ if (ssdd) {
+ memcpy(&sdesc->subdev_desc, ssdd,
+ sizeof(sdesc->subdev_desc));
+ if (ssdd->reset)
+ ssdd->reset(icd->pdev);
+ }
+
+ icd->control = &client->dev;
+ }
+ }
+
+ return 0;
+}
+
+static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct soc_camera_async_client *sasc = container_of(notifier,
+ struct soc_camera_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (icd->clk) {
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+ }
+}
+
+static int soc_camera_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct soc_camera_async_client *sasc = container_of(notifier,
+ struct soc_camera_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (to_soc_camera_control(icd)) {
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ mutex_lock(&list_lock);
+ ret = soc_camera_probe(ici, icd);
+ mutex_unlock(&list_lock);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int scan_async_group(struct soc_camera_host *ici,
+ struct v4l2_async_subdev **asd, unsigned int size)
+{
+ struct soc_camera_async_subdev *sasd;
+ struct soc_camera_async_client *sasc;
+ struct soc_camera_device *icd;
+ struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
+ char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ int ret, i;
+
+ /* First look for a sensor */
+ for (i = 0; i < size; i++) {
+ sasd = container_of(asd[i], struct soc_camera_async_subdev, asd);
+ if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE)
+ break;
+ }
+
+ if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) {
+ /* All useless */
+ dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n");
+ return -ENODEV;
+ }
+
+ /* Or shall this be managed by the soc-camera device? */
+ sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL);
+ if (!sasc)
+ return -ENOMEM;
+
+ /* HACK: just need a != NULL */
+ sdesc.host_desc.board_info = ERR_PTR(-ENODATA);
+
+ ret = soc_camera_dyn_pdev(&sdesc, sasc);
+ if (ret < 0)
+ return ret;
+
+ sasc->sensor = &sasd->asd;
+
+ icd = soc_camera_add_pdev(sasc);
+ if (!icd) {
+ platform_device_put(sasc->pdev);
+ return -ENOMEM;
+ }
+
+ sasc->notifier.subdev = asd;
+ sasc->notifier.num_subdevs = size;
+ sasc->notifier.bound = soc_camera_async_bound;
+ sasc->notifier.unbind = soc_camera_async_unbind;
+ sasc->notifier.complete = soc_camera_async_complete;
+
+ icd->sasc = sasc;
+ icd->parent = ici->v4l2_dev.dev;
+
+ snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+ sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address);
+
+ icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+ if (IS_ERR(icd->clk)) {
+ ret = PTR_ERR(icd->clk);
+ goto eclkreg;
+ }
+
+ ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier);
+ if (!ret)
+ return 0;
+
+ v4l2_clk_unregister(icd->clk);
+eclkreg:
+ icd->clk = NULL;
+ platform_device_unregister(sasc->pdev);
+ dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret);
+
+ return ret;
+}
+
+static void scan_async_host(struct soc_camera_host *ici)
+{
+ struct v4l2_async_subdev **asd;
+ int j;
+
+ for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) {
+ scan_async_group(ici, asd, ici->asd_sizes[j]);
+ asd += ici->asd_sizes[j];
+ }
}
#else
-#define soc_camera_init_i2c(icd, sdesc) (-ENODEV)
-#define soc_camera_free_i2c(icd) do {} while (0)
+#define soc_camera_i2c_init(icd, sdesc) (-ENODEV)
+#define soc_camera_i2c_free(icd) do {} while (0)
+#define scan_async_host(ici) do {} while (0)
#endif
-static int soc_camera_video_start(struct soc_camera_device *icd);
-static int video_dev_create(struct soc_camera_device *icd);
/* Called during host-driver probe */
-static int soc_camera_probe(struct soc_camera_device *icd)
+static int soc_camera_probe(struct soc_camera_host *ici,
+ struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
struct soc_camera_host_desc *shd = &sdesc->host_desc;
- struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
struct device *control = NULL;
- struct v4l2_subdev *sd;
- struct v4l2_mbus_framefmt mf;
int ret;
dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
@@ -1162,30 +1570,32 @@
if (ret < 0)
return ret;
- /* The camera could have been already on, try to reset */
- if (ssdd->reset)
- ssdd->reset(icd->pdev);
-
- mutex_lock(&ici->host_lock);
- ret = ici->ops->add(icd);
- mutex_unlock(&ici->host_lock);
- if (ret < 0)
- goto eadd;
-
/* Must have icd->vdev before registering the device */
ret = video_dev_create(icd);
if (ret < 0)
goto evdc;
+ /*
+ * ..._video_start() will create a device node, video_register_device()
+ * itself is protected against concurrent open() calls, but we also have
+ * to protect our data also during client probing.
+ */
+
/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
if (shd->board_info) {
- ret = soc_camera_init_i2c(icd, sdesc);
- if (ret < 0)
- goto eadddev;
+ ret = soc_camera_i2c_init(icd, sdesc);
+ if (ret < 0 && ret != -EPROBE_DEFER)
+ goto eadd;
} else if (!shd->add_device || !shd->del_device) {
ret = -EINVAL;
- goto eadddev;
+ goto eadd;
} else {
+ mutex_lock(&ici->clk_lock);
+ ret = ici->ops->clock_start(ici);
+ mutex_unlock(&ici->clk_lock);
+ if (ret < 0)
+ goto eadd;
+
if (shd->module_name)
ret = request_module(shd->module_name);
@@ -1206,81 +1616,49 @@
}
}
- sd = soc_camera_to_subdev(icd);
- sd->grp_id = soc_camera_grp_id(icd);
- v4l2_set_subdev_hostdata(sd, icd);
-
- ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
- if (ret < 0)
- goto ectrl;
-
- /* At this point client .probe() should have run already */
- ret = soc_camera_init_user_formats(icd);
- if (ret < 0)
- goto eiufmt;
-
- icd->field = V4L2_FIELD_ANY;
-
- /*
- * ..._video_start() will create a device node, video_register_device()
- * itself is protected against concurrent open() calls, but we also have
- * to protect our data.
- */
mutex_lock(&ici->host_lock);
-
- ret = soc_camera_video_start(icd);
- if (ret < 0)
- goto evidstart;
-
- /* Try to improve our guess of a reasonable window format */
- if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
- icd->user_width = mf.width;
- icd->user_height = mf.height;
- icd->colorspace = mf.colorspace;
- icd->field = mf.field;
- }
-
- ici->ops->remove(icd);
-
+ ret = soc_camera_probe_finish(icd);
mutex_unlock(&ici->host_lock);
+ if (ret < 0)
+ goto efinish;
return 0;
-evidstart:
- mutex_unlock(&ici->host_lock);
- soc_camera_free_user_formats(icd);
-eiufmt:
-ectrl:
+efinish:
if (shd->board_info) {
- soc_camera_free_i2c(icd);
+ soc_camera_i2c_free(icd);
} else {
shd->del_device(icd);
module_put(control->driver->owner);
- }
enodrv:
eadddev:
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+ }
+eadd:
video_device_release(icd->vdev);
icd->vdev = NULL;
+ if (icd->vdev) {
+ video_device_release(icd->vdev);
+ icd->vdev = NULL;
+ }
evdc:
- mutex_lock(&ici->host_lock);
- ici->ops->remove(icd);
- mutex_unlock(&ici->host_lock);
-eadd:
v4l2_ctrl_handler_free(&icd->ctrl_handler);
return ret;
}
/*
* This is called on device_unregister, which only means we have to disconnect
- * from the host, but not remove ourselves from the device list
+ * from the host, but not remove ourselves from the device list. With
+ * asynchronous client probing this can also be called without
+ * soc_camera_probe_finish() having run. Careful with clean up.
*/
static int soc_camera_remove(struct soc_camera_device *icd)
{
struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
struct video_device *vdev = icd->vdev;
- BUG_ON(!icd->parent);
-
v4l2_ctrl_handler_free(&icd->ctrl_handler);
if (vdev) {
video_unregister_device(vdev);
@@ -1288,15 +1666,27 @@
}
if (sdesc->host_desc.board_info) {
- soc_camera_free_i2c(icd);
+ soc_camera_i2c_free(icd);
} else {
- struct device_driver *drv = to_soc_camera_control(icd)->driver;
+ struct device *dev = to_soc_camera_control(icd);
+ struct device_driver *drv = dev ? dev->driver : NULL;
if (drv) {
sdesc->host_desc.del_device(icd);
module_put(drv->owner);
}
}
- soc_camera_free_user_formats(icd);
+
+ if (icd->num_user_formats)
+ soc_camera_free_user_formats(icd);
+
+ if (icd->clk) {
+ /* For the synchronous case */
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+ }
+
+ if (icd->sasc)
+ platform_device_unregister(icd->sasc->pdev);
return 0;
}
@@ -1372,8 +1762,8 @@
((!ici->ops->init_videobuf ||
!ici->ops->reqbufs) &&
!ici->ops->init_videobuf2) ||
- !ici->ops->add ||
- !ici->ops->remove ||
+ !ici->ops->clock_start ||
+ !ici->ops->clock_stop ||
!ici->ops->poll ||
!ici->v4l2_dev.dev)
return -EINVAL;
@@ -1407,7 +1797,18 @@
mutex_unlock(&list_lock);
mutex_init(&ici->host_lock);
- scan_add_host(ici);
+ mutex_init(&ici->clk_lock);
+
+ if (ici->asd_sizes)
+ /*
+ * No OF, host with a list of subdevices. Don't try to mix
+ * modes by initialising some groups statically and some
+ * dynamically!
+ */
+ scan_async_host(ici);
+ else
+ /* Legacy: static platform devices from board data */
+ scan_add_host(ici);
return 0;
@@ -1420,13 +1821,30 @@
/* Unregister all clients! */
void soc_camera_host_unregister(struct soc_camera_host *ici)
{
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd, *tmp;
+ struct soc_camera_async_client *sasc;
+ LIST_HEAD(notifiers);
+
+ mutex_lock(&list_lock);
+ list_del(&ici->list);
+ list_for_each_entry(icd, &devices, list)
+ if (icd->iface == ici->nr && icd->sasc) {
+ /* as long as we hold the device, sasc won't be freed */
+ get_device(icd->pdev);
+ list_add(&icd->sasc->list, ¬ifiers);
+ }
+ mutex_unlock(&list_lock);
+
+ list_for_each_entry(sasc, ¬ifiers, list) {
+ /* Must call unlocked to avoid AB-BA dead-lock */
+ v4l2_async_notifier_unregister(&sasc->notifier);
+ put_device(&sasc->pdev->dev);
+ }
mutex_lock(&list_lock);
- list_del(&ici->list);
- list_for_each_entry(icd, &devices, list)
- if (icd->iface == ici->nr && to_soc_camera_control(icd))
+ list_for_each_entry_safe(icd, tmp, &devices, list)
+ if (icd->iface == ici->nr)
soc_camera_remove(icd);
mutex_unlock(&list_lock);
@@ -1441,6 +1859,7 @@
struct soc_camera_device *ix;
int num = -1, i;
+ mutex_lock(&list_lock);
for (i = 0; i < 256 && num < 0; i++) {
num = i;
/* Check if this index is available on this interface */
@@ -1452,18 +1871,34 @@
}
}
- if (num < 0)
+ if (num < 0) {
/*
* ok, we have 256 cameras on this host...
* man, stay reasonable...
*/
+ mutex_unlock(&list_lock);
return -ENOMEM;
+ }
icd->devnum = num;
icd->use_count = 0;
icd->host_priv = NULL;
+ /*
+ * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting
+ * it again
+ */
+ i = to_platform_device(icd->pdev)->id;
+ if (i < 0)
+ /* One static (legacy) soc-camera platform device */
+ i = 0;
+ if (i >= MAP_MAX_NUM) {
+ mutex_unlock(&list_lock);
+ return -EBUSY;
+ }
+ set_bit(i, device_map);
list_add_tail(&icd->list, &devices);
+ mutex_unlock(&list_lock);
return 0;
}
@@ -1495,11 +1930,6 @@
.vidioc_s_selection = soc_camera_s_selection,
.vidioc_g_parm = soc_camera_g_parm,
.vidioc_s_parm = soc_camera_s_parm,
- .vidioc_g_chip_ident = soc_camera_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = soc_camera_g_register,
- .vidioc_s_register = soc_camera_s_register,
-#endif
};
static int video_dev_create(struct soc_camera_device *icd)
@@ -1512,12 +1942,10 @@
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
- vdev->parent = icd->pdev;
- vdev->current_norm = V4L2_STD_UNKNOWN;
+ vdev->v4l2_dev = &ici->v4l2_dev;
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
- vdev->tvnorms = V4L2_STD_UNKNOWN;
vdev->ctrl_handler = &icd->ctrl_handler;
vdev->lock = &ici->host_lock;
@@ -1537,6 +1965,7 @@
if (!icd->parent)
return -ENODEV;
+ video_set_drvdata(icd->vdev, icd);
ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
@@ -1563,6 +1992,12 @@
if (!icd)
return -ENOMEM;
+ /*
+ * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below
+ * regulator allocation is a dummy. They will be really requested later
+ * in soc_camera_async_bind(). Also note, that in that case regulators
+ * are attached to the I2C device and not to the camera platform device.
+ */
ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators,
ssdd->regulators);
if (ret < 0)
@@ -1587,11 +2022,25 @@
static int soc_camera_pdrv_remove(struct platform_device *pdev)
{
struct soc_camera_device *icd = platform_get_drvdata(pdev);
+ int i;
if (!icd)
return -EINVAL;
- list_del(&icd->list);
+ i = pdev->id;
+ if (i < 0)
+ i = 0;
+
+ /*
+ * In synchronous mode with static platform devices this is called in a
+ * loop from drivers/base/dd.c::driver_detach(), no parallel execution,
+ * no need to lock. In asynchronous case the caller -
+ * soc_camera_host_unregister() - already holds the lock
+ */
+ if (test_bit(i, device_map)) {
+ clear_bit(i, device_map);
+ list_del(&icd->list);
+ }
return 0;
}
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index 1b7a88c..ceaddfb 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -54,7 +54,7 @@
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on);
+ return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on);
}
static struct v4l2_subdev_core_ops platform_subdev_core_ops = {
@@ -137,7 +137,6 @@
struct soc_camera_platform_priv *priv;
struct soc_camera_platform_info *p = pdev->dev.platform_data;
struct soc_camera_device *icd;
- int ret;
if (!p)
return -EINVAL;
@@ -165,15 +164,7 @@
v4l2_set_subdevdata(&priv->subdev, p);
strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
- ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
- if (ret)
- goto evdrs;
-
- return ret;
-
-evdrs:
- platform_set_drvdata(pdev, NULL);
- return ret;
+ return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
}
static int soc_camera_platform_remove(struct platform_device *pdev)
@@ -183,7 +174,6 @@
p->icd->control = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c
new file mode 100644
index 0000000..cbd3a34
--- /dev/null
+++ b/drivers/media/platform/soc_camera/soc_scale_crop.c
@@ -0,0 +1,402 @@
+/*
+ * soc-camera generic scaling-cropping manipulation functions
+ *
+ * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/device.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+
+#include "soc_scale_crop.h"
+
+#ifdef DEBUG_GEOMETRY
+#define dev_geo dev_info
+#else
+#define dev_geo dev_dbg
+#endif
+
+/* Check if any dimension of r1 is smaller than respective one of r2 */
+static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ return r1->width < r2->width || r1->height < r2->height;
+}
+
+/* Check if r1 fails to cover r2 */
+static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ return r1->left > r2->left || r1->top > r2->top ||
+ r1->left + r1->width < r2->left + r2->width ||
+ r1->top + r1->height < r2->top + r2->height;
+}
+
+/* Get and store current client crop */
+int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
+{
+ struct v4l2_crop crop;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_crop, &crop);
+ if (!ret) {
+ *rect = crop.c;
+ return ret;
+ }
+
+ /* Camera driver doesn't support .g_crop(), assume default rectangle */
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (!ret)
+ *rect = cap.defrect;
+
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_client_g_rect);
+
+/* Client crop has changed, update our sub-rectangle to remain within the area */
+static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect)
+{
+ if (rect->width < subrect->width)
+ subrect->width = rect->width;
+
+ if (rect->height < subrect->height)
+ subrect->height = rect->height;
+
+ if (rect->left > subrect->left)
+ subrect->left = rect->left;
+ else if (rect->left + rect->width >
+ subrect->left + subrect->width)
+ subrect->left = rect->left + rect->width -
+ subrect->width;
+
+ if (rect->top > subrect->top)
+ subrect->top = rect->top;
+ else if (rect->top + rect->height >
+ subrect->top + subrect->height)
+ subrect->top = rect->top + rect->height -
+ subrect->height;
+}
+
+/*
+ * The common for both scaling and cropping iterative approach is:
+ * 1. try if the client can produce exactly what requested by the user
+ * 2. if (1) failed, try to double the client image until we get one big enough
+ * 3. if (2) failed, try to request the maximum image
+ */
+int soc_camera_client_s_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *crop, struct v4l2_crop *cam_crop,
+ struct v4l2_rect *target_rect, struct v4l2_rect *subrect)
+{
+ struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
+ struct device *dev = sd->v4l2_dev->dev;
+ struct v4l2_cropcap cap;
+ int ret;
+ unsigned int width, height;
+
+ v4l2_subdev_call(sd, video, s_crop, crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now cam_crop contains the current camera input rectangle, and it must
+ * be within camera cropcap bounds
+ */
+ if (!memcmp(rect, cam_rect, sizeof(*rect))) {
+ /* Even if camera S_CROP failed, but camera rectangle matches */
+ dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
+ rect->width, rect->height, rect->left, rect->top);
+ *target_rect = *cam_rect;
+ return 0;
+ }
+
+ /* Try to fix cropping, that camera hasn't managed to set */
+ dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top,
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* We need sensor maximum rectangle */
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ /* Put user requested rectangle within sensor bounds */
+ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
+ cap.bounds.width);
+ soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
+ cap.bounds.height);
+
+ /*
+ * Popular special case - some cameras can only handle fixed sizes like
+ * QVGA, VGA,... Take care to avoid infinite loop.
+ */
+ width = max(cam_rect->width, 2);
+ height = max(cam_rect->height, 2);
+
+ /*
+ * Loop as long as sensor is not covering the requested rectangle and
+ * is still within its bounds
+ */
+ while (!ret && (is_smaller(cam_rect, rect) ||
+ is_inside(cam_rect, rect)) &&
+ (cap.bounds.width > width || cap.bounds.height > height)) {
+
+ width *= 2;
+ height *= 2;
+
+ cam_rect->width = width;
+ cam_rect->height = height;
+
+ /*
+ * We do not know what capabilities the camera has to set up
+ * left and top borders. We could try to be smarter in iterating
+ * them, e.g., if camera current left is to the right of the
+ * target left, set it to the middle point between the current
+ * left and minimum left. But that would add too much
+ * complexity: we would have to iterate each border separately.
+ * Instead we just drop to the left and top bounds.
+ */
+ if (cam_rect->left > rect->left)
+ cam_rect->left = cap.bounds.left;
+
+ if (cam_rect->left + cam_rect->width < rect->left + rect->width)
+ cam_rect->width = rect->left + rect->width -
+ cam_rect->left;
+
+ if (cam_rect->top > rect->top)
+ cam_rect->top = cap.bounds.top;
+
+ if (cam_rect->top + cam_rect->height < rect->top + rect->height)
+ cam_rect->height = rect->top + rect->height -
+ cam_rect->top;
+
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ /* S_CROP must not modify the rectangle */
+ if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
+ /*
+ * The camera failed to configure a suitable cropping,
+ * we cannot use the current rectangle, set to max
+ */
+ *cam_rect = cap.bounds;
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ if (!ret) {
+ *target_rect = *cam_rect;
+ update_subrect(target_rect, subrect);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_client_s_crop);
+
+/* Iterative s_mbus_fmt, also updates cached client crop on success */
+static int client_s_fmt(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ unsigned int max_width, unsigned int max_height,
+ struct v4l2_mbus_framefmt *mf, bool host_can_scale)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
+ struct v4l2_cropcap cap;
+ bool host_1to1;
+ int ret;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
+
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ host_1to1 = true;
+ goto update_cache;
+ }
+
+ host_1to1 = false;
+ if (!host_can_scale)
+ goto update_cache;
+
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ if (max_width > cap.bounds.width)
+ max_width = cap.bounds.width;
+ if (max_height > cap.bounds.height)
+ max_height = cap.bounds.height;
+
+ /* Camera set a format, but geometry is not precise, try to improve */
+ tmp_w = mf->width;
+ tmp_h = mf->height;
+
+ /* width <= max_width && height <= max_height - guaranteed by try_fmt */
+ while ((width > tmp_w || height > tmp_h) &&
+ tmp_w < max_width && tmp_h < max_height) {
+ tmp_w = min(2 * tmp_w, max_width);
+ tmp_h = min(2 * tmp_h, max_height);
+ mf->width = tmp_w;
+ mf->height = tmp_h;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ dev_geo(dev, "Camera scaled to %ux%u\n",
+ mf->width, mf->height);
+ if (ret < 0) {
+ /* This shouldn't happen */
+ dev_err(dev, "Client failed to set format: %d\n", ret);
+ return ret;
+ }
+ }
+
+update_cache:
+ /* Update cache */
+ ret = soc_camera_client_g_rect(sd, rect);
+ if (ret < 0)
+ return ret;
+
+ if (host_1to1)
+ *subrect = *rect;
+ else
+ update_subrect(rect, subrect);
+
+ return 0;
+}
+
+/**
+ * @icd - soc-camera device
+ * @rect - camera cropping window
+ * @subrect - part of rect, sent to the user
+ * @mf - in- / output camera output window
+ * @width - on input: max host input width
+ * on output: user width, mapped back to input
+ * @height - on input: max host input height
+ * on output: user height, mapped back to input
+ * @host_can_scale - host can scale this pixel format
+ * @shift - shift, used for scaling
+ */
+int soc_camera_client_scale(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool host_can_scale, unsigned int shift)
+{
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt mf_tmp = *mf;
+ unsigned int scale_h, scale_v;
+ int ret;
+
+ /*
+ * 5. Apply iterative camera S_FMT for camera user window (also updates
+ * client crop cache and the imaginary sub-rectangle).
+ */
+ ret = client_s_fmt(icd, rect, subrect, *width, *height,
+ &mf_tmp, host_can_scale);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "5: camera scaled to %ux%u\n",
+ mf_tmp.width, mf_tmp.height);
+
+ /* 6. Retrieve camera output window (g_fmt) */
+
+ /* unneeded - it is already in "mf_tmp" */
+
+ /* 7. Calculate new client scales. */
+ scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp.width);
+ scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp.height);
+
+ mf->width = mf_tmp.width;
+ mf->height = mf_tmp.height;
+ mf->colorspace = mf_tmp.colorspace;
+
+ /*
+ * 8. Calculate new host crop - apply camera scales to previously
+ * updated "effective" crop.
+ */
+ *width = soc_camera_shift_scale(subrect->width, shift, scale_h);
+ *height = soc_camera_shift_scale(subrect->height, shift, scale_v);
+
+ dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
+
+ return 0;
+}
+EXPORT_SYMBOL(soc_camera_client_scale);
+
+/*
+ * Calculate real client output window by applying new scales to the current
+ * client crop. New scales are calculated from the requested output format and
+ * host crop, mapped backed onto the client input (subrect).
+ */
+void soc_camera_calc_client_output(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf,
+ unsigned int shift)
+{
+ struct device *dev = icd->parent;
+ unsigned int scale_v, scale_h;
+
+ if (subrect->width == rect->width &&
+ subrect->height == rect->height) {
+ /* No sub-cropping */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ return;
+ }
+
+ /* 1.-2. Current camera scales and subwin - cached. */
+
+ dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
+ subrect->width, subrect->height,
+ subrect->left, subrect->top);
+
+ /*
+ * 3. Calculate new combined scales from input sub-window to requested
+ * user window.
+ */
+
+ /*
+ * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
+ * (128x96) or larger than VGA. This and similar limitations have to be
+ * taken into account here.
+ */
+ scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width);
+ scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height);
+
+ dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
+
+ /*
+ * 4. Calculate desired client output window by applying combined scales
+ * to client (real) input window.
+ */
+ mf->width = soc_camera_shift_scale(rect->width, shift, scale_h);
+ mf->height = soc_camera_shift_scale(rect->height, shift, scale_v);
+}
+EXPORT_SYMBOL(soc_camera_calc_client_output);
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h
new file mode 100644
index 0000000..184a30d
--- /dev/null
+++ b/drivers/media/platform/soc_camera/soc_scale_crop.h
@@ -0,0 +1,47 @@
+/*
+ * soc-camera generic scaling-cropping manipulation functions
+ *
+ * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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 SOC_SCALE_CROP_H
+#define SOC_SCALE_CROP_H
+
+#include <linux/kernel.h>
+
+struct soc_camera_device;
+
+struct v4l2_crop;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+struct v4l2_rect;
+struct v4l2_subdev;
+
+static inline unsigned int soc_camera_shift_scale(unsigned int size,
+ unsigned int shift, unsigned int scale)
+{
+ return DIV_ROUND_CLOSEST(size << shift, scale);
+}
+
+#define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out)
+
+int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
+int soc_camera_client_s_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *crop, struct v4l2_crop *cam_crop,
+ struct v4l2_rect *target_rect, struct v4l2_rect *subrect);
+int soc_camera_client_scale(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool host_can_scale, unsigned int shift);
+void soc_camera_calc_client_output(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf,
+ unsigned int shift);
+
+#endif
diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c
index a2f7bdd..b557caf 100644
--- a/drivers/media/platform/timblogiw.c
+++ b/drivers/media/platform/timblogiw.c
@@ -239,13 +239,12 @@
struct video_device *vdev = video_devdata(file);
dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
- memset(cap, 0, sizeof(*cap));
strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
- strlcpy(cap->bus_info, vdev->name, sizeof(cap->bus_info));
- cap->version = TIMBLOGIW_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -834,11 +833,9 @@
goto err_request;
}
-
return 0;
err_request:
- platform_set_drvdata(pdev, NULL);
v4l2_device_unregister(&lw->v4l2_dev);
err_register:
kfree(lw);
@@ -858,8 +855,6 @@
kfree(lw);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index a794cd6..b4f9d03 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -17,7 +17,6 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-ctrls.h>
#include <media/ov7670.h>
#include <media/videobuf-dma-sg.h>
@@ -805,20 +804,6 @@
* The long list of v4l2 ioctl ops
*/
-static int viacam_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *ident)
-{
- struct via_camera *cam = priv;
-
- ident->ident = V4L2_IDENT_NONE;
- ident->revision = 0;
- if (v4l2_chip_match_host(&ident->match)) {
- ident->ident = V4L2_IDENT_VIA_VX855;
- return 0;
- }
- return sensor_call(cam, core, g_chip_ident, ident);
-}
-
/*
* Only one input.
*/
@@ -852,6 +837,12 @@
return 0;
}
+static int viacam_g_std(struct file *filp, void *priv, v4l2_std_id *std)
+{
+ *std = V4L2_STD_NTSC_M;
+ return 0;
+}
+
/*
* Video format stuff. Here is our default format until
* user space messes with things.
@@ -1174,11 +1165,11 @@
static const struct v4l2_ioctl_ops viacam_ioctl_ops = {
- .vidioc_g_chip_ident = viacam_g_chip_ident,
.vidioc_enum_input = viacam_enum_input,
.vidioc_g_input = viacam_g_input,
.vidioc_s_input = viacam_s_input,
.vidioc_s_std = viacam_s_std,
+ .vidioc_g_std = viacam_g_std,
.vidioc_enum_fmt_vid_cap = viacam_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = viacam_try_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = viacam_g_fmt_vid_cap,
@@ -1266,7 +1257,6 @@
.name = "via-camera",
.minor = -1,
.tvnorms = V4L2_STD_NTSC_M,
- .current_norm = V4L2_STD_NTSC_M,
.fops = &viacam_fops,
.ioctl_ops = &viacam_ioctl_ops,
.release = video_device_release_empty, /* Check this */
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
index 4c9ae76..21db23b 100644
--- a/drivers/media/radio/radio-keene.c
+++ b/drivers/media/radio/radio-keene.c
@@ -93,7 +93,7 @@
/* If bit 4 is set, then tune to the frequency.
If bit 3 is set, then unmute; if bit 2 is set, then mute.
If bit 1 is set, then enter idle mode; if bit 0 is set,
- then enter transit mode.
+ then enter transmit mode.
*/
radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) |
(freq ? 0x10 : 0);
@@ -350,7 +350,6 @@
radio->pa = 118;
radio->tx = 0x32;
radio->stereo = true;
- radio->curfreq = 95.16 * FREQ_MUL;
if (hdl->error) {
retval = hdl->error;
@@ -383,6 +382,10 @@
video_set_drvdata(&radio->vdev, radio);
set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
+ /* at least 11ms is needed in order to settle hardware */
+ msleep(20);
+ keene_cmd_main(radio, 95.16 * FREQ_MUL, false);
+
retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
if (retval < 0) {
dev_err(&intf->dev, "could not register video device\n");
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index adfcc61..6f4318f 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -27,6 +27,8 @@
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include "lm7000.h"
MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
@@ -44,10 +46,11 @@
struct fmi
{
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
struct video_device vdev;
int io;
bool mute;
- unsigned long curfreq; /* freq in kHz */
+ u32 curfreq; /* freq in kHz */
struct mutex lock;
};
@@ -55,8 +58,8 @@
static struct pnp_dev *dev;
bool pnp_attached;
-#define RSF16_MINFREQ (87 * 16000)
-#define RSF16_MAXFREQ (108 * 16000)
+#define RSF16_MINFREQ (87U * 16000)
+#define RSF16_MAXFREQ (108U * 16000)
#define FMI_BIT_TUN_CE (1 << 0)
#define FMI_BIT_TUN_CLK (1 << 1)
@@ -115,13 +118,22 @@
return (res & 2) ? 0 : 0xFFFF;
}
+static void fmi_set_freq(struct fmi *fmi)
+{
+ fmi->curfreq = clamp(fmi->curfreq, RSF16_MINFREQ, RSF16_MAXFREQ);
+ /* rounding in steps of 800 to match the freq
+ that will be used */
+ lm7000_set_freq((fmi->curfreq / 800) * 800, fmi, fmi_set_pins);
+}
+
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
strlcpy(v->card, "SF16-FMI/FMP/FMD radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ strlcpy(v->bus_info, "ISA:radio-sf16fmi", sizeof(v->bus_info));
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -157,12 +169,10 @@
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
- if (f->frequency < RSF16_MINFREQ ||
- f->frequency > RSF16_MAXFREQ)
- return -EINVAL;
- /* rounding in steps of 800 to match the freq
- that will be used */
- lm7000_set_freq((f->frequency / 800) * 800, fmi, fmi_set_pins);
+
+ fmi->curfreq = f->frequency;
+ fmi_set_freq(fmi);
+
return 0;
}
@@ -178,74 +188,31 @@
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int fmi_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fmi *fmi = video_drvdata(file);
+ struct fmi *fmi = container_of(ctrl->handler, struct fmi, hdl);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- ctrl->value = fmi->mute;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct fmi *fmi = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
+ if (ctrl->val)
fmi_mute(fmi);
else
fmi_unmute(fmi);
- fmi->mute = ctrl->value;
+ fmi->mute = ctrl->val;
return 0;
}
return -EINVAL;
}
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- const struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
+static const struct v4l2_ctrl_ops fmi_ctrl_ops = {
+ .s_ctrl = fmi_s_ctrl,
+};
static const struct v4l2_file_operations fmi_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
};
@@ -253,15 +220,11 @@
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/* ladis: this is my card. does any other types exist? */
@@ -311,6 +274,7 @@
{
struct fmi *fmi = &fmi_card;
struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
+ struct v4l2_ctrl_handler *hdl = &fmi->hdl;
int res, i;
int probe_ports[] = { 0, 0x284, 0x384 };
@@ -363,19 +327,35 @@
return res;
}
+ v4l2_ctrl_handler_init(hdl, 1);
+ v4l2_ctrl_new_std(hdl, &fmi_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ v4l2_dev->ctrl_handler = hdl;
+ if (hdl->error) {
+ res = hdl->error;
+ v4l2_err(v4l2_dev, "Could not register controls\n");
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(v4l2_dev);
+ return res;
+ }
+
strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name));
fmi->vdev.v4l2_dev = v4l2_dev;
fmi->vdev.fops = &fmi_fops;
fmi->vdev.ioctl_ops = &fmi_ioctl_ops;
fmi->vdev.release = video_device_release_empty;
+ set_bit(V4L2_FL_USE_FH_PRIO, &fmi->vdev.flags);
video_set_drvdata(&fmi->vdev, fmi);
mutex_init(&fmi->lock);
- /* mute card - prevents noisy bootups */
- fmi_mute(fmi);
+ /* mute card and set default frequency */
+ fmi->mute = 1;
+ fmi->curfreq = RSF16_MINFREQ;
+ fmi_set_freq(fmi);
if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
+ v4l2_ctrl_handler_free(hdl);
v4l2_device_unregister(v4l2_dev);
release_region(fmi->io, 2);
if (pnp_attached)
@@ -391,6 +371,7 @@
{
struct fmi *fmi = &fmi_card;
+ v4l2_ctrl_handler_free(&fmi->hdl);
video_unregister_device(&fmi->vdev);
v4l2_device_unregister(&fmi->v4l2_dev);
release_region(fmi->io, 2);
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index 9dc8baf..9c9084c 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -1018,16 +1018,6 @@
return retval;
}
-static int si476x_radio_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
- v4l2_chip_match_host(&chip->match))
- return 0;
- return -EINVAL;
-}
-
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int si476x_radio_g_register(struct file *file, void *fh,
struct v4l2_dbg_register *reg)
@@ -1203,7 +1193,6 @@
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = si476x_radio_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = si476x_radio_g_register,
.vidioc_s_register = si476x_radio_s_register,
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 38d563d..036e2f5 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -39,6 +39,9 @@
#include <linux/i2c.h> /* I2C */
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#define DRIVER_VERSION "0.0.2"
@@ -57,8 +60,8 @@
/* Frequency limits in MHz -- these are European values. For Japanese
devices, that would be 76000 and 91000. */
-#define FREQ_MIN 87500
-#define FREQ_MAX 108000
+#define FREQ_MIN 87500U
+#define FREQ_MAX 108000U
#define FREQ_MUL 16
/* TEA5764 registers */
@@ -138,8 +141,10 @@
static int use_xtal = RADIO_TEA5764_XTAL;
struct tea5764_device {
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
struct i2c_client *i2c_client;
- struct video_device *videodev;
+ struct video_device vdev;
struct tea5764_regs regs;
struct mutex mutex;
};
@@ -187,18 +192,6 @@
return 0;
}
-/* V4L2 code related */
-static struct v4l2_queryctrl radio_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }
-};
-
static void tea5764_power_up(struct tea5764_device *radio)
{
struct tea5764_regs *r = &radio->regs;
@@ -291,23 +284,19 @@
tea5764_i2c_write(radio);
}
-static int tea5764_is_muted(struct tea5764_device *radio)
-{
- return radio->regs.tnctrl & TEA5764_TNCTRL_MU;
-}
-
/* V4L2 vidioc */
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
struct tea5764_device *radio = video_drvdata(file);
- struct video_device *dev = radio->videodev;
+ struct video_device *dev = &radio->vdev;
strlcpy(v->driver, dev->dev.driver->name, sizeof(v->driver));
strlcpy(v->card, dev->name, sizeof(v->card));
snprintf(v->bus_info, sizeof(v->bus_info),
"I2C:%s", dev_name(&dev->dev));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -320,8 +309,7 @@
if (v->index > 0)
return -EINVAL;
- memset(v, 0, sizeof(*v));
- strcpy(v->name, "FM");
+ strlcpy(v->name, "FM", sizeof(v->name));
v->type = V4L2_TUNER_RADIO;
tea5764_i2c_read(radio);
v->rangelow = FREQ_MIN * FREQ_MUL;
@@ -354,19 +342,23 @@
const struct v4l2_frequency *f)
{
struct tea5764_device *radio = video_drvdata(file);
+ unsigned freq = f->frequency;
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
- if (f->frequency == 0) {
+ if (freq == 0) {
/* We special case this as a power down control. */
tea5764_power_down(radio);
+ /* Yes, that's what is returned in this case. This
+ whole special case is non-compliant and should really
+ be replaced with something better, but changing this
+ might well break code that depends on this behavior.
+ So we keep it as-is. */
+ return -EINVAL;
}
- if (f->frequency < (FREQ_MIN * FREQ_MUL))
- return -EINVAL;
- if (f->frequency > (FREQ_MAX * FREQ_MUL))
- return -EINVAL;
+ clamp(freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
tea5764_power_up(radio);
- tea5764_tune(radio, (f->frequency * 125) / 2);
+ tea5764_tune(radio, (freq * 125) / 2);
return 0;
}
@@ -379,7 +371,6 @@
if (f->tuner != 0)
return -EINVAL;
tea5764_i2c_read(radio);
- memset(f, 0, sizeof(*f));
f->type = V4L2_TUNER_RADIO;
if (r->tnctrl & TEA5764_TNCTRL_PUPD0)
f->frequency = (tea5764_get_freq(radio) * 2) / 125;
@@ -389,83 +380,29 @@
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int tea5764_s_ctrl(struct v4l2_ctrl *ctrl)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
- if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct tea5764_device *radio = video_drvdata(file);
+ struct tea5764_device *radio =
+ container_of(ctrl->handler, struct tea5764_device, ctrl_handler);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- tea5764_i2c_read(radio);
- ctrl->value = tea5764_is_muted(radio) ? 1 : 0;
+ tea5764_mute(radio, ctrl->val);
return 0;
}
return -EINVAL;
}
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct tea5764_device *radio = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- tea5764_mute(radio, ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- if (i != 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index > 1)
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- const struct v4l2_audio *a)
-{
- if (a->index != 0)
- return -EINVAL;
-
- return 0;
-}
+static const struct v4l2_ctrl_ops tea5764_ctrl_ops = {
+ .s_ctrl = tea5764_s_ctrl,
+};
/* File system interface */
static const struct v4l2_file_operations tea5764_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
};
@@ -473,15 +410,11 @@
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/* V4L2 interface */
@@ -489,7 +422,7 @@
.name = "TEA5764 FM-Radio",
.fops = &tea5764_fops,
.ioctl_ops = &tea5764_ioctl_ops,
- .release = video_device_release,
+ .release = video_device_release_empty,
};
/* I2C probe: check if the device exists and register with v4l if it is */
@@ -497,6 +430,8 @@
const struct i2c_device_id *id)
{
struct tea5764_device *radio;
+ struct v4l2_device *v4l2_dev;
+ struct v4l2_ctrl_handler *hdl;
struct tea5764_regs *r;
int ret;
@@ -505,31 +440,45 @@
if (!radio)
return -ENOMEM;
+ v4l2_dev = &radio->v4l2_dev;
+ ret = v4l2_device_register(&client->dev, v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "could not register v4l2_device\n");
+ goto errfr;
+ }
+
+ hdl = &radio->ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 1);
+ v4l2_ctrl_new_std(hdl, &tea5764_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ v4l2_dev->ctrl_handler = hdl;
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_err(v4l2_dev, "Could not register controls\n");
+ goto errunreg;
+ }
+
mutex_init(&radio->mutex);
radio->i2c_client = client;
ret = tea5764_i2c_read(radio);
if (ret)
- goto errfr;
+ goto errunreg;
r = &radio->regs;
PDEBUG("chipid = %04X, manid = %04X", r->chipid, r->manid);
if (r->chipid != TEA5764_CHIPID ||
(r->manid & 0x0fff) != TEA5764_MANID) {
PWARN("This chip is not a TEA5764!");
ret = -EINVAL;
- goto errfr;
+ goto errunreg;
}
- radio->videodev = video_device_alloc();
- if (!(radio->videodev)) {
- ret = -ENOMEM;
- goto errfr;
- }
- memcpy(radio->videodev, &tea5764_radio_template,
- sizeof(tea5764_radio_template));
+ radio->vdev = tea5764_radio_template;
i2c_set_clientdata(client, radio);
- video_set_drvdata(radio->videodev, radio);
- radio->videodev->lock = &radio->mutex;
+ video_set_drvdata(&radio->vdev, radio);
+ radio->vdev.lock = &radio->mutex;
+ radio->vdev.v4l2_dev = v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
/* initialize and power off the chip */
tea5764_i2c_read(radio);
@@ -537,16 +486,17 @@
tea5764_mute(radio, 1);
tea5764_power_down(radio);
- ret = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
+ ret = video_register_device(&radio->vdev, VFL_TYPE_RADIO, radio_nr);
if (ret < 0) {
PWARN("Could not register video device!");
- goto errrel;
+ goto errunreg;
}
PINFO("registered.");
return 0;
-errrel:
- video_device_release(radio->videodev);
+errunreg:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(v4l2_dev);
errfr:
kfree(radio);
return ret;
@@ -559,7 +509,9 @@
PDEBUG("remove");
if (radio) {
tea5764_power_down(radio);
- video_unregister_device(radio->videodev);
+ video_unregister_device(&radio->vdev);
+ v4l2_ctrl_handler_free(&radio->ctrl_handler);
+ v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio);
}
return 0;
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index bb7b143..0817964 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -19,6 +19,8 @@
#include <linux/io.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
@@ -44,7 +46,8 @@
strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver));
strlcpy(v->card, "Timberdale Radio", sizeof(v->card));
snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME);
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -62,34 +65,6 @@
return v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v);
}
-static int timbradio_vidioc_g_input(struct file *filp, void *priv,
- unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int timbradio_vidioc_s_input(struct file *filp, void *priv,
- unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int timbradio_vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int timbradio_vidioc_s_audio(struct file *file, void *priv,
- const struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
static int timbradio_vidioc_s_frequency(struct file *file, void *priv,
const struct v4l2_frequency *f)
{
@@ -104,44 +79,22 @@
return v4l2_subdev_call(tr->sd_tuner, tuner, g_frequency, f);
}
-static int timbradio_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct timbradio *tr = video_drvdata(file);
- return v4l2_subdev_call(tr->sd_dsp, core, queryctrl, qc);
-}
-
-static int timbradio_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct timbradio *tr = video_drvdata(file);
- return v4l2_subdev_call(tr->sd_dsp, core, g_ctrl, ctrl);
-}
-
-static int timbradio_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct timbradio *tr = video_drvdata(file);
- return v4l2_subdev_call(tr->sd_dsp, core, s_ctrl, ctrl);
-}
-
static const struct v4l2_ioctl_ops timbradio_ioctl_ops = {
.vidioc_querycap = timbradio_vidioc_querycap,
.vidioc_g_tuner = timbradio_vidioc_g_tuner,
.vidioc_s_tuner = timbradio_vidioc_s_tuner,
.vidioc_g_frequency = timbradio_vidioc_g_frequency,
.vidioc_s_frequency = timbradio_vidioc_s_frequency,
- .vidioc_g_input = timbradio_vidioc_g_input,
- .vidioc_s_input = timbradio_vidioc_s_input,
- .vidioc_g_audio = timbradio_vidioc_g_audio,
- .vidioc_s_audio = timbradio_vidioc_s_audio,
- .vidioc_queryctrl = timbradio_vidioc_queryctrl,
- .vidioc_g_ctrl = timbradio_vidioc_g_ctrl,
- .vidioc_s_ctrl = timbradio_vidioc_s_ctrl
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct v4l2_file_operations timbradio_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
};
@@ -173,6 +126,7 @@
tr->video_dev.release = video_device_release_empty;
tr->video_dev.minor = -1;
tr->video_dev.lock = &tr->lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &tr->video_dev.flags);
strlcpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name));
err = v4l2_device_register(NULL, &tr->v4l2_dev);
@@ -181,6 +135,15 @@
tr->video_dev.v4l2_dev = &tr->v4l2_dev;
+ tr->sd_tuner = v4l2_i2c_new_subdev_board(&tr->v4l2_dev,
+ i2c_get_adapter(pdata->i2c_adapter), pdata->tuner, NULL);
+ tr->sd_dsp = v4l2_i2c_new_subdev_board(&tr->v4l2_dev,
+ i2c_get_adapter(pdata->i2c_adapter), pdata->dsp, NULL);
+ if (tr->sd_tuner == NULL || tr->sd_dsp == NULL)
+ goto err_video_req;
+
+ tr->v4l2_dev.ctrl_handler = tr->sd_dsp->ctrl_handler;
+
err = video_register_device(&tr->video_dev, VFL_TYPE_RADIO, -1);
if (err) {
dev_err(&pdev->dev, "Error reg video\n");
@@ -193,7 +156,6 @@
return 0;
err_video_req:
- video_device_release_empty(&tr->video_dev);
v4l2_device_unregister(&tr->v4l2_dev);
err:
dev_err(&pdev->dev, "Failed to register: %d\n", err);
@@ -206,10 +168,7 @@
struct timbradio *tr = platform_get_drvdata(pdev);
video_unregister_device(&tr->video_dev);
- video_device_release_empty(&tr->video_dev);
-
v4l2_device_unregister(&tr->v4l2_dev);
-
return 0;
}
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index 06c06cc..ec805b0 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -25,7 +25,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#define DRIVER_NAME "saa7706h"
@@ -127,6 +127,7 @@
struct saa7706h_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
unsigned muted;
};
@@ -317,51 +318,32 @@
return err;
}
-static int saa7706h_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+static int saa7706h_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
- return -EINVAL;
-}
-
-static int saa7706h_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct saa7706h_state *state = to_state(sd);
+ struct saa7706h_state *state =
+ container_of(ctrl->handler, struct saa7706h_state, hdl);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->muted;
- return 0;
+ if (ctrl->val)
+ return saa7706h_mute(&state->sd);
+ return saa7706h_unmute(&state->sd);
}
return -EINVAL;
}
-static int saa7706h_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- return saa7706h_mute(sd);
- return saa7706h_unmute(sd);
- }
- return -EINVAL;
-}
-
-static int saa7706h_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7706H, 0);
-}
+static const struct v4l2_ctrl_ops saa7706h_ctrl_ops = {
+ .s_ctrl = saa7706h_s_ctrl,
+};
static const struct v4l2_subdev_core_ops saa7706h_core_ops = {
- .g_chip_ident = saa7706h_g_chip_ident,
- .queryctrl = saa7706h_queryctrl,
- .g_ctrl = saa7706h_g_ctrl,
- .s_ctrl = saa7706h_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_ops saa7706h_ops = {
@@ -393,13 +375,20 @@
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &saa7706h_ops);
+ v4l2_ctrl_handler_init(&state->hdl, 4);
+ v4l2_ctrl_new_std(&state->hdl, &saa7706h_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ sd->ctrl_handler = &state->hdl;
+ err = state->hdl.error;
+ if (err)
+ goto err;
+
/* check the rom versions */
err = saa7706h_get_reg16(sd, SAA7706H_DSP1_ROM_VER);
if (err < 0)
goto err;
if (err != SUPPORTED_DSP1_ROM_VER)
v4l2_warn(sd, "Unknown DSP1 ROM code version: 0x%x\n", err);
-
state->muted = 1;
/* startup in a muted state */
@@ -411,6 +400,7 @@
err:
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
kfree(to_state(sd));
printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", err);
@@ -421,9 +411,11 @@
static int saa7706h_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct saa7706h_state *state = to_state(sd);
saa7706h_mute(sd);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 82c6c94..06ac692 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -25,14 +25,13 @@
#include <linux/slab.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#define DRIVER_NAME "tef6862"
#define FREQ_MUL 16000
-#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10)
-#define TEF6862_HI_FREQ (108 * FREQ_MUL)
+#define TEF6862_LO_FREQ (875U * FREQ_MUL / 10)
+#define TEF6862_HI_FREQ (108U * FREQ_MUL)
/* Write mode sub addresses */
#define WM_SUB_BANDWIDTH 0x0
@@ -105,6 +104,7 @@
{
struct tef6862_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ unsigned freq = f->frequency;
u16 pll;
u8 i2cmsg[3];
int err;
@@ -112,7 +112,8 @@
if (f->tuner != 0)
return -EINVAL;
- pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
+ clamp(freq, TEF6862_LO_FREQ, TEF6862_HI_FREQ);
+ pll = 1964 + ((freq - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM;
i2cmsg[1] = (pll >> 8) & 0xff;
i2cmsg[2] = pll & 0xff;
@@ -121,7 +122,7 @@
if (err != sizeof(i2cmsg))
return err < 0 ? err : -EIO;
- state->freq = f->frequency;
+ state->freq = freq;
return 0;
}
@@ -136,14 +137,6 @@
return 0;
}
-static int tef6862_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0);
-}
-
static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = {
.g_tuner = tef6862_g_tuner,
.s_tuner = tef6862_s_tuner,
@@ -151,12 +144,7 @@
.g_frequency = tef6862_g_frequency,
};
-static const struct v4l2_subdev_core_ops tef6862_core_ops = {
- .g_chip_ident = tef6862_g_chip_ident,
-};
-
static const struct v4l2_subdev_ops tef6862_ops = {
- .core = &tef6862_core_ops,
.tuner = &tef6862_tuner_ops,
};
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
index aac0f02..a587c9b 100644
--- a/drivers/media/radio/wl128x/fmdrv.h
+++ b/drivers/media/radio/wl128x/fmdrv.h
@@ -30,6 +30,7 @@
#include <linux/timer.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#define FM_DRV_VERSION "0.1.1"
@@ -202,6 +203,7 @@
/* FM driver operation structure */
struct fmdev {
struct video_device *radio_dev; /* V4L2 video device pointer */
+ struct v4l2_device v4l2_dev; /* V4L2 top level struct */
struct snd_card *card; /* Card which holds FM mixer controls */
u16 asci_id;
spinlock_t rds_buff_lock; /* To protect access to RDS buffer */
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
index a002234..253f307 100644
--- a/drivers/media/radio/wl128x/fmdrv_common.c
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -715,7 +715,7 @@
struct fm_rdsdata_format rds_fmt;
struct fm_rds *rds = &fmdev->rx.rds;
unsigned long group_idx, flags;
- u8 *rds_data, meta_data, tmpbuf[3];
+ u8 *rds_data, meta_data, tmpbuf[FM_RDS_BLK_SIZE];
u8 type, blk_idx;
u16 cur_picode;
u32 rds_len;
@@ -1073,6 +1073,7 @@
u8 __user *buf, size_t count)
{
u32 block_count;
+ u8 tmpbuf[FM_RDS_BLK_SIZE];
unsigned long flags;
int ret;
@@ -1087,29 +1088,32 @@
}
/* Calculate block count from byte count */
- count /= 3;
+ count /= FM_RDS_BLK_SIZE;
block_count = 0;
ret = 0;
- spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
-
while (block_count < count) {
- if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx)
- break;
+ spin_lock_irqsave(&fmdev->rds_buff_lock, flags);
- if (copy_to_user(buf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx],
- FM_RDS_BLK_SIZE))
+ if (fmdev->rx.rds.wr_idx == fmdev->rx.rds.rd_idx) {
+ spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
break;
-
+ }
+ memcpy(tmpbuf, &fmdev->rx.rds.buff[fmdev->rx.rds.rd_idx],
+ FM_RDS_BLK_SIZE);
fmdev->rx.rds.rd_idx += FM_RDS_BLK_SIZE;
if (fmdev->rx.rds.rd_idx >= fmdev->rx.rds.buf_size)
fmdev->rx.rds.rd_idx = 0;
+ spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
+
+ if (copy_to_user(buf, tmpbuf, FM_RDS_BLK_SIZE))
+ break;
+
block_count++;
buf += FM_RDS_BLK_SIZE;
ret += FM_RDS_BLK_SIZE;
}
- spin_unlock_irqrestore(&fmdev->rds_buff_lock, flags);
return ret;
}
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index 5dec323..b55012c 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -533,6 +533,11 @@
struct v4l2_ctrl *ctrl;
int ret;
+ strlcpy(fmdev->v4l2_dev.name, FM_DRV_NAME, sizeof(fmdev->v4l2_dev.name));
+ ret = v4l2_device_register(NULL, &fmdev->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
/* Init mutex for core locking */
mutex_init(&fmdev->mutex);
@@ -549,6 +554,7 @@
video_set_drvdata(gradio_dev, fmdev);
gradio_dev->lock = &fmdev->mutex;
+ gradio_dev->v4l2_dev = &fmdev->v4l2_dev;
/* Register with V4L2 subsystem as RADIO device */
if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
@@ -611,5 +617,7 @@
/* Unregister RADIO device from V4L2 subsystem */
video_unregister_device(gradio_dev);
+ v4l2_device_unregister(&fmdev->v4l2_dev);
+
return fmdev;
}
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 8b82ae9..07aacfa 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -178,7 +178,6 @@
return 0;
err_request_irq:
- platform_set_drvdata(pdev, NULL);
rc_unregister_device(rcdev);
rcdev = NULL;
err_register_rc_device:
@@ -196,7 +195,6 @@
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
- platform_set_drvdata(pdev, NULL);
rc_unregister_device(gpio_dev->rcdev);
gpio_free(gpio_dev->gpio_nr);
kfree(gpio_dev);
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 5ab94ea..b1cde8c 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -20,6 +20,7 @@
rc-budget-ci-old.o \
rc-cinergy-1400.o \
rc-cinergy.o \
+ rc-delock-61959.o \
rc-dib0700-nec.o \
rc-dib0700-rc5.o \
rc-digitalnow-tinytwin.o \
diff --git a/drivers/media/rc/keymaps/rc-delock-61959.c b/drivers/media/rc/keymaps/rc-delock-61959.c
new file mode 100644
index 0000000..01bed86
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-delock-61959.c
@@ -0,0 +1,83 @@
+/* rc-delock-61959.c - Keytable for Delock
+ *
+ * Copyright (c) 2013 by Jakob Haufe <sur5r@sur5r.net>
+ *
+ * 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 <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Keytable for remote provided with Delock 61959
+ */
+static struct rc_map_table delock_61959[] = {
+ { 0x866b16, KEY_POWER2 }, /* Power */
+ { 0x866b0c, KEY_POWER }, /* Shut Down */
+
+ { 0x866b00, KEY_1},
+ { 0x866b01, KEY_2},
+ { 0x866b02, KEY_3},
+ { 0x866b03, KEY_4},
+ { 0x866b04, KEY_5},
+ { 0x866b05, KEY_6},
+ { 0x866b06, KEY_7},
+ { 0x866b07, KEY_8},
+ { 0x866b08, KEY_9},
+ { 0x866b14, KEY_0},
+
+ { 0x866b0a, KEY_ZOOM}, /* Full Screen */
+ { 0x866b10, KEY_CAMERA}, /* Photo */
+ { 0x866b0e, KEY_CHANNEL}, /* circular arrow / Recall */
+ { 0x866b13, KEY_ESC}, /* Back */
+
+ { 0x866b20, KEY_UP},
+ { 0x866b21, KEY_DOWN},
+ { 0x866b42, KEY_LEFT},
+ { 0x866b43, KEY_RIGHT},
+ { 0x866b0b, KEY_OK},
+
+ { 0x866b11, KEY_CHANNELUP},
+ { 0x866b1b, KEY_CHANNELDOWN},
+
+ { 0x866b12, KEY_VOLUMEUP},
+ { 0x866b48, KEY_VOLUMEDOWN},
+ { 0x866b44, KEY_MUTE},
+
+ { 0x866b1a, KEY_RECORD},
+ { 0x866b41, KEY_PLAY},
+ { 0x866b40, KEY_STOP},
+ { 0x866b19, KEY_PAUSE},
+ { 0x866b1c, KEY_FASTFORWARD}, /* >> / FWD */
+ { 0x866b1e, KEY_REWIND}, /* << / REW */
+
+};
+
+static struct rc_map_list delock_61959_map = {
+ .map = {
+ .scan = delock_61959,
+ .size = ARRAY_SIZE(delock_61959),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_DELOCK_61959,
+ }
+};
+
+static int __init init_rc_map_delock_61959(void)
+{
+ return rc_map_register(&delock_61959_map);
+}
+
+static void __exit exit_rc_map_delock_61959(void)
+{
+ rc_map_unregister(&delock_61959_map);
+}
+
+module_init(init_rc_map_delock_61959)
+module_exit(exit_rc_map_delock_61959)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jakob Haufe <sur5r@sur5r.net>");
+MODULE_DESCRIPTION("Delock 61959 remote keytable");
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index 4835021..1c23666 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -364,8 +364,8 @@
}
if (len <= 0)
return;
- if (len > NUM_REGS)
- len = NUM_REGS;
+ if (len > NUM_REGS - r)
+ len = NUM_REGS - r;
tuner_dbg("%s: prev reg=%02x len=%d: %*ph\n",
__func__, r + REG_SHADOW_START, len, len, val);
@@ -1857,9 +1857,9 @@
int reg18, reg19, reg1f;
if (priv->cfg->xtal > 24000000)
- ring_ref = priv->cfg->xtal / 2;
+ ring_ref = priv->cfg->xtal / 2000;
else
- ring_ref = priv->cfg->xtal;
+ ring_ref = priv->cfg->xtal / 1000;
n_ring = 15;
for (n = 0; n < 16; n++) {
@@ -2256,7 +2256,6 @@
mutex_unlock(&r820t_list_mutex);
- kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
return 0;
@@ -2311,8 +2310,6 @@
break;
}
- memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops, sizeof(r820t_tuner_ops));
-
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -2327,15 +2324,14 @@
tuner_info("Rafael Micro r820t successfully identified\n");
- fe->tuner_priv = priv;
- memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
mutex_unlock(&r820t_list_mutex);
+ memcpy(&fe->ops.tuner_ops, &r820t_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
return fe;
err:
if (fe->ops.i2c_gate_ctrl)
diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig
index 0a7d520..cfe8056 100644
--- a/drivers/media/usb/Kconfig
+++ b/drivers/media/usb/Kconfig
@@ -1,6 +1,7 @@
+if USB && MEDIA_SUPPORT
+
menuconfig MEDIA_USB_SUPPORT
bool "Media USB Adapters"
- depends on USB && MEDIA_SUPPORT
help
Enable media drivers for USB bus.
If you have such devices, say Y.
@@ -17,6 +18,7 @@
source "drivers/media/usb/stkwebcam/Kconfig"
source "drivers/media/usb/s2255/Kconfig"
source "drivers/media/usb/sn9c102/Kconfig"
+source "drivers/media/usb/usbtv/Kconfig"
endif
if MEDIA_ANALOG_TV_SUPPORT
@@ -52,3 +54,4 @@
endif
endif #MEDIA_USB_SUPPORT
+endif #USB
diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile
index 7f51d7e..0935f47 100644
--- a/drivers/media/usb/Makefile
+++ b/drivers/media/usb/Makefile
@@ -20,3 +20,4 @@
obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
obj-$(CONFIG_VIDEO_TM6000) += tm6000/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_USBTV) += usbtv/
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 75ac994..f615454 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -36,7 +36,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-chip-ident.h>
#include <media/tuner.h>
#include "au0828.h"
#include "au0828-reg.h"
@@ -1638,26 +1637,6 @@
return 0;
}
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
-
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = V4L2_IDENT_AU0828;
- return 0;
- }
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
- if (chip->ident == V4L2_IDENT_NONE)
- return -EINVAL;
-
- return 0;
-}
-
static int vidioc_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *cc)
{
@@ -1779,16 +1758,8 @@
struct au0828_fh *fh = priv;
struct au0828_dev *dev = fh->dev;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- default:
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- }
-
reg->val = au0828_read(dev, reg->reg);
+ reg->size = 1;
return 0;
}
@@ -1798,14 +1769,6 @@
struct au0828_fh *fh = priv;
struct au0828_dev *dev = fh->dev;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- default:
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- }
return au0828_writereg(dev, reg->reg, reg->val);
}
#endif
@@ -1943,7 +1906,6 @@
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
.vidioc_log_status = vidioc_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index f548db8..2f63029 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -1840,7 +1840,6 @@
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_log_status = vidioc_log_status,
- .vidioc_g_chip_ident = cx231xx_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = cx231xx_g_register,
.vidioc_s_register = cx231xx_s_register,
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 235ba65..89de00b 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -35,7 +35,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
#include "cx231xx.h"
#include "cx231xx-dif.h"
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 13249e5..27948e1 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -29,7 +29,6 @@
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
#include <media/cx25840.h>
#include "dvb-usb-ids.h"
diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index 1340ff2..c027942 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
@@ -32,7 +32,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index cd22147..9906261 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -36,7 +36,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
@@ -1228,179 +1227,93 @@
return rc;
}
-int cx231xx_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+int cx231xx_g_chip_info(struct file *file, void *fh,
+ struct v4l2_dbg_chip_info *chip)
{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match))
- chip->ident = V4L2_IDENT_CX23100;
+ switch (chip->match.addr) {
+ case 0: /* Cx231xx - internal registers */
+ return 0;
+ case 1: /* AFE - read byte */
+ strlcpy(chip->name, "AFE (byte)", sizeof(chip->name));
+ return 0;
+ case 2: /* Video Block - read byte */
+ strlcpy(chip->name, "Video (byte)", sizeof(chip->name));
+ return 0;
+ case 3: /* I2S block - read byte */
+ strlcpy(chip->name, "I2S (byte)", sizeof(chip->name));
+ return 0;
+ case 4: /* AFE - read dword */
+ strlcpy(chip->name, "AFE (dword)", sizeof(chip->name));
+ return 0;
+ case 5: /* Video Block - read dword */
+ strlcpy(chip->name, "Video (dword)", sizeof(chip->name));
+ return 0;
+ case 6: /* I2S Block - read dword */
+ strlcpy(chip->name, "I2S (dword)", sizeof(chip->name));
return 0;
}
return -EINVAL;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-
-/*
- -R, --list-registers=type=<host/i2cdrv/i2caddr>,
- chip=<chip>[,min=<addr>,max=<addr>]
- dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]
- -r, --set-register=type=<host/i2cdrv/i2caddr>,
- chip=<chip>,reg=<addr>,val=<val>
- set the register [VIDIOC_DBG_S_REGISTER]
-
- if type == host, then <chip> is the hosts chip ID (default 0)
- if type == i2cdrv (default), then <chip> is the I2C driver name or ID
- if type == i2caddr, then <chip> is the 7-bit I2C address
-*/
-
int cx231xx_g_register(struct file *file, void *priv,
struct v4l2_dbg_register *reg)
{
struct cx231xx_fh *fh = priv;
struct cx231xx *dev = fh->dev;
- int ret = 0;
+ int ret;
u8 value[4] = { 0, 0, 0, 0 };
u32 data = 0;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- switch (reg->match.addr) {
- case 0: /* Cx231xx - internal registers */
- ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
- (u16)reg->reg, value, 4);
- reg->val = value[0] | value[1] << 8 |
- value[2] << 16 | value[3] << 24;
- break;
- case 1: /* AFE - read byte */
- ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2, &data, 1);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 14: /* AFE - read dword */
- ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2, &data, 4);
- reg->val = le32_to_cpu(data);
- break;
- case 2: /* Video Block - read byte */
- ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2, &data, 1);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 24: /* Video Block - read dword */
- ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2, &data, 4);
- reg->val = le32_to_cpu(data);
- break;
- case 3: /* I2S block - read byte */
- ret = cx231xx_read_i2c_data(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- &data, 1);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 34: /* I2S Block - read dword */
- ret =
- cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1, &data, 4);
- reg->val = le32_to_cpu(data);
- break;
- }
- return ret < 0 ? ret : 0;
-
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- call_all(dev, core, g_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/
- switch (reg->match.addr) {
- case 0: /* Cx231xx - internal registers */
- ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
- (u16)reg->reg, value, 4);
- reg->val = value[0] | value[1] << 8 |
- value[2] << 16 | value[3] << 24;
-
- break;
- case 0x600:/* AFE - read byte */
- ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- break;
-
- case 0x880:/* Video Block - read byte */
- if (reg->reg < 0x0b) {
- ret = cx231xx_read_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- } else {
- ret = cx231xx_read_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- &data, 4 , 0);
- reg->val = le32_to_cpu(data);
- }
- break;
- case 0x980:
- ret = cx231xx_read_i2c_master(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 0x400:
- ret =
- cx231xx_read_i2c_master(dev, 0x40,
- (u16)reg->reg, 1,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 0xc01:
- ret =
- cx231xx_read_i2c_master(dev, 0xc0,
- (u16)reg->reg, 2,
- &data, 38, 1);
- reg->val = le32_to_cpu(data);
- break;
- case 0x022:
- ret =
- cx231xx_read_i2c_master(dev, 0x02,
- (u16)reg->reg, 1,
- &data, 1, 2);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 0x322:
- ret = cx231xx_read_i2c_master(dev,
- 0x32,
- (u16)reg->reg, 1,
- &data, 4 , 2);
- reg->val = le32_to_cpu(data);
- break;
- case 0x342:
- ret = cx231xx_read_i2c_master(dev,
- 0x34,
- (u16)reg->reg, 1,
- &data, 4 , 2);
- reg->val = le32_to_cpu(data);
- break;
-
- default:
- cx231xx_info("no match device address!!\n");
- break;
- }
- return ret < 0 ? ret : 0;
- /*return -EINVAL;*/
+ switch (reg->match.addr) {
+ case 0: /* Cx231xx - internal registers */
+ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ (u16)reg->reg, value, 4);
+ reg->val = value[0] | value[1] << 8 |
+ value[2] << 16 | value[3] << 24;
+ reg->size = 4;
+ break;
+ case 1: /* AFE - read byte */
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, &data, 1);
+ reg->val = data;
+ reg->size = 1;
+ break;
+ case 2: /* Video Block - read byte */
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, &data, 1);
+ reg->val = data;
+ reg->size = 1;
+ break;
+ case 3: /* I2S block - read byte */
+ ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1, &data, 1);
+ reg->val = data;
+ reg->size = 1;
+ break;
+ case 4: /* AFE - read dword */
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, &data, 4);
+ reg->val = data;
+ reg->size = 4;
+ break;
+ case 5: /* Video Block - read dword */
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, &data, 4);
+ reg->val = data;
+ reg->size = 4;
+ break;
+ case 6: /* I2S Block - read dword */
+ ret = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1, &data, 4);
+ reg->val = data;
+ reg->size = 4;
+ break;
default:
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
+ return -EINVAL;
}
-
- call_all(dev, core, g_register, reg);
-
- return ret;
+ return ret < 0 ? ret : 0;
}
int cx231xx_s_register(struct file *file, void *priv,
@@ -1408,165 +1321,46 @@
{
struct cx231xx_fh *fh = priv;
struct cx231xx *dev = fh->dev;
- int ret = 0;
- __le64 buf;
- u32 value;
+ int ret;
u8 data[4] = { 0, 0, 0, 0 };
- buf = cpu_to_le64(reg->val);
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- {
- value = (u32) buf & 0xffffffff;
-
- switch (reg->match.addr) {
- case 0: /* cx231xx internal registers */
- data[0] = (u8) value;
- data[1] = (u8) (value >> 8);
- data[2] = (u8) (value >> 16);
- data[3] = (u8) (value >> 24);
- ret = cx231xx_write_ctrl_reg(dev,
- VRT_SET_REGISTER,
- (u16)reg->reg, data,
- 4);
- break;
- case 1: /* AFE - read byte */
- ret = cx231xx_write_i2c_data(dev,
- AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- value, 1);
- break;
- case 14: /* AFE - read dword */
- ret = cx231xx_write_i2c_data(dev,
- AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- value, 4);
- break;
- case 2: /* Video Block - read byte */
- ret =
- cx231xx_write_i2c_data(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 1);
- break;
- case 24: /* Video Block - read dword */
- ret =
- cx231xx_write_i2c_data(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 4);
- break;
- case 3: /* I2S block - read byte */
- ret =
- cx231xx_write_i2c_data(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- value, 1);
- break;
- case 34: /* I2S block - read dword */
- ret =
- cx231xx_write_i2c_data(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- value, 4);
- break;
- }
- }
- return ret < 0 ? ret : 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- {
- value = (u32) buf & 0xffffffff;
-
- switch (reg->match.addr) {
- case 0:/*cx231xx internal registers*/
- data[0] = (u8) value;
- data[1] = (u8) (value >> 8);
- data[2] = (u8) (value >> 16);
- data[3] = (u8) (value >> 24);
- ret = cx231xx_write_ctrl_reg(dev,
- VRT_SET_REGISTER,
- (u16)reg->reg, data,
- 4);
- break;
- case 0x600:/* AFE - read byte */
- ret = cx231xx_write_i2c_master(dev,
- AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- value, 1 , 0);
- break;
-
- case 0x880:/* Video Block - read byte */
- if (reg->reg < 0x0b)
- cx231xx_write_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 1, 0);
- else
- cx231xx_write_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 4, 0);
- break;
- case 0x980:
- ret =
- cx231xx_write_i2c_master(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- value, 1, 0);
- break;
- case 0x400:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x40,
- (u16)reg->reg, 1,
- value, 1, 0);
- break;
- case 0xc01:
- ret =
- cx231xx_write_i2c_master(dev,
- 0xc0,
- (u16)reg->reg, 1,
- value, 1, 1);
- break;
-
- case 0x022:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x02,
- (u16)reg->reg, 1,
- value, 1, 2);
- break;
- case 0x322:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x32,
- (u16)reg->reg, 1,
- value, 4, 2);
- break;
-
- case 0x342:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x34,
- (u16)reg->reg, 1,
- value, 4, 2);
- break;
- default:
- cx231xx_info("no match device address, "
- "the value is %x\n", reg->match.addr);
- break;
-
- }
-
- }
- default:
+ switch (reg->match.addr) {
+ case 0: /* cx231xx internal registers */
+ data[0] = (u8) reg->val;
+ data[1] = (u8) (reg->val >> 8);
+ data[2] = (u8) (reg->val >> 16);
+ data[3] = (u8) (reg->val >> 24);
+ ret = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ (u16)reg->reg, data, 4);
break;
+ case 1: /* AFE - write byte */
+ ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, reg->val, 1);
+ break;
+ case 2: /* Video Block - write byte */
+ ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, reg->val, 1);
+ break;
+ case 3: /* I2S block - write byte */
+ ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1, reg->val, 1);
+ break;
+ case 4: /* AFE - write dword */
+ ret = cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, reg->val, 4);
+ break;
+ case 5: /* Video Block - write dword */
+ ret = cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, reg->val, 4);
+ break;
+ case 6: /* I2S block - write dword */
+ ret = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1, reg->val, 4);
+ break;
+ default:
+ return -EINVAL;
}
-
- call_all(dev, core, s_register, reg);
-
- return ret;
+ return ret < 0 ? ret : 0;
}
#endif
@@ -2208,8 +2002,8 @@
.vidioc_s_tuner = cx231xx_s_tuner,
.vidioc_g_frequency = cx231xx_g_frequency,
.vidioc_s_frequency = cx231xx_s_frequency,
- .vidioc_g_chip_ident = cx231xx_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_chip_info = cx231xx_g_chip_info,
.vidioc_g_register = cx231xx_g_register,
.vidioc_s_register = cx231xx_s_register,
#endif
@@ -2240,8 +2034,8 @@
.vidioc_s_tuner = radio_s_tuner,
.vidioc_g_frequency = cx231xx_g_frequency,
.vidioc_s_frequency = cx231xx_s_frequency,
- .vidioc_g_chip_ident = cx231xx_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_chip_info = cx231xx_g_chip_info,
.vidioc_g_register = cx231xx_g_register,
.vidioc_s_register = cx231xx_s_register,
#endif
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 5ad9fd6..e812119 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -945,7 +945,7 @@
struct v4l2_input *i);
int cx231xx_g_input(struct file *file, void *priv, unsigned int *i);
int cx231xx_s_input(struct file *file, void *priv, unsigned int i);
-int cx231xx_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip);
+int cx231xx_g_chip_info(struct file *file, void *fh, struct v4l2_dbg_chip_info *chip);
int cx231xx_g_register(struct file *file, void *priv,
struct v4l2_dbg_register *reg);
int cx231xx_s_register(struct file *file, void *priv,
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index b638fc1..1ea17dc 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -55,7 +55,7 @@
if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) ||
req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) {
dev_err(&d->udev->dev, "%s: too much data wlen=%d rlen=%d\n",
- __func__, req->wlen, req->rlen);
+ KBUILD_MODNAME, req->wlen, req->rlen);
ret = -EINVAL;
goto exit;
}
@@ -91,9 +91,10 @@
checksum = af9035_checksum(state->buf, rlen - 2);
tmp_checksum = (state->buf[rlen - 2] << 8) | state->buf[rlen - 1];
if (tmp_checksum != checksum) {
- dev_err(&d->udev->dev, "%s: command=%02x checksum mismatch " \
- "(%04x != %04x)\n", KBUILD_MODNAME, req->cmd,
- tmp_checksum, checksum);
+ dev_err(&d->udev->dev,
+ "%s: command=%02x checksum mismatch (%04x != %04x)\n",
+ KBUILD_MODNAME, req->cmd, tmp_checksum,
+ checksum);
ret = -EIO;
goto exit;
}
@@ -268,11 +269,29 @@
memcpy(&buf[5], msg[0].buf, msg[0].len);
ret = af9035_ctrl_msg(d, &req);
}
+ } else if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 40) {
+ /* TODO: correct limits > 40 */
+ ret = -EOPNOTSUPP;
+ } else {
+ /* I2C */
+ u8 buf[5];
+ struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
+ buf, msg[0].len, msg[0].buf };
+ req.mbox |= ((msg[0].addr & 0x80) >> 3);
+ buf[0] = msg[0].len;
+ buf[1] = msg[0].addr << 1;
+ buf[2] = 0x00; /* reg addr len */
+ buf[3] = 0x00; /* reg addr MSB */
+ buf[4] = 0x00; /* reg addr LSB */
+ ret = af9035_ctrl_msg(d, &req);
+ }
} else {
/*
- * We support only two kind of I2C transactions:
- * 1) 1 x read + 1 x write
+ * We support only three kind of I2C transactions:
+ * 1) 1 x read + 1 x write (repeated start)
* 2) 1 x write
+ * 3) 1 x read
*/
ret = -EOPNOTSUPP;
}
@@ -317,8 +336,8 @@
dev_info(&d->udev->dev,
"%s: prechip_version=%02x chip_version=%02x chip_type=%04x\n",
- __func__, state->prechip_version, state->chip_version,
- state->chip_type);
+ KBUILD_MODNAME, state->prechip_version,
+ state->chip_version, state->chip_type);
if (state->chip_type == 0x9135) {
if (state->chip_version == 0x02)
@@ -382,9 +401,10 @@
hdr_checksum = fw->data[fw->size - i + 5] << 8;
hdr_checksum |= fw->data[fw->size - i + 6] << 0;
- dev_dbg(&d->udev->dev, "%s: core=%d addr=%04x data_len=%d " \
- "checksum=%04x\n", __func__, hdr_core, hdr_addr,
- hdr_data_len, hdr_checksum);
+ dev_dbg(&d->udev->dev,
+ "%s: core=%d addr=%04x data_len=%d checksum=%04x\n",
+ __func__, hdr_core, hdr_addr, hdr_data_len,
+ hdr_checksum);
if (((hdr_core != 1) && (hdr_core != 2)) ||
(hdr_data_len > i)) {
@@ -489,7 +509,7 @@
u8 rbuf[4];
u8 tmp;
struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
- struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
+ struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf };
dev_dbg(&d->udev->dev, "%s:\n", __func__);
/*
@@ -498,11 +518,11 @@
* which is done by master demod.
* Master feeds also clock and controls power via GPIO.
*/
- ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_DUAL_MODE, &tmp);
+ ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp);
if (ret < 0)
goto err;
- if (tmp) {
+ if (tmp == 1 || tmp == 3) {
/* configure gpioh1, reset & power slave demod */
ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01);
if (ret < 0)
@@ -620,13 +640,15 @@
}
/* check if there is dual tuners */
- ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_DUAL_MODE, &tmp);
+ ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp);
if (ret < 0)
goto err;
- state->dual_mode = tmp;
- dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", __func__,
- state->dual_mode);
+ if (tmp == 1 || tmp == 3)
+ state->dual_mode = true;
+
+ dev_dbg(&d->udev->dev, "%s: ts mode=%d dual mode=%d\n", __func__,
+ tmp, state->dual_mode);
if (state->dual_mode) {
/* read 2nd demodulator I2C address */
@@ -1200,9 +1222,9 @@
{ 0x80f9a4, 0x00, 0x01 },
};
- dev_dbg(&d->udev->dev, "%s: USB speed=%d frame_size=%04x " \
- "packet_size=%02x\n", __func__,
- d->udev->speed, frame_size, packet_size);
+ dev_dbg(&d->udev->dev,
+ "%s: USB speed=%d frame_size=%04x packet_size=%02x\n",
+ __func__, d->udev->speed, frame_size, packet_size);
/* init endpoints */
for (i = 0; i < ARRAY_SIZE(tab); i++) {
@@ -1477,7 +1499,7 @@
&af9035_props, "AVerMedia Twinstar (A825)", NULL) },
{ DVB_USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100MINI_PLUS,
&af9035_props, "Asus U3100Mini Plus", NULL) },
- { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00aa,
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00aa,
&af9035_props, "TerraTec Cinergy T Stick (rev. 2)", NULL) },
/* IT9135 devices */
#if 0
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h
index b5827ca..a1c68d8 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.h
+++ b/drivers/media/usb/dvb-usb-v2/af9035.h
@@ -100,8 +100,13 @@
* eeprom is memory mapped as read only. Writing that memory mapped address
* will not corrupt eeprom.
*
- * eeprom has value 0x00 single mode and 0x03 for dual mode as far as I have
- * seen to this day.
+ * TS mode:
+ * 0 TS
+ * 1 DCA + PIP
+ * 3 PIP
+ * n DCA
+ *
+ * Values 0 and 3 are seen to this day. 0 for single TS and 3 for dual TS.
*/
#define EEPROM_BASE_AF9035 0x42fd
@@ -109,7 +114,7 @@
#define EEPROM_SHIFT 0x10
#define EEPROM_IR_MODE 0x10
-#define EEPROM_DUAL_MODE 0x29
+#define EEPROM_TS_MODE 0x29
#define EEPROM_2ND_DEMOD_ADDR 0x2a
#define EEPROM_IR_TYPE 0x2c
#define EEPROM_1_IF_L 0x30
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
index 658c6d4..399916b 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -140,7 +140,7 @@
int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
int (*query) (struct dvb_usb_device *d);
unsigned int interval;
- const enum rc_driver_type driver_type;
+ enum rc_driver_type driver_type;
bool bulk_mode;
};
diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c
index e48cdeb..1cb6899 100644
--- a/drivers/media/usb/dvb-usb-v2/it913x.c
+++ b/drivers/media/usb/dvb-usb-v2/it913x.c
@@ -45,7 +45,7 @@
static int dvb_usb_it913x_firmware;
module_param_named(firmware, dvb_usb_it913x_firmware, int, 0644);
-MODULE_PARM_DESC(firmware, "set firmware 0=auto"\
+MODULE_PARM_DESC(firmware, "set firmware 0=auto "\
"1=IT9137 2=IT9135 V1 3=IT9135 V2");
#define FW_IT9137 "dvb-usb-it9137-01.fw"
#define FW_IT9135_V1 "dvb-usb-it9135-01.fw"
@@ -796,6 +796,9 @@
{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835,
&it913x_properties, "Avermedia A835B(4835)",
RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CTVDIGDUAL_V2,
+ &it913x_properties, "Digital Dual TV Receiver CTVDIGDUAL_V2",
+ RC_MAP_IT913X_V1) },
{} /* Terminating entry */
};
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
index ef4c65f..879c529 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
@@ -31,8 +31,6 @@
if (mxl111sf_tuner_debug) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
-#define err pr_err
-
/* ------------------------------------------------------------------------ */
struct mxl111sf_tuner_state {
@@ -113,7 +111,7 @@
filt_bw = 63;
break;
default:
- err("%s: invalid bandwidth setting!", __func__);
+ pr_err("%s: invalid bandwidth setting!", __func__);
return NULL;
}
@@ -304,12 +302,12 @@
bw = 8;
break;
default:
- err("%s: bandwidth not set!", __func__);
+ pr_err("%s: bandwidth not set!", __func__);
return -EINVAL;
}
break;
default:
- err("%s: modulation type not supported!", __func__);
+ pr_err("%s: modulation type not supported!", __func__);
return -EINVAL;
}
ret = mxl1x1sf_tune_rf(fe, c->frequency, bw);
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
index efdcb15..e97964e 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -52,12 +52,6 @@
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-#define deb_info pr_debug
-#define deb_reg pr_debug
-#define deb_adv pr_debug
-#define err pr_err
-#define info pr_info
-
int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
@@ -65,7 +59,7 @@
int ret;
u8 sndbuf[1+wlen];
- deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
+ pr_debug("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
memset(sndbuf, 0, 1+wlen);
@@ -98,12 +92,12 @@
if (buf[0] == addr)
*data = buf[1];
else {
- err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
+ pr_err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
addr, buf[0], buf[1]);
ret = -EINVAL;
}
- deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data);
+ pr_debug("R: (0x%02x, 0x%02x)\n", addr, *data);
fail:
return ret;
}
@@ -113,11 +107,11 @@
u8 buf[] = { addr, data };
int ret;
- deb_reg("W: (0x%02x, 0x%02x)\n", addr, data);
+ pr_debug("W: (0x%02x, 0x%02x)\n", addr, data);
ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
if (mxl_fail(ret))
- err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
+ pr_err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
return ret;
}
@@ -134,7 +128,7 @@
#if 1
/* dont know why this usually errors out on the first try */
if (mxl_fail(ret))
- err("error writing addr: 0x%02x, mask: 0x%02x, "
+ pr_err("error writing addr: 0x%02x, mask: 0x%02x, "
"data: 0x%02x, retrying...", addr, mask, data);
ret = mxl111sf_read_reg(state, addr, &val);
@@ -167,7 +161,7 @@
ctrl_reg_info[i].mask,
ctrl_reg_info[i].data);
if (mxl_fail(ret)) {
- err("failed on reg #%d (0x%02x)", i,
+ pr_err("failed on reg #%d (0x%02x)", i,
ctrl_reg_info[i].addr);
break;
}
@@ -225,7 +219,7 @@
mxl_rev = "UNKNOWN REVISION";
break;
}
- info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
+ pr_info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
fail:
return ret;
}
@@ -239,7 +233,7 @@
" on first probe attempt"); \
___ret = mxl1x1sf_get_chip_info(state); \
if (mxl_fail(___ret)) \
- err("failed to get chip info during probe"); \
+ pr_err("failed to get chip info during probe"); \
else \
mxl_debug("probe needed a retry " \
"in order to succeed."); \
@@ -270,14 +264,14 @@
goto fail;
}
- deb_info("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
mutex_lock(&state->fe_lock);
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
+ pr_err("set interface failed");
err = mxl1x1sf_soft_reset(state);
mxl_fail(err);
@@ -326,7 +320,7 @@
goto fail;
}
- deb_info("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0;
@@ -344,7 +338,7 @@
struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id];
int ret = 0;
- deb_info("%s(%d)\n", __func__, onoff);
+ pr_debug("%s(%d)\n", __func__, onoff);
if (onoff) {
ret = mxl111sf_enable_usb_output(state);
@@ -368,7 +362,7 @@
struct mxl111sf_state *state = fe_to_priv(fe);
int ret = 0;
- deb_info("%s(%d)\n", __func__, onoff);
+ pr_debug("%s(%d)\n", __func__, onoff);
if (onoff) {
ret = mxl111sf_enable_usb_output(state);
@@ -394,7 +388,7 @@
struct mxl111sf_state *state = fe_to_priv(fe);
int ret = 0;
- deb_info("%s(%d)\n", __func__, onoff);
+ pr_debug("%s(%d)\n", __func__, onoff);
if (onoff) {
ret = mxl111sf_enable_usb_output(state);
@@ -424,7 +418,7 @@
struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
int ret;
- deb_adv("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
/* save a pointer to the dvb_usb_device in device state */
state->d = d;
@@ -432,7 +426,7 @@
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
+ pr_err("set interface failed");
state->gpio_mode = MXL111SF_GPIO_MOD_ATSC;
adap_state->gpio_mode = state->gpio_mode;
@@ -495,7 +489,7 @@
struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
int ret;
- deb_adv("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
/* save a pointer to the dvb_usb_device in device state */
state->d = d;
@@ -503,7 +497,7 @@
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
+ pr_err("set interface failed");
state->gpio_mode = MXL111SF_GPIO_MOD_MH;
adap_state->gpio_mode = state->gpio_mode;
@@ -580,7 +574,7 @@
struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
int ret;
- deb_adv("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
/* save a pointer to the dvb_usb_device in device state */
state->d = d;
@@ -588,7 +582,7 @@
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
+ pr_err("set interface failed");
state->gpio_mode = MXL111SF_GPIO_MOD_MH;
adap_state->gpio_mode = state->gpio_mode;
@@ -667,7 +661,7 @@
struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
int ret;
- deb_adv("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
/* save a pointer to the dvb_usb_device in device state */
state->d = d;
@@ -675,7 +669,7 @@
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
+ pr_err("set interface failed");
state->gpio_mode = MXL111SF_GPIO_MOD_MH;
adap_state->gpio_mode = state->gpio_mode;
@@ -742,7 +736,7 @@
struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
int ret;
- deb_adv("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
/* save a pointer to the dvb_usb_device in device state */
state->d = d;
@@ -750,7 +744,7 @@
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
+ pr_err("set interface failed");
state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
adap_state->gpio_mode = state->gpio_mode;
@@ -802,7 +796,7 @@
}
#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \
- err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
+ pr_err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
__func__, __LINE__, \
(ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \
pwr0, pwr1, pwr2, pwr3)
@@ -868,7 +862,7 @@
struct mxl111sf_state *state = adap_to_priv(adap);
int i;
- deb_adv("%s()\n", __func__);
+ pr_debug("%s()\n", __func__);
for (i = 0; i < state->num_frontends; i++) {
if (dvb_attach(mxl111sf_tuner_attach, adap->fe[i], state,
@@ -902,7 +896,7 @@
ret = get_chip_info(state);
if (mxl_fail(ret))
- err("failed to get chip info during probe");
+ pr_err("failed to get chip info during probe");
mutex_init(&state->fe_lock);
@@ -950,7 +944,7 @@
static int mxl111sf_frontend_attach_atsc_mh(struct dvb_usb_adapter *adap)
{
int ret;
- deb_info("%s\n", __func__);
+ pr_debug("%s\n", __func__);
ret = mxl111sf_lgdt3305_frontend_attach(adap, 0);
if (ret < 0)
@@ -970,7 +964,7 @@
static int mxl111sf_frontend_attach_mercury(struct dvb_usb_adapter *adap)
{
int ret;
- deb_info("%s\n", __func__);
+ pr_debug("%s\n", __func__);
ret = mxl111sf_lgdt3305_frontend_attach(adap, 0);
if (ret < 0)
@@ -990,7 +984,7 @@
static int mxl111sf_frontend_attach_mercury_mh(struct dvb_usb_adapter *adap)
{
int ret;
- deb_info("%s\n", __func__);
+ pr_debug("%s\n", __func__);
ret = mxl111sf_attach_demod(adap, 0);
if (ret < 0)
@@ -1006,7 +1000,7 @@
static void mxl111sf_stream_config_bulk(struct usb_data_stream_properties *stream, u8 endpoint)
{
- deb_info("%s: endpoint=%d size=8192\n", __func__, endpoint);
+ pr_debug("%s: endpoint=%d size=8192\n", __func__, endpoint);
stream->type = USB_BULK;
stream->count = 5;
stream->endpoint = endpoint;
@@ -1016,7 +1010,7 @@
static void mxl111sf_stream_config_isoc(struct usb_data_stream_properties *stream,
u8 endpoint, int framesperurb, int framesize)
{
- deb_info("%s: endpoint=%d size=%d\n", __func__, endpoint,
+ pr_debug("%s: endpoint=%d size=%d\n", __func__, endpoint,
framesperurb * framesize);
stream->type = USB_ISOC;
stream->count = 5;
@@ -1035,7 +1029,7 @@
static int mxl111sf_get_stream_config_dvbt(struct dvb_frontend *fe,
u8 *ts_type, struct usb_data_stream_properties *stream)
{
- deb_info("%s: fe=%d\n", __func__, fe->id);
+ pr_debug("%s: fe=%d\n", __func__, fe->id);
*ts_type = DVB_USB_FE_TS_TYPE_188;
if (dvb_usb_mxl111sf_isoc)
@@ -1076,7 +1070,7 @@
static int mxl111sf_get_stream_config_atsc(struct dvb_frontend *fe,
u8 *ts_type, struct usb_data_stream_properties *stream)
{
- deb_info("%s: fe=%d\n", __func__, fe->id);
+ pr_debug("%s: fe=%d\n", __func__, fe->id);
*ts_type = DVB_USB_FE_TS_TYPE_188;
if (dvb_usb_mxl111sf_isoc)
@@ -1117,7 +1111,7 @@
static int mxl111sf_get_stream_config_mh(struct dvb_frontend *fe,
u8 *ts_type, struct usb_data_stream_properties *stream)
{
- deb_info("%s: fe=%d\n", __func__, fe->id);
+ pr_debug("%s: fe=%d\n", __func__, fe->id);
*ts_type = DVB_USB_FE_TS_TYPE_RAW;
if (dvb_usb_mxl111sf_isoc)
@@ -1158,7 +1152,7 @@
static int mxl111sf_get_stream_config_atsc_mh(struct dvb_frontend *fe,
u8 *ts_type, struct usb_data_stream_properties *stream)
{
- deb_info("%s: fe=%d\n", __func__, fe->id);
+ pr_debug("%s: fe=%d\n", __func__, fe->id);
if (fe->id == 0) {
*ts_type = DVB_USB_FE_TS_TYPE_188;
@@ -1184,7 +1178,7 @@
static int mxl111sf_streaming_ctrl_atsc_mh(struct dvb_frontend *fe, int onoff)
{
- deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+ pr_debug("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
if (fe->id == 0)
return mxl111sf_ep6_streaming_ctrl(fe, onoff);
@@ -1228,7 +1222,7 @@
static int mxl111sf_get_stream_config_mercury(struct dvb_frontend *fe,
u8 *ts_type, struct usb_data_stream_properties *stream)
{
- deb_info("%s: fe=%d\n", __func__, fe->id);
+ pr_debug("%s: fe=%d\n", __func__, fe->id);
if (fe->id == 0) {
*ts_type = DVB_USB_FE_TS_TYPE_188;
@@ -1260,7 +1254,7 @@
static int mxl111sf_streaming_ctrl_mercury(struct dvb_frontend *fe, int onoff)
{
- deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+ pr_debug("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
if (fe->id == 0)
return mxl111sf_ep6_streaming_ctrl(fe, onoff);
@@ -1306,7 +1300,7 @@
static int mxl111sf_get_stream_config_mercury_mh(struct dvb_frontend *fe,
u8 *ts_type, struct usb_data_stream_properties *stream)
{
- deb_info("%s: fe=%d\n", __func__, fe->id);
+ pr_debug("%s: fe=%d\n", __func__, fe->id);
if (fe->id == 0) {
*ts_type = DVB_USB_FE_TS_TYPE_188;
@@ -1332,7 +1326,7 @@
static int mxl111sf_streaming_ctrl_mercury_mh(struct dvb_frontend *fe, int onoff)
{
- deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+ pr_debug("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
if (fe->id == 0)
return mxl111sf_ep4_streaming_ctrl(fe, onoff);
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 2cc8ec7..c0cd084 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -1041,67 +1041,34 @@
static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
{
int ret;
- u8 val;
dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
if (onoff) {
- /* set output values */
- ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ /* GPIO3=1, GPIO4=0 */
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x08, 0x18);
if (ret)
goto err;
- val |= 0x08;
- val &= 0xef;
-
- ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ /* suspend? */
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL1, 0x00, 0x10);
if (ret)
goto err;
- /* demod_ctl_1 */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+ /* enable PLL */
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x80, 0x80);
if (ret)
goto err;
- val &= 0xef;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
- if (ret)
- goto err;
-
- /* demod control */
- /* PLL enable */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- /* bit 7 to 1 */
- val |= 0x80;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
- if (ret)
- goto err;
-
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- val |= 0x20;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ /* disable reset */
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x20, 0x20);
if (ret)
goto err;
mdelay(5);
- /*enable ADC_Q and ADC_I */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- val |= 0x48;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ /* enable ADC */
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x48, 0x48);
if (ret)
goto err;
@@ -1114,36 +1081,18 @@
if (ret)
goto err;
} else {
- /* demod_ctl_1 */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+ /* GPIO4=1 */
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x10, 0x10);
if (ret)
goto err;
- val |= 0x0c;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+ /* disable ADC */
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x48);
if (ret)
goto err;
- /* set output values */
- ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
- if (ret)
- goto err;
-
- val |= 0x10;
-
- ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
- if (ret)
- goto err;
-
- /* demod control */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- val &= 0x37;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ /* disable PLL */
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80);
if (ret)
goto err;
@@ -1242,42 +1191,47 @@
return 0;
}
-#else
- #define rtl2831u_get_rc_config NULL
-#endif
-#if IS_ENABLED(CONFIG_RC_CORE)
static int rtl2832u_rc_query(struct dvb_usb_device *d)
{
- int ret, i;
+ int ret, i, len;
struct rtl28xxu_priv *priv = d->priv;
+ struct ir_raw_event ev;
u8 buf[128];
- int len;
- struct rtl28xxu_reg_val rc_nec_tab[] = {
- { IR_RX_CTRL, 0x20 },
- { IR_RX_BUF_CTRL, 0x80 },
- { IR_RX_IF, 0xff },
- { IR_RX_IE, 0xff },
- { IR_MAX_DURATION0, 0xd0 },
- { IR_MAX_DURATION1, 0x07 },
- { IR_IDLE_LEN0, 0xc0 },
- { IR_IDLE_LEN1, 0x00 },
- { IR_GLITCH_LEN, 0x03 },
- { IR_RX_CLK, 0x09 },
- { IR_RX_CFG, 0x1c },
- { IR_MAX_H_TOL_LEN, 0x1e },
- { IR_MAX_L_TOL_LEN, 0x1e },
- { IR_RX_CTRL, 0x80 },
+ static const struct rtl28xxu_reg_val_mask refresh_tab[] = {
+ {IR_RX_IF, 0x03, 0xff},
+ {IR_RX_BUF_CTRL, 0x80, 0xff},
+ {IR_RX_CTRL, 0x80, 0xff},
};
/* init remote controller */
if (!priv->rc_active) {
- for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
- ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
- rc_nec_tab[i].val);
+ static const struct rtl28xxu_reg_val_mask init_tab[] = {
+ {SYS_DEMOD_CTL1, 0x00, 0x04},
+ {SYS_DEMOD_CTL1, 0x00, 0x08},
+ {USB_CTRL, 0x20, 0x20},
+ {SYS_GPIO_DIR, 0x00, 0x08},
+ {SYS_GPIO_OUT_EN, 0x08, 0x08},
+ {SYS_GPIO_OUT_VAL, 0x08, 0x08},
+ {IR_MAX_DURATION0, 0xd0, 0xff},
+ {IR_MAX_DURATION1, 0x07, 0xff},
+ {IR_IDLE_LEN0, 0xc0, 0xff},
+ {IR_IDLE_LEN1, 0x00, 0xff},
+ {IR_GLITCH_LEN, 0x03, 0xff},
+ {IR_RX_CLK, 0x09, 0xff},
+ {IR_RX_CFG, 0x1c, 0xff},
+ {IR_MAX_H_TOL_LEN, 0x1e, 0xff},
+ {IR_MAX_L_TOL_LEN, 0x1e, 0xff},
+ {IR_RX_CTRL, 0x80, 0xff},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_tab); i++) {
+ ret = rtl28xx_wr_reg_mask(d, init_tab[i].reg,
+ init_tab[i].val, init_tab[i].mask);
if (ret)
goto err;
}
+
priv->rc_active = true;
}
@@ -1293,14 +1247,32 @@
goto err;
len = buf[0];
+
+ /* read raw code from hw */
ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
+ if (ret)
+ goto err;
- /* TODO: pass raw IR to Kernel IR decoder */
+ /* let hw receive new code */
+ for (i = 0; i < ARRAY_SIZE(refresh_tab); i++) {
+ ret = rtl28xx_wr_reg_mask(d, refresh_tab[i].reg,
+ refresh_tab[i].val, refresh_tab[i].mask);
+ if (ret)
+ goto err;
+ }
- ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03);
- ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
- ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80);
+ /* pass data to Kernel IR decoder */
+ init_ir_raw_event(&ev);
+ for (i = 0; i < len; i++) {
+ ev.pulse = buf[i] >> 7;
+ ev.duration = 50800 * (buf[i] & 0x7f);
+ ir_raw_event_store_with_filter(d->rc_dev, &ev);
+ }
+
+ /* 'flush' ir_raw_event_store_with_filter() */
+ ir_raw_event_set_idle(d->rc_dev, true);
+ ir_raw_event_handle(d->rc_dev);
exit:
return ret;
err:
@@ -1311,15 +1283,19 @@
static int rtl2832u_get_rc_config(struct dvb_usb_device *d,
struct dvb_usb_rc *rc)
{
- rc->map_name = RC_MAP_EMPTY;
- rc->allowed_protos = RC_BIT_NEC;
+ /* load empty to enable rc */
+ if (!rc->map_name)
+ rc->map_name = RC_MAP_EMPTY;
+ rc->allowed_protos = RC_BIT_ALL;
+ rc->driver_type = RC_DRIVER_IR_RAW;
rc->query = rtl2832u_rc_query;
rc->interval = 400;
return 0;
}
#else
- #define rtl2832u_get_rc_config NULL
+#define rtl2831u_get_rc_config NULL
+#define rtl2832u_get_rc_config NULL
#endif
static const struct dvb_usb_device_properties rtl2831u_props = {
@@ -1379,7 +1355,7 @@
{ DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838,
&rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1,
- &rtl2832u_props, "TerraTec Cinergy T Stick Black", NULL) },
+ &rtl2832u_props, "TerraTec Cinergy T Stick Black", RC_MAP_TERRATEC_SLIM) },
{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT,
&rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) },
{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK,
@@ -1403,11 +1379,15 @@
{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393,
&rtl2832u_props, "GIGABYTE U7300", NULL) },
{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104,
- &rtl2832u_props, "Digivox Micro Hd", NULL) },
+ &rtl2832u_props, "MSI DIGIVOX Micro HD", NULL) },
{ DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620,
&rtl2832u_props, "Compro VideoMate U620F", NULL) },
{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394,
&rtl2832u_props, "MaxMedia HU394-T", NULL) },
+ { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03,
+ &rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) },
+ { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
+ &rtl2832u_props, "Crypto ReDi PC 50 A", NULL) },
{ }
};
MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index 533a331..729b354 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -97,6 +97,12 @@
u8 val;
};
+struct rtl28xxu_reg_val_mask {
+ u16 reg;
+ u8 val;
+ u8 mask;
+};
+
/*
* memory map
*
diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c
index 91e0119..ea2d5ee 100644
--- a/drivers/media/usb/dvb-usb/az6027.c
+++ b/drivers/media/usb/dvb-usb/az6027.c
@@ -264,7 +264,7 @@
.demod_address = 0xd0, /* 0x68, 0xd0 >> 1 */
.xtal_freq = 27000000,
- .inversion = IQ_SWAP_ON, /* 1 */
+ .inversion = IQ_SWAP_ON,
.lo_clk = 76500000,
.hi_clk = 99000000,
diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c
index d1ddfa1..449a996 100644
--- a/drivers/media/usb/dvb-usb/pctv452e.c
+++ b/drivers/media/usb/dvb-usb/pctv452e.c
@@ -828,7 +828,7 @@
.block_sync_mode = STB0899_SYNC_FORCED, /* ? */
.xtal_freq = 27000000, /* Assume Hz ? */
- .inversion = IQ_SWAP_ON, /* ? */
+ .inversion = IQ_SWAP_ON,
.lo_clk = 76500000,
.hi_clk = 99000000,
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 83bfbe4..dc65742 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -37,7 +37,6 @@
#include <media/i2c-addr.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
#include "em28xx.h"
@@ -83,26 +82,26 @@
/* Reset for the most [analog] boards */
static struct em28xx_reg_seq default_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
/* Reset for the most [digital] boards */
static struct em28xx_reg_seq default_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
/* Board Hauppauge WinTV HVR 900 analog */
static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
- {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x2d, ~EM_GPIO_4, 10},
{0x05, 0xff, 0x10, 10},
{ -1, -1, -1, -1},
};
/* Board Hauppauge WinTV HVR 900 digital */
static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x2e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x04, 0x0f, 10},
{EM2880_R04_GPO, 0x0c, 0x0f, 10},
{ -1, -1, -1, -1},
@@ -110,14 +109,14 @@
/* Board Hauppauge WinTV HVR 900 (R2) digital */
static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x2e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x0c, 0x0f, 10},
{ -1, -1, -1, -1},
};
/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
- {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x69, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
@@ -128,11 +127,11 @@
/* Board - EM2882 Kworld 315U digital */
static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10},
{EM2880_R04_GPO, 0x04, 0xff, 10},
{EM2880_R04_GPO, 0x0c, 0xff, 10},
- {EM28XX_R08_GPIO, 0x7e, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0x7e, 0xff, 10},
{ -1, -1, -1, -1},
};
@@ -145,13 +144,13 @@
};
static struct em28xx_reg_seq kworld_330u_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x00, 0xff, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq kworld_330u_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
{ -1, -1, -1, -1},
};
@@ -163,12 +162,12 @@
GOP3 - s5h1409 reset
*/
static struct em28xx_reg_seq evga_indtube_analog[] = {
- {EM28XX_R08_GPIO, 0x79, 0xff, 60},
+ {EM2820_R08_GPIO_CTRL, 0x79, 0xff, 60},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq evga_indtube_digital[] = {
- {EM28XX_R08_GPIO, 0x7a, 0xff, 1},
+ {EM2820_R08_GPIO_CTRL, 0x7a, 0xff, 1},
{EM2880_R04_GPO, 0x04, 0xff, 10},
{EM2880_R04_GPO, 0x0c, 0xff, 1},
{ -1, -1, -1, -1},
@@ -186,31 +185,31 @@
* EM_GPIO_7 - currently unknown
*/
static struct em28xx_reg_seq kworld_a340_digital[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
/* Pinnacle Hybrid Pro eb1a:2881 */
static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
- {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0xfd, ~EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */
{EM2880_R04_GPO, 0x0c, 0xff, 1},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6d, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x00, 0xff, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
{ -1, -1, -1, -1},
};
@@ -219,66 +218,66 @@
GPIO4 - CU1216L NIM
Other GPIOs seems to be don't care. */
static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
- {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
- {EM28XX_R08_GPIO, 0xde, 0xff, 10},
- {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM28XX_R08_GPIO, 0x7f, 0xff, 10},
- {EM28XX_R08_GPIO, 0x6f, 0xff, 10},
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xde, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0x7f, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6f, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
{-1, -1, -1, -1},
};
/* Callback for the most boards */
static struct em28xx_reg_seq default_tuner_gpio[] = {
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, EM_GPIO_4, EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0, EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, EM_GPIO_4, EM_GPIO_4, 10},
{ -1, -1, -1, -1},
};
/* Mute/unmute */
static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
- {EM28XX_R08_GPIO, 5, 7, 10},
+ {EM2820_R08_GPIO_CTRL, 5, 7, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
- {EM28XX_R08_GPIO, 4, 7, 10},
+ {EM2820_R08_GPIO_CTRL, 4, 7, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq compro_mute_gpio[] = {
- {EM28XX_R08_GPIO, 6, 7, 10},
+ {EM2820_R08_GPIO_CTRL, 6, 7, 10},
{ -1, -1, -1, -1},
};
/* Terratec AV350 */
static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
- {EM28XX_R08_GPIO, 0xff, 0x7f, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0x7f, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq silvercrest_reg_seq[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM28XX_R08_GPIO, 0x01, 0xf7, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2820_R08_GPIO_CTRL, 0x01, 0xf7, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq vc211a_enable[] = {
- {EM28XX_R08_GPIO, 0xff, 0x07, 10},
- {EM28XX_R08_GPIO, 0xff, 0x0f, 10},
- {EM28XX_R08_GPIO, 0xff, 0x0b, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0x07, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0x0f, 10},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0x0b, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq dikom_dk300_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2820_R08_GPIO_CTRL, 0x6e, ~EM_GPIO_4, 10},
{EM2880_R04_GPO, 0x08, 0xff, 10},
{ -1, -1, -1, -1},
};
@@ -286,14 +285,14 @@
/* Reset for the most [digital] boards */
static struct em28xx_reg_seq leadership_digital[] = {
- {EM2874_R80_GPIO, 0x70, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0x70, 0xff, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq leadership_reset[] = {
- {EM2874_R80_GPIO, 0xf0, 0xff, 10},
- {EM2874_R80_GPIO, 0xb0, 0xff, 10},
- {EM2874_R80_GPIO, 0xf0, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xb0, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf0, 0xff, 10},
{ -1, -1, -1, -1},
};
@@ -302,25 +301,25 @@
* GPIO_7 - LED
*/
static struct em28xx_reg_seq pctv_290e[] = {
- {EM2874_R80_GPIO, 0x00, 0xff, 80},
- {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
- {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 80},
+ {EM2874_R80_GPIO_P0_CTRL, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
{-1, -1, -1, -1},
};
#if 0
static struct em28xx_reg_seq terratec_h5_gpio[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- {EM2874_R80_GPIO, 0xf2, 0xff, 50},
- {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 50},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq terratec_h5_digital[] = {
- {EM2874_R80_GPIO, 0xf6, 0xff, 10},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 10},
{ -1, -1, -1, -1},
};
#endif
@@ -336,51 +335,52 @@
* GPIO_7 - LED (green LED)
*/
static struct em28xx_reg_seq pctv_460e[] = {
- {EM2874_R80_GPIO, 0x01, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0x01, 0xff, 50},
{0x0d, 0xff, 0xff, 50},
- {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x41, 0xff, 50}, /* GPIO_6=1 */
{0x0d, 0x42, 0xff, 50},
- {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x61, 0xff, 50}, /* GPIO_5=1 */
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq c3tech_digital_duo_digital[] = {
- {EM2874_R80_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xfd, 0xff, 10}, /* xc5000 reset */
- {EM2874_R80_GPIO, 0xf9, 0xff, 35},
- {EM2874_R80_GPIO, 0xfd, 0xff, 10},
- {EM2874_R80_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xfe, 0xff, 10},
- {EM2874_R80_GPIO, 0xbe, 0xff, 10},
- {EM2874_R80_GPIO, 0xfe, 0xff, 20},
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 10}, /* xc5000 reset */
+ {EM2874_R80_GPIO_P0_CTRL, 0xf9, 0xff, 35},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfd, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xbe, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 20},
{ -1, -1, -1, -1},
};
#if 0
static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
- {EM2874_R80_GPIO, 0x6f, 0xff, 10},
- {EM2874_R80_GPIO, 0x4f, 0xff, 10}, /* xc5000 reset */
- {EM2874_R80_GPIO, 0x6f, 0xff, 10},
- {EM2874_R80_GPIO, 0x4f, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0x4f, 0xff, 10}, /* xc5000 reset */
+ {EM2874_R80_GPIO_P0_CTRL, 0x6f, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0x4f, 0xff, 10},
{ -1, -1, -1, -1},
};
static struct em28xx_reg_seq hauppauge_930c_digital[] = {
- {EM2874_R80_GPIO, 0xf6, 0xff, 10},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 10},
{ -1, -1, -1, -1},
};
#endif
/* 1b80:e425 MaxMedia UB425-TC
+ * 1b80:e1cc Delock 61959
* GPIO_6 - demod reset, 0=active
* GPIO_7 - LED, 0=active
*/
static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
- {EM2874_R80_GPIO, 0x83, 0xff, 100},
- {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
- {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x83, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
{-1, -1, -1, -1},
};
@@ -391,9 +391,9 @@
* GPIO_7: LED, 1=active
*/
static struct em28xx_reg_seq pctv_510e[] = {
- {EM2874_R80_GPIO, 0x10, 0xff, 100},
- {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
- {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
{ -1, -1, -1, -1},
};
@@ -404,10 +404,10 @@
* GPIO_7: LED, 1=active
*/
static struct em28xx_reg_seq pctv_520e[] = {
- {EM2874_R80_GPIO, 0x10, 0xff, 100},
- {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
- {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
- {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO_P0_CTRL, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
{ -1, -1, -1, -1},
};
@@ -2017,6 +2017,19 @@
.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
},
+ /* 1b80:e1cc Delock 61959
+ * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2
+ * mostly the same as MaxMedia UB-425-TC but different remote */
+ [EM2874_BOARD_DELOCK_61959] = {
+ .name = "Delock 61959",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = maxmedia_ub425_tc,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_DELOCK_61959,
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -2178,6 +2191,8 @@
.driver_info = EM2884_BOARD_PCTV_510E },
{ USB_DEVICE(0x2013, 0x0251),
.driver_info = EM2884_BOARD_PCTV_520E },
+ { USB_DEVICE(0x1b80, 0xe1cc),
+ .driver_info = EM2874_BOARD_DELOCK_61959 },
{ },
};
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -2284,9 +2299,9 @@
break;
case EM2861_BOARD_KWORLD_PVRTV_300U:
case EM2880_BOARD_KWORLD_DVB_305U:
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0x6d);
msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0x7d);
msleep(10);
break;
case EM2870_BOARD_COMPRO_VIDEOMATE:
@@ -2296,45 +2311,45 @@
msleep(10);
em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xdc);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc);
mdelay(70);
break;
case EM2870_BOARD_TERRATEC_XS_MT2060:
/* this device needs some gpio writes to get the DVB-T
demod work */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
mdelay(70);
break;
case EM2870_BOARD_PINNACLE_PCTV_DVB:
/* this device needs some gpio writes to get the
DVB-T demod work */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde);
mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
mdelay(70);
break;
case EM2820_BOARD_GADMEI_UTV310:
case EM2820_BOARD_MSI_VOX_USB_2:
/* enables audio for that devices */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
break;
case EM2882_BOARD_KWORLD_ATSC_315U:
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
msleep(10);
em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
msleep(10);
@@ -2360,13 +2375,13 @@
break;
case EM2820_BOARD_IODATA_GVMVP_SZ:
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
msleep(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7);
msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe);
msleep(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
msleep(70);
break;
}
@@ -2653,7 +2668,7 @@
dev->tuner_type = tv.tuner_type;
- if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
+ if (tv.audio_processor == TVEEPROM_AUDPROC_MSP) {
dev->i2s_speed = 2048000;
dev->board.has_msp34xx = 1;
}
@@ -2662,12 +2677,12 @@
case EM2882_BOARD_KWORLD_ATSC_315U:
em28xx_write_reg(dev, 0x0d, 0x42);
msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd);
msleep(10);
break;
case EM2820_BOARD_KWORLD_PVRTV2800RF:
/* GPIO enables sound on KWORLD PVR TV 2800RF */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
+ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf9);
break;
case EM2820_BOARD_UNKNOWN:
case EM2800_BOARD_UNKNOWN:
@@ -2881,10 +2896,6 @@
em28xx_set_model(dev);
- /* Set the default GPO/GPIO for legacy devices */
- dev->reg_gpo_num = EM2880_R04_GPO;
- dev->reg_gpio_num = EM28XX_R08_GPIO;
-
dev->wait_after_write = 5;
/* Based on the Chip ID, set the device configuration */
@@ -2932,13 +2943,11 @@
break;
case CHIP_ID_EM2874:
chip_name = "em2874";
- dev->reg_gpio_num = EM2874_R80_GPIO;
dev->wait_after_write = 0;
dev->eeprom_addrwidth_16bit = 1;
break;
case CHIP_ID_EM28174:
chip_name = "em28174";
- dev->reg_gpio_num = EM2874_R80_GPIO;
dev->wait_after_write = 0;
dev->eeprom_addrwidth_16bit = 1;
break;
@@ -2948,7 +2957,6 @@
break;
case CHIP_ID_EM2884:
chip_name = "em2884";
- dev->reg_gpio_num = EM2874_R80_GPIO;
dev->wait_after_write = 0;
dev->eeprom_addrwidth_16bit = 1;
break;
@@ -2977,11 +2985,6 @@
return 0;
}
- /* Prepopulate cached GPO register content */
- retval = em28xx_read_reg(dev, dev->reg_gpo_num);
- if (retval >= 0)
- dev->reg_gpo = retval;
-
em28xx_pre_card_setup(dev);
if (!dev->board.is_em2800) {
@@ -3071,7 +3074,7 @@
if (dev->board.has_msp34xx) {
/* Send a reset to other chips via gpio */
- retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ retval = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7);
if (retval < 0) {
em28xx_errdev("%s: em28xx_write_reg - "
"msp34xx(1) failed! error [%d]\n",
@@ -3080,7 +3083,7 @@
}
msleep(3);
- retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ retval = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff);
if (retval < 0) {
em28xx_errdev("%s: em28xx_write_reg - "
"msp34xx(2) failed! error [%d]\n",
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index a802128..fc157af 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -193,23 +193,7 @@
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
{
- int rc;
-
- rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
-
- /* Stores GPO/GPIO values at the cache, if changed
- Only write values should be stored, since input on a GPIO
- register will return the input bits.
- Not sure what happens on reading GPO register.
- */
- if (rc >= 0) {
- if (reg == dev->reg_gpo_num)
- dev->reg_gpo = buf[0];
- else if (reg == dev->reg_gpio_num)
- dev->reg_gpio = buf[0];
- }
-
- return rc;
+ return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
}
EXPORT_SYMBOL_GPL(em28xx_write_regs);
@@ -231,14 +215,7 @@
int oldval;
u8 newval;
- /* Uses cache for gpo/gpio registers */
- if (reg == dev->reg_gpo_num)
- oldval = dev->reg_gpo;
- else if (reg == dev->reg_gpio_num)
- oldval = dev->reg_gpio;
- else
- oldval = em28xx_read_reg(dev, reg);
-
+ oldval = em28xx_read_reg(dev, reg);
if (oldval < 0)
return oldval;
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index b22f8fe..bb1e8dc 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -421,23 +421,23 @@
int i;
struct em28xx_reg_seq hauppauge_hvr930c_init[] = {
- {EM2874_R80_GPIO, 0xff, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xfb, 0xff, 0x32},
- {EM2874_R80_GPIO, 0xff, 0xff, 0xb8},
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0x65},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfb, 0xff, 0x32},
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0xb8},
{ -1, -1, -1, -1},
};
struct em28xx_reg_seq hauppauge_hvr930c_end[] = {
- {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
- {EM2874_R80_GPIO, 0xaf, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x76},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
- {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x40},
+ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x01},
+ {EM2874_R80_GPIO_P0_CTRL, 0xaf, 0xff, 0x65},
+ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x76},
+ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x01},
+ {EM2874_R80_GPIO_P0_CTRL, 0xcf, 0xff, 0x0b},
+ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x40},
- {EM2874_R80_GPIO, 0xcf, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
+ {EM2874_R80_GPIO_P0_CTRL, 0xcf, 0xff, 0x65},
+ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x65},
+ {EM2874_R80_GPIO_P0_CTRL, 0xcf, 0xff, 0x0b},
+ {EM2874_R80_GPIO_P0_CTRL, 0xef, 0xff, 0x65},
{ -1, -1, -1, -1},
};
@@ -487,16 +487,16 @@
{
int i;
struct em28xx_reg_seq terratec_h5_init[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- {EM2874_R80_GPIO, 0xf2, 0xff, 50},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100},
{ -1, -1, -1, -1},
};
struct em28xx_reg_seq terratec_h5_end[] = {
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 50},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
{ -1, -1, -1, -1},
};
struct {
@@ -543,15 +543,15 @@
* 0xb6: unknown (does not affect DVB-T).
*/
struct em28xx_reg_seq terratec_htc_stick_init[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- {EM2874_R80_GPIO, 0xe6, 0xff, 50},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 100},
{ -1, -1, -1, -1},
};
struct em28xx_reg_seq terratec_htc_stick_end[] = {
- {EM2874_R80_GPIO, 0xb6, 0xff, 100},
- {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xb6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf6, 0xff, 50},
{ -1, -1, -1, -1},
};
@@ -590,16 +590,16 @@
int i;
struct em28xx_reg_seq terratec_htc_usb_xs_init[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xb2, 0xff, 100},
- {EM2874_R80_GPIO, 0xb2, 0xff, 50},
- {EM2874_R80_GPIO, 0xb6, 0xff, 100},
+ {EM2820_R08_GPIO_CTRL, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xb2, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xb2, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xb6, 0xff, 100},
{ -1, -1, -1, -1},
};
struct em28xx_reg_seq terratec_htc_usb_xs_end[] = {
- {EM2874_R80_GPIO, 0xa6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 50},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xa6, 0xff, 50},
+ {EM2874_R80_GPIO_P0_CTRL, 0xe6, 0xff, 100},
{ -1, -1, -1, -1},
};
@@ -1216,6 +1216,7 @@
dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus],
&em28xx_a8293_config);
break;
+ case EM2874_BOARD_DELOCK_61959:
case EM2874_BOARD_MAXMEDIA_UB425_TC:
/* attach demodulator */
dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
@@ -1235,8 +1236,8 @@
}
/* TODO: we need drx-3913k firmware in order to support DVB-T */
- em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \
- "driver version\n");
+ em28xx_info("MaxMedia UB425-TC/Delock 61959: only DVB-C " \
+ "supported by that driver version\n");
break;
case EM2884_BOARD_PCTV_510E:
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 466b19d..ea181e4 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -32,7 +32,6 @@
#define EM28XX_SNAPSHOT_KEY KEY_CAMERA
#define EM28XX_SBUTTON_QUERY_INTERVAL 500
-#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20
static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index 622871d..0e04778 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -49,8 +49,9 @@
/* GPIO/GPO registers */
-#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
-#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */
+#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
+#define EM2820_R08_GPIO_CTRL 0x08 /* em2820-em2873/83 only */
+#define EM2820_R09_GPIO_STATE 0x09 /* em2820-em2873/83 only */
#define EM28XX_R06_I2C_CLK 0x06
@@ -67,7 +68,8 @@
#define EM28XX_R0A_CHIPID 0x0a
-#define EM28XX_R0C_USBSUSP 0x0c /* */
+#define EM28XX_R0C_USBSUSP 0x0c
+#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20 /* 1=button pressed, needs reset */
#define EM28XX_R0E_AUDIOSRC 0x0e
#define EM28XX_R0F_XCLK 0x0f
@@ -193,7 +195,20 @@
#define EM2874_R50_IR_CONFIG 0x50
#define EM2874_R51_IR 0x51
#define EM2874_R5F_TS_ENABLE 0x5f
-#define EM2874_R80_GPIO 0x80
+
+/* em2874/174/84, em25xx, em276x/7x/8x GPIO registers */
+/*
+ * NOTE: not all ports are bonded out;
+ * Some ports are multiplexed with special function I/O
+ */
+#define EM2874_R80_GPIO_P0_CTRL 0x80
+#define EM2874_R81_GPIO_P1_CTRL 0x81
+#define EM2874_R82_GPIO_P2_CTRL 0x82
+#define EM2874_R83_GPIO_P3_CTRL 0x83
+#define EM2874_R84_GPIO_P0_STATE 0x84
+#define EM2874_R85_GPIO_P1_STATE 0x85
+#define EM2874_R86_GPIO_P2_STATE 0x86
+#define EM2874_R87_GPIO_P3_STATE 0x87
/* em2874 IR config register (0x50) */
#define EM2874_IR_NEC 0x00
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 32d60e5..1a577ed 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -41,7 +41,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
@@ -1309,28 +1308,6 @@
return 0;
}
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_BRIDGE) {
- if (chip->match.addr > 1)
- return -EINVAL;
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
-
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vidioc_g_chip_info(struct file *file, void *priv,
struct v4l2_dbg_chip_info *chip)
@@ -1366,14 +1343,9 @@
struct em28xx *dev = fh->dev;
int ret;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_BRIDGE:
- if (reg->match.addr > 1)
- return -EINVAL;
- if (!reg->match.addr)
- break;
- /* fall-through */
- case V4L2_CHIP_MATCH_AC97:
+ if (reg->match.addr > 1)
+ return -EINVAL;
+ if (reg->match.addr) {
ret = em28xx_read_ac97(dev, reg->reg);
if (ret < 0)
return ret;
@@ -1381,15 +1353,6 @@
reg->val = ret;
reg->size = 1;
return 0;
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /* TODO: is this correct? */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- default:
- return -EINVAL;
}
/* Match host */
@@ -1421,25 +1384,10 @@
struct em28xx *dev = fh->dev;
__le16 buf;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_BRIDGE:
- if (reg->match.addr > 1)
- return -EINVAL;
- if (!reg->match.addr)
- break;
- /* fall-through */
- case V4L2_CHIP_MATCH_AC97:
- return em28xx_write_ac97(dev, reg->reg, reg->val);
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /* TODO: is this correct? */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- default:
+ if (reg->match.addr > 1)
return -EINVAL;
- }
+ if (reg->match.addr)
+ return em28xx_write_ac97(dev, reg->reg, reg->val);
/* Match host */
buf = cpu_to_le16(reg->val);
@@ -1795,7 +1743,6 @@
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_chip_info = vidioc_g_chip_info,
.vidioc_g_register = vidioc_g_register,
@@ -1826,7 +1773,6 @@
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_chip_info = vidioc_g_chip_info,
.vidioc_g_register = vidioc_g_register,
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index a9323b6..205e903 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -130,6 +130,7 @@
#define EM2884_BOARD_PCTV_520E 86
#define EM2884_BOARD_TERRATEC_HTC_USB_XS 87
#define EM2884_BOARD_C3TECH_DIGITAL_DUO 88
+#define EM2874_BOARD_DELOCK_61959 89
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -636,12 +637,6 @@
enum em28xx_mode mode;
- /* register numbers for GPO/GPIO registers */
- u16 reg_gpo_num, reg_gpio_num;
-
- /* Caches GPO and GPIO registers */
- unsigned char reg_gpo, reg_gpio;
-
/* Snapshot button */
char snapshot_button_path[30]; /* path of the input dev */
struct input_dev *sbutton_input_dev;
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index 5995ec4..b7ae872 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -1029,8 +1029,19 @@
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_chip_info(struct file *file, void *priv,
+ struct v4l2_dbg_chip_info *chip)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->sd_desc->get_chip_info)
+ return gspca_dev->sd_desc->get_chip_info(gspca_dev, chip);
+ return chip->match.addr ? -EINVAL : 0;
+}
+
static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
+ struct v4l2_dbg_register *reg)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
@@ -1039,7 +1050,7 @@
}
static int vidioc_s_register(struct file *file, void *priv,
- const struct v4l2_dbg_register *reg)
+ const struct v4l2_dbg_register *reg)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
@@ -1048,15 +1059,6 @@
}
#endif
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dev->usb_err = 0;
- return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
-}
-
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *fmtdesc)
{
@@ -1974,10 +1976,10 @@
.vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_chip_info = vidioc_g_chip_info,
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
@@ -2086,14 +2088,10 @@
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
- if (!gspca_dev->sd_desc->get_chip_ident)
- v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT);
#ifdef CONFIG_VIDEO_ADV_DEBUG
- if (!gspca_dev->sd_desc->get_chip_ident ||
- !gspca_dev->sd_desc->get_register)
+ if (!gspca_dev->sd_desc->get_register)
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
- if (!gspca_dev->sd_desc->get_chip_ident ||
- !gspca_dev->sd_desc->set_register)
+ if (!gspca_dev->sd_desc->set_register)
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
#endif
if (!gspca_dev->sd_desc->get_jcomp)
diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h
index ef8efeb..ac0b11f 100644
--- a/drivers/media/usb/gspca/gspca.h
+++ b/drivers/media/usb/gspca/gspca.h
@@ -78,8 +78,8 @@
struct v4l2_dbg_register *);
typedef int (*cam_set_reg_op) (struct gspca_dev *,
const struct v4l2_dbg_register *);
-typedef int (*cam_ident_op) (struct gspca_dev *,
- struct v4l2_dbg_chip_ident *);
+typedef int (*cam_chip_info_op) (struct gspca_dev *,
+ struct v4l2_dbg_chip_info *);
typedef void (*cam_streamparm_op) (struct gspca_dev *,
struct v4l2_streamparm *);
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
@@ -112,8 +112,8 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
cam_set_reg_op set_register;
cam_get_reg_op get_register;
+ cam_chip_info_op get_chip_info;
#endif
- cam_ident_op get_chip_ident;
#if IS_ENABLED(CONFIG_INPUT)
cam_int_pkt_op int_pkt_scan;
/* other_input makes the gspca core create gspca_dev->input even when
diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c
index 6008c8d..a915096 100644
--- a/drivers/media/usb/gspca/pac7302.c
+++ b/drivers/media/usb/gspca/pac7302.c
@@ -93,7 +93,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/input.h>
-#include <media/v4l2-chip-ident.h>
#include "gspca.h"
/* Include pac common sof detection functions */
#include "pac_common.h"
@@ -849,8 +848,7 @@
* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
* long on the USB bus)
*/
- if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
- reg->match.addr == 0 &&
+ if (reg->match.addr == 0 &&
(reg->reg < 0x000000ff) &&
(reg->val <= 0x000000ff)
) {
@@ -871,20 +869,6 @@
}
return gspca_dev->usb_err;
}
-
-static int sd_chip_ident(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_chip_ident *chip)
-{
- int ret = -EINVAL;
-
- if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
- chip->match.addr == 0) {
- chip->revision = 0;
- chip->ident = V4L2_IDENT_UNKNOWN;
- ret = 0;
- }
- return ret;
-}
#endif
#if IS_ENABLED(CONFIG_INPUT)
@@ -931,7 +915,6 @@
.dq_callback = do_autogain,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.set_register = sd_dbg_s_register,
- .get_chip_ident = sd_chip_ident,
#endif
#if IS_ENABLED(CONFIG_INPUT)
.int_pkt_scan = sd_int_pkt_scan,
diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c
index ead9a1f..f4453d5 100644
--- a/drivers/media/usb/gspca/sn9c20x.c
+++ b/drivers/media/usb/gspca/sn9c20x.c
@@ -27,7 +27,6 @@
#include "gspca.h"
#include "jpeg.h"
-#include <media/v4l2-chip-ident.h>
#include <linux/dmi.h>
MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
@@ -582,22 +581,6 @@
4, 2, 0, -1, -3, -5, -7, -9, -11
};
-static const u16 i2c_ident[] = {
- V4L2_IDENT_OV9650,
- V4L2_IDENT_OV9655,
- V4L2_IDENT_SOI968,
- V4L2_IDENT_OV7660,
- V4L2_IDENT_OV7670,
- V4L2_IDENT_MT9V011,
- V4L2_IDENT_MT9V111,
- V4L2_IDENT_MT9V112,
- V4L2_IDENT_MT9M001C12ST,
- V4L2_IDENT_MT9M111,
- V4L2_IDENT_MT9M112,
- V4L2_IDENT_HV7131R,
-[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN,
-};
-
static const u16 bridge_init[][2] = {
{0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
{0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
@@ -1574,21 +1557,19 @@
{
struct sd *sd = (struct sd *) gspca_dev;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- if (reg->match.addr != 0)
- return -EINVAL;
+ reg->size = 1;
+ switch (reg->match.addr) {
+ case 0:
if (reg->reg < 0x1000 || reg->reg > 0x11ff)
return -EINVAL;
reg_r(gspca_dev, reg->reg, 1);
reg->val = gspca_dev->usb_buf[0];
return gspca_dev->usb_err;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- if (reg->match.addr != sd->i2c_addr)
- return -EINVAL;
+ case 1:
if (sd->sensor >= SENSOR_MT9V011 &&
sd->sensor <= SENSOR_MT9M112) {
i2c_r2(gspca_dev, reg->reg, (u16 *) ®->val);
+ reg->size = 2;
} else {
i2c_r1(gspca_dev, reg->reg, (u8 *) ®->val);
}
@@ -1602,17 +1583,13 @@
{
struct sd *sd = (struct sd *) gspca_dev;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- if (reg->match.addr != 0)
- return -EINVAL;
+ switch (reg->match.addr) {
+ case 0:
if (reg->reg < 0x1000 || reg->reg > 0x11ff)
return -EINVAL;
reg_w1(gspca_dev, reg->reg, reg->val);
return gspca_dev->usb_err;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- if (reg->match.addr != sd->i2c_addr)
- return -EINVAL;
+ case 1:
if (sd->sensor >= SENSOR_MT9V011 &&
sd->sensor <= SENSOR_MT9M112) {
i2c_w2(gspca_dev, reg->reg, reg->val);
@@ -1623,29 +1600,17 @@
}
return -EINVAL;
}
-#endif
-static int sd_chip_ident(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_chip_ident *chip)
+static int sd_chip_info(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_info *chip)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (chip->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- if (chip->match.addr != 0)
- return -EINVAL;
- chip->revision = 0;
- chip->ident = V4L2_IDENT_SN9C20X;
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- if (chip->match.addr != sd->i2c_addr)
- return -EINVAL;
- chip->revision = 0;
- chip->ident = i2c_ident[sd->sensor];
- return 0;
- }
- return -EINVAL;
+ if (chip->match.addr > 1)
+ return -EINVAL;
+ if (chip->match.addr == 1)
+ strlcpy(chip->name, "sensor", sizeof(chip->name));
+ return 0;
}
+#endif
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
@@ -2356,8 +2321,8 @@
#ifdef CONFIG_VIDEO_ADV_DEBUG
.set_register = sd_dbg_s_register,
.get_register = sd_dbg_g_register,
+ .get_chip_info = sd_chip_info,
#endif
- .get_chip_ident = sd_chip_ident,
};
#define SN9C20X(sensor, i2c_addr, flags) \
diff --git a/drivers/media/usb/hdpvr/Kconfig b/drivers/media/usb/hdpvr/Kconfig
index de247f3..d73d9a1 100644
--- a/drivers/media/usb/hdpvr/Kconfig
+++ b/drivers/media/usb/hdpvr/Kconfig
@@ -1,7 +1,7 @@
config VIDEO_HDPVR
tristate "Hauppauge HD PVR support"
- depends on VIDEO_DEV
+ depends on VIDEO_DEV && VIDEO_V4L2
---help---
This is a video4linux driver for Hauppauge's HD PVR USB device.
diff --git a/drivers/media/usb/hdpvr/hdpvr-control.c b/drivers/media/usb/hdpvr/hdpvr-control.c
index ae8f229..6053661 100644
--- a/drivers/media/usb/hdpvr/hdpvr-control.c
+++ b/drivers/media/usb/hdpvr/hdpvr-control.c
@@ -45,20 +45,11 @@
return ret < 0 ? ret : 0;
}
-struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev)
+int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vidinf)
{
- struct hdpvr_video_info *vidinf = NULL;
-#ifdef HDPVR_DEBUG
- char print_buf[15];
-#endif
int ret;
- vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL);
- if (!vidinf) {
- v4l2_err(&dev->v4l2_dev, "out of memory\n");
- goto err;
- }
-
+ vidinf->valid = false;
mutex_lock(&dev->usbc_mutex);
ret = usb_control_msg(dev->udev,
usb_rcvctrlpipe(dev->udev, 0),
@@ -66,14 +57,10 @@
0x1400, 0x0003,
dev->usbc_buf, 5,
1000);
- if (ret == 5) {
- vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
- vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2];
- vidinf->fps = dev->usbc_buf[4];
- }
#ifdef HDPVR_DEBUG
if (hdpvr_debug & MSG_INFO) {
+ char print_buf[15];
hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf,
sizeof(print_buf), 0);
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
@@ -82,12 +69,15 @@
#endif
mutex_unlock(&dev->usbc_mutex);
- if (!vidinf->width || !vidinf->height || !vidinf->fps) {
- kfree(vidinf);
- vidinf = NULL;
- }
-err:
- return vidinf;
+ if (ret < 0)
+ return ret;
+
+ vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
+ vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2];
+ vidinf->fps = dev->usbc_buf[4];
+ vidinf->valid = vidinf->width && vidinf->height && vidinf->fps;
+
+ return 0;
}
int get_input_lines_info(struct hdpvr_device *dev)
diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index 8247c19..cb69405 100644
--- a/drivers/media/usb/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -220,7 +220,6 @@
{
int ret;
u8 *buf;
- struct hdpvr_video_info *vidinf;
if (device_authorization(dev))
return -EACCES;
@@ -242,13 +241,6 @@
"control request returned %d\n", ret);
mutex_unlock(&dev->usbc_mutex);
- vidinf = get_video_info(dev);
- if (!vidinf)
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "no valid video signal or device init failed\n");
- else
- kfree(vidinf);
-
/* enable fan and bling leds */
mutex_lock(&dev->usbc_mutex);
buf[0] = 0x1;
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
index 774ba0e..4f8567a 100644
--- a/drivers/media/usb/hdpvr/hdpvr-video.c
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -277,44 +277,50 @@
static int hdpvr_start_streaming(struct hdpvr_device *dev)
{
int ret;
- struct hdpvr_video_info *vidinf;
+ struct hdpvr_video_info vidinf;
if (dev->status == STATUS_STREAMING)
return 0;
- else if (dev->status != STATUS_IDLE)
+ if (dev->status != STATUS_IDLE)
return -EAGAIN;
- vidinf = get_video_info(dev);
+ ret = get_video_info(dev, &vidinf);
+ if (ret < 0)
+ return ret;
- if (vidinf) {
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "video signal: %dx%d@%dhz\n", vidinf->width,
- vidinf->height, vidinf->fps);
- kfree(vidinf);
-
- /* start streaming 2 request */
- ret = usb_control_msg(dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- 0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "encoder start control request returned %d\n", ret);
-
- hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
-
- dev->status = STATUS_STREAMING;
-
- INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
- queue_work(dev->workqueue, &dev->worker);
-
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "streaming started\n");
-
- return 0;
+ if (!vidinf.valid) {
+ msleep(250);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "no video signal at input %d\n", dev->options.video_input);
+ return -EAGAIN;
}
- msleep(250);
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "no video signal at input %d\n", dev->options.video_input);
- return -EAGAIN;
+
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "video signal: %dx%d@%dhz\n", vidinf.width,
+ vidinf.height, vidinf.fps);
+
+ /* start streaming 2 request */
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "encoder start control request returned %d\n", ret);
+ if (ret < 0)
+ return ret;
+
+ ret = hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
+ if (ret)
+ return ret;
+
+ dev->status = STATUS_STREAMING;
+
+ INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
+ queue_work(dev->workqueue, &dev->worker);
+
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "streaming started\n");
+
+ return 0;
}
@@ -606,22 +612,20 @@
static int vidioc_querystd(struct file *file, void *_fh, v4l2_std_id *a)
{
struct hdpvr_device *dev = video_drvdata(file);
- struct hdpvr_video_info *vid_info;
+ struct hdpvr_video_info vid_info;
struct hdpvr_fh *fh = _fh;
+ int ret;
- *a = V4L2_STD_ALL;
+ *a = V4L2_STD_UNKNOWN;
if (dev->options.video_input == HDPVR_COMPONENT)
return fh->legacy_mode ? 0 : -ENODATA;
- vid_info = get_video_info(dev);
- if (vid_info == NULL)
- return 0;
- if (vid_info->width == 720 &&
- (vid_info->height == 480 || vid_info->height == 576)) {
- *a = (vid_info->height == 480) ?
+ ret = get_video_info(dev, &vid_info);
+ if (vid_info.valid && vid_info.width == 720 &&
+ (vid_info.height == 480 || vid_info.height == 576)) {
+ *a = (vid_info.height == 480) ?
V4L2_STD_525_60 : V4L2_STD_625_50;
}
- kfree(vid_info);
- return 0;
+ return ret;
}
static int vidioc_s_dv_timings(struct file *file, void *_fh,
@@ -665,7 +669,7 @@
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
- struct hdpvr_video_info *vid_info;
+ struct hdpvr_video_info vid_info;
bool interlaced;
int ret = 0;
int i;
@@ -673,10 +677,12 @@
fh->legacy_mode = false;
if (dev->options.video_input)
return -ENODATA;
- vid_info = get_video_info(dev);
- if (vid_info == NULL)
+ ret = get_video_info(dev, &vid_info);
+ if (ret)
+ return ret;
+ if (!vid_info.valid)
return -ENOLCK;
- interlaced = vid_info->fps <= 30;
+ interlaced = vid_info.fps <= 30;
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) {
const struct v4l2_bt_timings *bt = &hdpvr_dv_timings[i].bt;
unsigned hsize;
@@ -688,17 +694,17 @@
bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch +
bt->height;
fps = (unsigned)bt->pixelclock / (hsize * vsize);
- if (bt->width != vid_info->width ||
- bt->height != vid_info->height ||
+ if (bt->width != vid_info.width ||
+ bt->height != vid_info.height ||
bt->interlaced != interlaced ||
- (fps != vid_info->fps && fps + 1 != vid_info->fps))
+ (fps != vid_info.fps && fps + 1 != vid_info.fps))
continue;
*timings = hdpvr_dv_timings[i];
break;
}
if (i == ARRAY_SIZE(hdpvr_dv_timings))
ret = -ERANGE;
- kfree(vid_info);
+
return ret;
}
@@ -988,6 +994,7 @@
{
struct hdpvr_device *dev = video_drvdata(file);
struct hdpvr_fh *fh = _fh;
+ int ret;
/*
* The original driver would always returns the current detected
@@ -1000,14 +1007,15 @@
* last set format.
*/
if (fh->legacy_mode) {
- struct hdpvr_video_info *vid_info;
+ struct hdpvr_video_info vid_info;
- vid_info = get_video_info(dev);
- if (!vid_info)
+ ret = get_video_info(dev, &vid_info);
+ if (ret < 0)
+ return ret;
+ if (!vid_info.valid)
return -EFAULT;
- f->fmt.pix.width = vid_info->width;
- f->fmt.pix.height = vid_info->height;
- kfree(vid_info);
+ f->fmt.pix.width = vid_info.width;
+ f->fmt.pix.height = vid_info.height;
} else {
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h
index 1478f3d..dc685d4 100644
--- a/drivers/media/usb/hdpvr/hdpvr.h
+++ b/drivers/media/usb/hdpvr/hdpvr.h
@@ -154,6 +154,7 @@
u16 width;
u16 height;
u8 fps;
+ bool valid;
};
enum {
@@ -303,7 +304,7 @@
int hdpvr_config_call(struct hdpvr_device *dev, uint value,
unsigned char valbuf);
-struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev);
+int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vid_info);
/* :0 s b8 81 1800 0003 0003 3 < */
/* :0 0 3 = 0301ff */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index e11267f..c4d51d7 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -2704,6 +2704,10 @@
pvr2_hdw_render_useless(hdw);
}
+void pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *hdw, struct video_device *vdev)
+{
+ vdev->v4l2_dev = &hdw->v4l2_dev;
+}
/* Destroy hardware interaction structure */
void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
@@ -5162,41 +5166,3 @@
} while(0); LOCK_GIVE(hdw->ctl_lock);
return result;
}
-
-
-int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
- const struct v4l2_dbg_match *match, u64 reg_id,
- int setFl, u64 *val_ptr)
-{
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- struct v4l2_dbg_register req;
- int stat = 0;
- int okFl = 0;
-
- if (!capable(CAP_SYS_ADMIN)) return -EPERM;
-
- req.match = *match;
- req.reg = reg_id;
- if (setFl) req.val = *val_ptr;
- /* It would be nice to know if a sub-device answered the request */
- v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req);
- if (!setFl) *val_ptr = req.val;
- if (okFl) {
- return stat;
- }
- return -EINVAL;
-#else
- return -ENOSYS;
-#endif
-}
-
-
-/*
- Stuff for Emacs to see, in order to encourage consistent editing style:
- *** Local Variables: ***
- *** mode: c ***
- *** fill-column: 75 ***
- *** tab-width: 8 ***
- *** c-basic-offset: 8 ***
- *** End: ***
- */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
index 91bae93..4184707 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
@@ -22,6 +22,7 @@
#include <linux/usb.h>
#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
#include "pvrusb2-io.h"
#include "pvrusb2-ctrl.h"
@@ -138,6 +139,9 @@
/* Called when hardware has been unplugged */
void pvr2_hdw_disconnect(struct pvr2_hdw *);
+/* Sets v4l2_dev of a video_device struct */
+void pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *, struct video_device *);
+
/* Get the number of defined controls */
unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *);
@@ -234,15 +238,6 @@
void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,
enum pvr2_v4l_type index,int);
-/* Direct read/write access to chip's registers:
- match - specify criteria to identify target chip (this is a v4l dbg struct)
- reg_id - register number to access
- setFl - true to set the register, false to read it
- val_ptr - storage location for source / result. */
-int pvr2_hdw_register_access(struct pvr2_hdw *,
- const struct v4l2_dbg_match *match, u64 reg_id,
- int setFl, u64 *val_ptr);
-
/* The following entry points are all lower level things you normally don't
want to worry about. */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c
index 20b6ae0..1e35474 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-io.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c
@@ -354,9 +354,9 @@
if (scnt < sp->buffer_slot_count) {
struct pvr2_buffer **nb = NULL;
if (scnt) {
- nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ nb = kmemdup(sp->buffers, scnt * sizeof(*nb),
+ GFP_KERNEL);
if (!nb) return -ENOMEM;
- memcpy(nb,sp->buffers,scnt * sizeof(*nb));
}
kfree(sp->buffers);
sp->buffers = nb;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
index a8a65fa..7c280f3 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -31,6 +31,7 @@
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
@@ -800,36 +801,6 @@
return 0;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- u64 val;
- int ret;
-
- ret = pvr2_hdw_register_access(
- hdw, &req->match, req->reg,
- 0, &val);
- req->val = val;
- return ret;
-}
-
-static int pvr2_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *req)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- u64 val;
- int ret;
-
- val = req->val;
- ret = pvr2_hdw_register_access(
- hdw, &req->match, req->reg,
- 1, &val);
- return ret;
-}
-#endif
-
static const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
.vidioc_querycap = pvr2_querycap,
.vidioc_g_priority = pvr2_g_priority,
@@ -864,10 +835,6 @@
.vidioc_g_ext_ctrls = pvr2_g_ext_ctrls,
.vidioc_s_ext_ctrls = pvr2_s_ext_ctrls,
.vidioc_try_ext_ctrls = pvr2_try_ext_ctrls,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = pvr2_g_register,
- .vidioc_s_register = pvr2_s_register,
-#endif
};
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
@@ -904,8 +871,8 @@
static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
{
if (!dip) return;
- if (!dip->devbase.parent) return;
- dip->devbase.parent = NULL;
+ if (!dip->devbase.v4l2_dev->dev) return;
+ dip->devbase.v4l2_dev->dev = NULL;
device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
}
@@ -1298,7 +1265,6 @@
struct pvr2_v4l2 *vp,
int v4l_type)
{
- struct usb_device *usbdev;
int mindevnum;
int unit_number;
struct pvr2_hdw *hdw;
@@ -1306,7 +1272,6 @@
dip->v4lp = vp;
hdw = vp->channel.mc_head->hdw;
- usbdev = pvr2_hdw_get_dev(hdw);
dip->v4l_type = v4l_type;
switch (v4l_type) {
case VFL_TYPE_GRABBER:
@@ -1355,7 +1320,7 @@
if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
mindevnum = nr_ptr[unit_number];
}
- dip->devbase.parent = &usbdev->dev;
+ pvr2_hdw_set_v4l2_dev(hdw, &dip->devbase);
if ((video_register_device(&dip->devbase,
dip->v4l_type, mindevnum) < 0) &&
(video_register_device(&dip->devbase,
diff --git a/drivers/media/usb/sn9c102/sn9c102.h b/drivers/media/usb/sn9c102/sn9c102.h
index 2bc153e..8a917f06 100644
--- a/drivers/media/usb/sn9c102/sn9c102.h
+++ b/drivers/media/usb/sn9c102/sn9c102.h
@@ -25,6 +25,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -100,6 +101,8 @@
struct sn9c102_device {
struct video_device* v4ldev;
+ struct v4l2_device v4l2_dev;
+
enum sn9c102_bridge bridge;
struct sn9c102_sensor sensor;
diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c
index c957e9a..2cb44de 100644
--- a/drivers/media/usb/sn9c102/sn9c102_core.c
+++ b/drivers/media/usb/sn9c102/sn9c102_core.c
@@ -1737,6 +1737,7 @@
video_device_node_name(cam->v4ldev));
video_set_drvdata(cam->v4ldev, NULL);
video_unregister_device(cam->v4ldev);
+ v4l2_device_unregister(&cam->v4l2_dev);
usb_put_dev(cam->usbdev);
kfree(cam->control_buffer);
kfree(cam);
@@ -3254,6 +3255,13 @@
cam->usbdev = udev;
+ /* register v4l2_device early so it can be used for printks */
+ if (v4l2_device_register(&intf->dev, &cam->v4l2_dev)) {
+ dev_err(&intf->dev, "v4l2_device_register failed\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
DBG(1, "kzalloc() failed");
err = -ENOMEM;
@@ -3325,7 +3333,7 @@
strcpy(cam->v4ldev->name, "SN9C1xx PC Camera");
cam->v4ldev->fops = &sn9c102_fops;
cam->v4ldev->release = video_device_release;
- cam->v4ldev->parent = &udev->dev;
+ cam->v4ldev->v4l2_dev = &cam->v4l2_dev;
init_completion(&cam->probe);
@@ -3377,6 +3385,7 @@
kfree(cam->control_buffer);
if (cam->v4ldev)
video_device_release(cam->v4ldev);
+ v4l2_device_unregister(&cam->v4l2_dev);
kfree(cam);
}
return err;
@@ -3407,6 +3416,8 @@
wake_up_interruptible_all(&cam->wait_open);
+ v4l2_device_disconnect(&cam->v4l2_dev);
+
kref_put(&cam->kref, sn9c102_release_resources);
up_write(&sn9c102_dev_lock);
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index a59153d2..876fc26 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -31,7 +31,6 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-chip-ident.h>
#include <media/videobuf2-vmalloc.h>
#include <media/saa7115.h>
@@ -454,19 +453,6 @@
return 0;
}
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- switch (chip->match.type) {
- case V4L2_CHIP_MATCH_BRIDGE:
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vidioc_g_register(struct file *file, void *priv,
struct v4l2_dbg_register *reg)
@@ -475,19 +461,6 @@
int rc;
u8 val;
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /* TODO: is this correct? */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- default:
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- }
-
/* Match host */
rc = stk1160_read_reg(dev, reg->reg, &val);
reg->val = val;
@@ -501,19 +474,6 @@
{
struct stk1160 *dev = video_drvdata(file);
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /* TODO: is this correct? */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- default:
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
- }
-
/* Match host */
return stk1160_write_reg(dev, reg->reg, cpu_to_le16(reg->val));
}
@@ -543,7 +503,6 @@
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c
index 307d8c5..1ccaadd 100644
--- a/drivers/media/usb/tm6000/tm6000-cards.c
+++ b/drivers/media/usb/tm6000/tm6000-cards.c
@@ -1114,7 +1114,7 @@
/* Default values for STD and resolutions */
dev->width = 720;
dev->height = 480;
- dev->norm = V4L2_STD_PAL_M;
+ dev->norm = V4L2_STD_NTSC_M;
/* Configure tuner */
tm6000_config_tuner(dev);
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index a78de1d..cc1aa14 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -1076,6 +1076,15 @@
return 0;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ *norm = dev->norm;
+ return 0;
+}
+
static const char *iname[] = {
[TM6000_INPUT_TV] = "Television",
[TM6000_INPUT_COMPOSITE1] = "Composite 1",
@@ -1134,7 +1143,7 @@
dev->input = i;
- rc = vidioc_s_std(file, priv, dev->vfd->current_norm);
+ rc = vidioc_s_std(file, priv, dev->norm);
return rc;
}
@@ -1547,6 +1556,7 @@
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
@@ -1570,7 +1580,6 @@
.ioctl_ops = &video_ioctl_ops,
.release = video_device_release,
.tvnorms = TM6000_STD,
- .current_norm = V4L2_STD_NTSC_M,
};
static const struct v4l2_file_operations radio_fops = {
diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
index 21b9049..f8a60c1 100644
--- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
@@ -1768,6 +1768,8 @@
i2c_del_adapter(&ttusb->i2c_adap);
err_unregister_adapter:
dvb_unregister_adapter (&ttusb->adapter);
+ ttusb_free_iso_urbs(ttusb);
+ kfree(ttusb);
return result;
}
diff --git a/drivers/media/usb/usbtv/Kconfig b/drivers/media/usb/usbtv/Kconfig
new file mode 100644
index 0000000..8864436
--- /dev/null
+++ b/drivers/media/usb/usbtv/Kconfig
@@ -0,0 +1,10 @@
+config VIDEO_USBTV
+ tristate "USBTV007 video capture support"
+ depends on VIDEO_DEV
+ select VIDEOBUF2_VMALLOC
+
+ ---help---
+ This is a video4linux2 driver for USBTV007 based video capture devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbtv
diff --git a/drivers/media/usb/usbtv/Makefile b/drivers/media/usb/usbtv/Makefile
new file mode 100644
index 0000000..28b872f
--- /dev/null
+++ b/drivers/media/usb/usbtv/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_USBTV) += usbtv.o
diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c
new file mode 100644
index 0000000..bf43f87
--- /dev/null
+++ b/drivers/media/usb/usbtv/usbtv.c
@@ -0,0 +1,696 @@
+/*
+ * Fushicai USBTV007 Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Following LWN articles were very useful in construction of this driver:
+ * Video4Linux2 API series: http://lwn.net/Articles/203924/
+ * videobuf2 API explanation: http://lwn.net/Articles/447435/
+ * Thanks go to Jonathan Corbet for providing this quality documentation.
+ * He is awesome.
+ *
+ * Copyright (c) 2013 Lubomir Rintel
+ * All rights reserved.
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* Hardware. */
+#define USBTV_VIDEO_ENDP 0x81
+#define USBTV_BASE 0xc000
+#define USBTV_REQUEST_REG 12
+
+/* Number of concurrent isochronous urbs submitted.
+ * Higher numbers was seen to overly saturate the USB bus. */
+#define USBTV_ISOC_TRANSFERS 16
+#define USBTV_ISOC_PACKETS 8
+
+#define USBTV_WIDTH 720
+#define USBTV_HEIGHT 480
+
+#define USBTV_CHUNK_SIZE 256
+#define USBTV_CHUNK 240
+#define USBTV_CHUNKS (USBTV_WIDTH * USBTV_HEIGHT \
+ / 2 / USBTV_CHUNK)
+
+/* Chunk header. */
+#define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \
+ == 0x88000000)
+#define USBTV_FRAME_ID(chunk) ((be32_to_cpu(chunk[0]) & 0x00ff0000) >> 16)
+#define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
+#define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff)
+
+/* A single videobuf2 frame buffer. */
+struct usbtv_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+/* Per-device structure. */
+struct usbtv {
+ struct device *dev;
+ struct usb_device *udev;
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct vb2_queue vb2q;
+ struct mutex v4l2_lock;
+ struct mutex vb2q_lock;
+
+ /* List of videobuf2 buffers protected by a lock. */
+ spinlock_t buflock;
+ struct list_head bufs;
+
+ /* Number of currently processed frame, useful find
+ * out when a new one begins. */
+ u32 frame_id;
+
+ int iso_size;
+ unsigned int sequence;
+ struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
+};
+
+static int usbtv_setup_capture(struct usbtv *usbtv)
+{
+ int ret;
+ int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
+ int i;
+ static const u16 protoregs[][2] = {
+ /* These seem to enable the device. */
+ { USBTV_BASE + 0x0008, 0x0001 },
+ { USBTV_BASE + 0x01d0, 0x00ff },
+ { USBTV_BASE + 0x01d9, 0x0002 },
+
+ /* These seem to influence color parameters, such as
+ * brightness, etc. */
+ { USBTV_BASE + 0x0239, 0x0040 },
+ { USBTV_BASE + 0x0240, 0x0000 },
+ { USBTV_BASE + 0x0241, 0x0000 },
+ { USBTV_BASE + 0x0242, 0x0002 },
+ { USBTV_BASE + 0x0243, 0x0080 },
+ { USBTV_BASE + 0x0244, 0x0012 },
+ { USBTV_BASE + 0x0245, 0x0090 },
+ { USBTV_BASE + 0x0246, 0x0000 },
+
+ { USBTV_BASE + 0x0278, 0x002d },
+ { USBTV_BASE + 0x0279, 0x000a },
+ { USBTV_BASE + 0x027a, 0x0032 },
+ { 0xf890, 0x000c },
+ { 0xf894, 0x0086 },
+
+ { USBTV_BASE + 0x00ac, 0x00c0 },
+ { USBTV_BASE + 0x00ad, 0x0000 },
+ { USBTV_BASE + 0x00a2, 0x0012 },
+ { USBTV_BASE + 0x00a3, 0x00e0 },
+ { USBTV_BASE + 0x00a4, 0x0028 },
+ { USBTV_BASE + 0x00a5, 0x0082 },
+ { USBTV_BASE + 0x00a7, 0x0080 },
+ { USBTV_BASE + 0x0000, 0x0014 },
+ { USBTV_BASE + 0x0006, 0x0003 },
+ { USBTV_BASE + 0x0090, 0x0099 },
+ { USBTV_BASE + 0x0091, 0x0090 },
+ { USBTV_BASE + 0x0094, 0x0068 },
+ { USBTV_BASE + 0x0095, 0x0070 },
+ { USBTV_BASE + 0x009c, 0x0030 },
+ { USBTV_BASE + 0x009d, 0x00c0 },
+ { USBTV_BASE + 0x009e, 0x00e0 },
+ { USBTV_BASE + 0x0019, 0x0006 },
+ { USBTV_BASE + 0x008c, 0x00ba },
+ { USBTV_BASE + 0x0101, 0x00ff },
+ { USBTV_BASE + 0x010c, 0x00b3 },
+ { USBTV_BASE + 0x01b2, 0x0080 },
+ { USBTV_BASE + 0x01b4, 0x00a0 },
+ { USBTV_BASE + 0x014c, 0x00ff },
+ { USBTV_BASE + 0x014d, 0x00ca },
+ { USBTV_BASE + 0x0113, 0x0053 },
+ { USBTV_BASE + 0x0119, 0x008a },
+ { USBTV_BASE + 0x013c, 0x0003 },
+ { USBTV_BASE + 0x0150, 0x009c },
+ { USBTV_BASE + 0x0151, 0x0071 },
+ { USBTV_BASE + 0x0152, 0x00c6 },
+ { USBTV_BASE + 0x0153, 0x0084 },
+ { USBTV_BASE + 0x0154, 0x00bc },
+ { USBTV_BASE + 0x0155, 0x00a0 },
+ { USBTV_BASE + 0x0156, 0x00a0 },
+ { USBTV_BASE + 0x0157, 0x009c },
+ { USBTV_BASE + 0x0158, 0x001f },
+ { USBTV_BASE + 0x0159, 0x0006 },
+ { USBTV_BASE + 0x015d, 0x0000 },
+
+ { USBTV_BASE + 0x0284, 0x0088 },
+ { USBTV_BASE + 0x0003, 0x0004 },
+ { USBTV_BASE + 0x001a, 0x0079 },
+ { USBTV_BASE + 0x0100, 0x00d3 },
+ { USBTV_BASE + 0x010e, 0x0068 },
+ { USBTV_BASE + 0x010f, 0x009c },
+ { USBTV_BASE + 0x0112, 0x00f0 },
+ { USBTV_BASE + 0x0115, 0x0015 },
+ { USBTV_BASE + 0x0117, 0x0000 },
+ { USBTV_BASE + 0x0118, 0x00fc },
+ { USBTV_BASE + 0x012d, 0x0004 },
+ { USBTV_BASE + 0x012f, 0x0008 },
+ { USBTV_BASE + 0x0220, 0x002e },
+ { USBTV_BASE + 0x0225, 0x0008 },
+ { USBTV_BASE + 0x024e, 0x0002 },
+ { USBTV_BASE + 0x024f, 0x0001 },
+ { USBTV_BASE + 0x0254, 0x005f },
+ { USBTV_BASE + 0x025a, 0x0012 },
+ { USBTV_BASE + 0x025b, 0x0001 },
+ { USBTV_BASE + 0x0263, 0x001c },
+ { USBTV_BASE + 0x0266, 0x0011 },
+ { USBTV_BASE + 0x0267, 0x0005 },
+ { USBTV_BASE + 0x024e, 0x0002 },
+ { USBTV_BASE + 0x024f, 0x0002 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(protoregs); i++) {
+ u16 index = protoregs[i][0];
+ u16 value = protoregs[i][1];
+
+ ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Called for each 256-byte image chunk.
+ * First word identifies the chunk, followed by 240 words of image
+ * data and padding. */
+static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk)
+{
+ int frame_id, odd, chunk_no;
+ u32 *frame;
+ struct usbtv_buf *buf;
+ unsigned long flags;
+
+ /* Ignore corrupted lines. */
+ if (!USBTV_MAGIC_OK(chunk))
+ return;
+ frame_id = USBTV_FRAME_ID(chunk);
+ odd = USBTV_ODD(chunk);
+ chunk_no = USBTV_CHUNK_NO(chunk);
+
+ /* Deinterlace. TODO: Use interlaced frame format. */
+ chunk_no = (chunk_no - chunk_no % 3) * 2 + chunk_no % 3;
+ chunk_no += !odd * 3;
+
+ if (chunk_no >= USBTV_CHUNKS)
+ return;
+
+ /* Beginning of a frame. */
+ if (chunk_no == 0)
+ usbtv->frame_id = frame_id;
+
+ spin_lock_irqsave(&usbtv->buflock, flags);
+ if (list_empty(&usbtv->bufs)) {
+ /* No free buffers. Userspace likely too slow. */
+ spin_unlock_irqrestore(&usbtv->buflock, flags);
+ return;
+ }
+
+ /* First available buffer. */
+ buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list);
+ frame = vb2_plane_vaddr(&buf->vb, 0);
+
+ /* Copy the chunk. */
+ memcpy(&frame[chunk_no * USBTV_CHUNK], &chunk[1],
+ USBTV_CHUNK * sizeof(chunk[1]));
+
+ /* Last chunk in a frame, signalling an end */
+ if (usbtv->frame_id && chunk_no == USBTV_CHUNKS-1) {
+ int size = vb2_plane_size(&buf->vb, 0);
+
+ buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
+ buf->vb.v4l2_buf.sequence = usbtv->sequence++;
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ vb2_set_plane_payload(&buf->vb, 0, size);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ list_del(&buf->list);
+ }
+
+ spin_unlock_irqrestore(&usbtv->buflock, flags);
+}
+
+/* Got image data. Each packet contains a number of 256-word chunks we
+ * compose the image from. */
+static void usbtv_iso_cb(struct urb *ip)
+{
+ int ret;
+ int i;
+ struct usbtv *usbtv = (struct usbtv *)ip->context;
+
+ switch (ip->status) {
+ /* All fine. */
+ case 0:
+ break;
+ /* Device disconnected or capture stopped? */
+ case -ENODEV:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ return;
+ /* Unknown error. Retry. */
+ default:
+ dev_warn(usbtv->dev, "Bad response for ISO request.\n");
+ goto resubmit;
+ }
+
+ for (i = 0; i < ip->number_of_packets; i++) {
+ int size = ip->iso_frame_desc[i].actual_length;
+ unsigned char *data = ip->transfer_buffer +
+ ip->iso_frame_desc[i].offset;
+ int offset;
+
+ for (offset = 0; USBTV_CHUNK_SIZE * offset < size; offset++)
+ usbtv_image_chunk(usbtv,
+ (u32 *)&data[USBTV_CHUNK_SIZE * offset]);
+ }
+
+resubmit:
+ ret = usb_submit_urb(ip, GFP_ATOMIC);
+ if (ret < 0)
+ dev_warn(usbtv->dev, "Could not resubmit ISO URB\n");
+}
+
+static struct urb *usbtv_setup_iso_transfer(struct usbtv *usbtv)
+{
+ struct urb *ip;
+ int size = usbtv->iso_size;
+ int i;
+
+ ip = usb_alloc_urb(USBTV_ISOC_PACKETS, GFP_KERNEL);
+ if (ip == NULL)
+ return NULL;
+
+ ip->dev = usbtv->udev;
+ ip->context = usbtv;
+ ip->pipe = usb_rcvisocpipe(usbtv->udev, USBTV_VIDEO_ENDP);
+ ip->interval = 1;
+ ip->transfer_flags = URB_ISO_ASAP;
+ ip->transfer_buffer = kzalloc(size * USBTV_ISOC_PACKETS,
+ GFP_KERNEL);
+ ip->complete = usbtv_iso_cb;
+ ip->number_of_packets = USBTV_ISOC_PACKETS;
+ ip->transfer_buffer_length = size * USBTV_ISOC_PACKETS;
+ for (i = 0; i < USBTV_ISOC_PACKETS; i++) {
+ ip->iso_frame_desc[i].offset = size * i;
+ ip->iso_frame_desc[i].length = size;
+ }
+
+ return ip;
+}
+
+static void usbtv_stop(struct usbtv *usbtv)
+{
+ int i;
+ unsigned long flags;
+
+ /* Cancel running transfers. */
+ for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) {
+ struct urb *ip = usbtv->isoc_urbs[i];
+ if (ip == NULL)
+ continue;
+ usb_kill_urb(ip);
+ kfree(ip->transfer_buffer);
+ usb_free_urb(ip);
+ usbtv->isoc_urbs[i] = NULL;
+ }
+
+ /* Return buffers to userspace. */
+ spin_lock_irqsave(&usbtv->buflock, flags);
+ while (!list_empty(&usbtv->bufs)) {
+ struct usbtv_buf *buf = list_first_entry(&usbtv->bufs,
+ struct usbtv_buf, list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ list_del(&buf->list);
+ }
+ spin_unlock_irqrestore(&usbtv->buflock, flags);
+}
+
+static int usbtv_start(struct usbtv *usbtv)
+{
+ int i;
+ int ret;
+
+ ret = usb_set_interface(usbtv->udev, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = usbtv_setup_capture(usbtv);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_set_interface(usbtv->udev, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < USBTV_ISOC_TRANSFERS; i++) {
+ struct urb *ip;
+
+ ip = usbtv_setup_iso_transfer(usbtv);
+ if (ip == NULL) {
+ ret = -ENOMEM;
+ goto start_fail;
+ }
+ usbtv->isoc_urbs[i] = ip;
+
+ ret = usb_submit_urb(ip, GFP_KERNEL);
+ if (ret < 0)
+ goto start_fail;
+ }
+
+ return 0;
+
+start_fail:
+ usbtv_stop(usbtv);
+ return ret;
+}
+
+struct usb_device_id usbtv_id_table[] = {
+ { USB_DEVICE(0x1b71, 0x3002) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, usbtv_id_table);
+
+static int usbtv_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct usbtv *dev = video_drvdata(file);
+
+ strlcpy(cap->driver, "usbtv", sizeof(cap->driver));
+ strlcpy(cap->card, "usbtv", sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE;
+ cap->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int usbtv_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index > 0)
+ return -EINVAL;
+
+ strlcpy(i->name, "Composite", sizeof(i->name));
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = V4L2_STD_525_60;
+ return 0;
+}
+
+static int usbtv_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index > 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "16 bpp YUY2, 4:2:2, packed",
+ sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ return 0;
+}
+
+static int usbtv_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ f->fmt.pix.width = USBTV_WIDTH;
+ f->fmt.pix.height = USBTV_HEIGHT;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ f->fmt.pix.bytesperline = USBTV_WIDTH * 2;
+ f->fmt.pix.sizeimage = (f->fmt.pix.bytesperline * f->fmt.pix.height);
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int usbtv_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ *norm = V4L2_STD_525_60;
+ return 0;
+}
+
+static int usbtv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int usbtv_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm)
+{
+ if (norm & V4L2_STD_525_60)
+ return 0;
+ return -EINVAL;
+}
+
+struct v4l2_ioctl_ops usbtv_ioctl_ops = {
+ .vidioc_querycap = usbtv_querycap,
+ .vidioc_enum_input = usbtv_enum_input,
+ .vidioc_enum_fmt_vid_cap = usbtv_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = usbtv_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = usbtv_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = usbtv_fmt_vid_cap,
+ .vidioc_g_std = usbtv_g_std,
+ .vidioc_s_std = usbtv_s_std,
+ .vidioc_g_input = usbtv_g_input,
+ .vidioc_s_input = usbtv_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+struct v4l2_file_operations usbtv_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+};
+
+static int usbtv_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *v4l_fmt, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+{
+ if (*nbuffers < 2)
+ *nbuffers = 2;
+ *nplanes = 1;
+ sizes[0] = USBTV_CHUNK * USBTV_CHUNKS * sizeof(u32);
+
+ return 0;
+}
+
+static void usbtv_buf_queue(struct vb2_buffer *vb)
+{
+ struct usbtv *usbtv = vb2_get_drv_priv(vb->vb2_queue);
+ struct usbtv_buf *buf = container_of(vb, struct usbtv_buf, vb);
+ unsigned long flags;
+
+ if (usbtv->udev == NULL) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&usbtv->buflock, flags);
+ list_add_tail(&buf->list, &usbtv->bufs);
+ spin_unlock_irqrestore(&usbtv->buflock, flags);
+}
+
+static int usbtv_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct usbtv *usbtv = vb2_get_drv_priv(vq);
+
+ if (usbtv->udev == NULL)
+ return -ENODEV;
+
+ return usbtv_start(usbtv);
+}
+
+static int usbtv_stop_streaming(struct vb2_queue *vq)
+{
+ struct usbtv *usbtv = vb2_get_drv_priv(vq);
+
+ if (usbtv->udev == NULL)
+ return -ENODEV;
+
+ usbtv_stop(usbtv);
+ return 0;
+}
+
+struct vb2_ops usbtv_vb2_ops = {
+ .queue_setup = usbtv_queue_setup,
+ .buf_queue = usbtv_buf_queue,
+ .start_streaming = usbtv_start_streaming,
+ .stop_streaming = usbtv_stop_streaming,
+};
+
+static void usbtv_release(struct v4l2_device *v4l2_dev)
+{
+ struct usbtv *usbtv = container_of(v4l2_dev, struct usbtv, v4l2_dev);
+
+ v4l2_device_unregister(&usbtv->v4l2_dev);
+ vb2_queue_release(&usbtv->vb2q);
+ kfree(usbtv);
+}
+
+static int usbtv_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ int size;
+ struct device *dev = &intf->dev;
+ struct usbtv *usbtv;
+
+ /* Checks that the device is what we think it is. */
+ if (intf->num_altsetting != 2)
+ return -ENODEV;
+ if (intf->altsetting[1].desc.bNumEndpoints != 4)
+ return -ENODEV;
+
+ /* Packet size is split into 11 bits of base size and count of
+ * extra multiplies of it.*/
+ size = usb_endpoint_maxp(&intf->altsetting[1].endpoint[0].desc);
+ size = (size & 0x07ff) * (((size & 0x1800) >> 11) + 1);
+
+ /* Device structure */
+ usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL);
+ if (usbtv == NULL)
+ return -ENOMEM;
+ usbtv->dev = dev;
+ usbtv->udev = usb_get_dev(interface_to_usbdev(intf));
+ usbtv->iso_size = size;
+ spin_lock_init(&usbtv->buflock);
+ mutex_init(&usbtv->v4l2_lock);
+ mutex_init(&usbtv->vb2q_lock);
+ INIT_LIST_HEAD(&usbtv->bufs);
+
+ /* videobuf2 structure */
+ usbtv->vb2q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ usbtv->vb2q.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ usbtv->vb2q.drv_priv = usbtv;
+ usbtv->vb2q.buf_struct_size = sizeof(struct usbtv_buf);
+ usbtv->vb2q.ops = &usbtv_vb2_ops;
+ usbtv->vb2q.mem_ops = &vb2_vmalloc_memops;
+ usbtv->vb2q.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ usbtv->vb2q.lock = &usbtv->vb2q_lock;
+ ret = vb2_queue_init(&usbtv->vb2q);
+ if (ret < 0) {
+ dev_warn(dev, "Could not initialize videobuf2 queue\n");
+ goto usbtv_fail;
+ }
+
+ /* v4l2 structure */
+ usbtv->v4l2_dev.release = usbtv_release;
+ ret = v4l2_device_register(dev, &usbtv->v4l2_dev);
+ if (ret < 0) {
+ dev_warn(dev, "Could not register v4l2 device\n");
+ goto v4l2_fail;
+ }
+
+ usb_set_intfdata(intf, usbtv);
+
+ /* Video structure */
+ strlcpy(usbtv->vdev.name, "usbtv", sizeof(usbtv->vdev.name));
+ usbtv->vdev.v4l2_dev = &usbtv->v4l2_dev;
+ usbtv->vdev.release = video_device_release_empty;
+ usbtv->vdev.fops = &usbtv_fops;
+ usbtv->vdev.ioctl_ops = &usbtv_ioctl_ops;
+ usbtv->vdev.tvnorms = V4L2_STD_525_60;
+ usbtv->vdev.queue = &usbtv->vb2q;
+ usbtv->vdev.lock = &usbtv->v4l2_lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &usbtv->vdev.flags);
+ video_set_drvdata(&usbtv->vdev, usbtv);
+ ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_warn(dev, "Could not register video device\n");
+ goto vdev_fail;
+ }
+
+ dev_info(dev, "Fushicai USBTV007 Video Grabber\n");
+ return 0;
+
+vdev_fail:
+ v4l2_device_unregister(&usbtv->v4l2_dev);
+v4l2_fail:
+ vb2_queue_release(&usbtv->vb2q);
+usbtv_fail:
+ kfree(usbtv);
+
+ return ret;
+}
+
+static void usbtv_disconnect(struct usb_interface *intf)
+{
+ struct usbtv *usbtv = usb_get_intfdata(intf);
+
+ mutex_lock(&usbtv->vb2q_lock);
+ mutex_lock(&usbtv->v4l2_lock);
+
+ usbtv_stop(usbtv);
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(&usbtv->vdev);
+ v4l2_device_disconnect(&usbtv->v4l2_dev);
+ usb_put_dev(usbtv->udev);
+ usbtv->udev = NULL;
+
+ mutex_unlock(&usbtv->v4l2_lock);
+ mutex_unlock(&usbtv->vb2q_lock);
+
+ v4l2_device_put(&usbtv->v4l2_dev);
+}
+
+MODULE_AUTHOR("Lubomir Rintel");
+MODULE_DESCRIPTION("Fushicai USBTV007 Video Grabber Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct usb_driver usbtv_usb_driver = {
+ .name = "usbtv",
+ .id_table = usbtv_id_table,
+ .probe = usbtv_probe,
+ .disconnect = usbtv_disconnect,
+};
+
+module_usb_driver(usbtv_usb_driver);
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index d34c2af..5c9e312 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -467,8 +467,6 @@
struct usb_usbvision *usbvision = video_drvdata(file);
int err_code;
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
/* NT100x has a 8-bit register space */
err_code = usbvision_read_reg(usbvision, reg->reg&0xff);
if (err_code < 0) {
@@ -488,8 +486,6 @@
struct usb_usbvision *usbvision = video_drvdata(file);
int err_code;
- if (!v4l2_chip_match_host(®->match))
- return -EINVAL;
/* NT100x has a 8-bit register space */
err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val);
if (err_code < 0) {
@@ -608,6 +604,14 @@
return 0;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ *id = usbvision->tvnorm_id;
+ return 0;
+}
+
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *vt)
{
@@ -1248,6 +1252,7 @@
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
@@ -1274,7 +1279,6 @@
.name = "usbvision-video",
.release = video_device_release,
.tvnorms = USBVISION_NORMS,
- .current_norm = V4L2_STD_PAL
};
@@ -1307,9 +1311,6 @@
.name = "usbvision-radio",
.release = video_device_release,
.ioctl_ops = &usbvision_radio_ioctl_ops,
-
- .tvnorms = USBVISION_NORMS,
- .current_norm = V4L2_STD_PAL
};
@@ -1459,6 +1460,7 @@
usbvision_remove_sysfs(usbvision->vdev);
usbvision_unregister_video(usbvision);
+ kfree(usbvision->alt_max_pkt_size);
usb_free_urb(usbvision->ctrl_urb);
@@ -1574,6 +1576,7 @@
usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL);
if (usbvision->alt_max_pkt_size == NULL) {
dev_err(&intf->dev, "usbvision: out of memory!\n");
+ usbvision_release(usbvision);
return -ENOMEM;
}
diff --git a/drivers/media/usb/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig
index 541c9f1..6ed85efa 100644
--- a/drivers/media/usb/uvc/Kconfig
+++ b/drivers/media/usb/uvc/Kconfig
@@ -1,5 +1,6 @@
config USB_VIDEO_CLASS
tristate "USB Video Class (UVC)"
+ depends on VIDEO_V4L2
select VIDEOBUF2_VMALLOC
---help---
Support for the USB Video Class (UVC). Currently only video
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 5dbefa6..81695d4 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1836,8 +1836,8 @@
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
atomic_set(&dev->nstreams, 0);
- atomic_set(&dev->users, 0);
atomic_set(&dev->nmappings, 0);
+ mutex_init(&dev->lock);
dev->udev = usb_get_dev(udev);
dev->intf = usb_get_intf(intf);
@@ -1950,8 +1950,13 @@
/* Controls are cached on the fly so they don't need to be saved. */
if (intf->cur_altsetting->desc.bInterfaceSubClass ==
- UVC_SC_VIDEOCONTROL)
- return uvc_status_suspend(dev);
+ UVC_SC_VIDEOCONTROL) {
+ mutex_lock(&dev->lock);
+ if (dev->users)
+ uvc_status_stop(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
list_for_each_entry(stream, &dev->streams, list) {
if (stream->intf == intf)
@@ -1973,14 +1978,20 @@
if (intf->cur_altsetting->desc.bInterfaceSubClass ==
UVC_SC_VIDEOCONTROL) {
- if (reset) {
- int ret = uvc_ctrl_resume_device(dev);
+ int ret = 0;
+ if (reset) {
+ ret = uvc_ctrl_resume_device(dev);
if (ret < 0)
return ret;
}
- return uvc_status_resume(dev);
+ mutex_lock(&dev->lock);
+ if (dev->users)
+ ret = uvc_status_start(dev, GFP_NOIO);
+ mutex_unlock(&dev->lock);
+
+ return ret;
}
list_for_each_entry(stream, &dev->streams, list) {
@@ -2163,6 +2174,24 @@
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_DEF },
+ /* Dell Alienware X51 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05a9,
+ .idProduct = 0x2643,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
+ /* Dell Studio Hybrid 140g (OmniVision webcam) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05a9,
+ .idProduct = 0x264a,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
/* Apple Built-In iSight */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index b749277..f552ab9 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -206,32 +206,15 @@
uvc_input_cleanup(dev);
}
-int uvc_status_start(struct uvc_device *dev)
+int uvc_status_start(struct uvc_device *dev, gfp_t flags)
{
if (dev->int_urb == NULL)
return 0;
- return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+ return usb_submit_urb(dev->int_urb, flags);
}
void uvc_status_stop(struct uvc_device *dev)
{
usb_kill_urb(dev->int_urb);
}
-
-int uvc_status_suspend(struct uvc_device *dev)
-{
- if (atomic_read(&dev->users))
- usb_kill_urb(dev->int_urb);
-
- return 0;
-}
-
-int uvc_status_resume(struct uvc_device *dev)
-{
- if (dev->int_urb == NULL || atomic_read(&dev->users) == 0)
- return 0;
-
- return usb_submit_urb(dev->int_urb, GFP_NOIO);
-}
-
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index b2dc326..3afff92 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -498,16 +498,20 @@
return -ENOMEM;
}
- if (atomic_inc_return(&stream->dev->users) == 1) {
- ret = uvc_status_start(stream->dev);
+ mutex_lock(&stream->dev->lock);
+ if (stream->dev->users == 0) {
+ ret = uvc_status_start(stream->dev, GFP_KERNEL);
if (ret < 0) {
- atomic_dec(&stream->dev->users);
+ mutex_unlock(&stream->dev->lock);
usb_autopm_put_interface(stream->dev->intf);
kfree(handle);
return ret;
}
}
+ stream->dev->users++;
+ mutex_unlock(&stream->dev->lock);
+
v4l2_fh_init(&handle->vfh, stream->vdev);
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
@@ -538,8 +542,10 @@
kfree(handle);
file->private_data = NULL;
- if (atomic_dec_return(&stream->dev->users) == 0)
+ mutex_lock(&stream->dev->lock);
+ if (--stream->dev->users == 0)
uvc_status_stop(stream->dev);
+ mutex_unlock(&stream->dev->lock);
usb_autopm_put_interface(stream->dev->intf);
return 0;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index af505fd..9e35982 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -514,7 +514,8 @@
char name[32];
enum uvc_device_state state;
- atomic_t users;
+ struct mutex lock; /* Protects users */
+ unsigned int users;
atomic_t nmappings;
/* Video control interface */
@@ -660,10 +661,8 @@
/* Status */
extern int uvc_status_init(struct uvc_device *dev);
extern void uvc_status_cleanup(struct uvc_device *dev);
-extern int uvc_status_start(struct uvc_device *dev);
+extern int uvc_status_start(struct uvc_device *dev, gfp_t flags);
extern void uvc_status_stop(struct uvc_device *dev);
-extern int uvc_status_suspend(struct uvc_device *dev);
-extern int uvc_status_resume(struct uvc_device *dev);
/* Controls */
extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index aa50c46..4c33b8d 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -5,7 +5,8 @@
tuner-objs := tuner-core.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
- v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o v4l2-clk.o \
+ v4l2-async.o
ifeq ($(CONFIG_COMPAT),y)
videodev-objs += v4l2-compat-ioctl32.o
endif
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
new file mode 100644
index 0000000..aae2417
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -0,0 +1,284 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
+{
+#if IS_ENABLED(CONFIG_I2C)
+ struct i2c_client *client = i2c_verify_client(dev);
+ return client &&
+ asd->bus_type == V4L2_ASYNC_BUS_I2C &&
+ asd->match.i2c.adapter_id == client->adapter->nr &&
+ asd->match.i2c.address == client->addr;
+#else
+ return false;
+#endif
+}
+
+static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd)
+{
+ return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM &&
+ !strcmp(asd->match.platform.name, dev_name(dev));
+}
+
+static LIST_HEAD(subdev_list);
+static LIST_HEAD(notifier_list);
+static DEFINE_MUTEX(list_lock);
+
+static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_subdev_list *asdl)
+{
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+ struct v4l2_async_subdev *asd;
+ bool (*match)(struct device *,
+ struct v4l2_async_subdev *);
+
+ list_for_each_entry(asd, ¬ifier->waiting, list) {
+ /* bus_type has been verified valid before */
+ switch (asd->bus_type) {
+ case V4L2_ASYNC_BUS_CUSTOM:
+ match = asd->match.custom.match;
+ if (!match)
+ /* Match always */
+ return asd;
+ break;
+ case V4L2_ASYNC_BUS_PLATFORM:
+ match = match_platform;
+ break;
+ case V4L2_ASYNC_BUS_I2C:
+ match = match_i2c;
+ break;
+ default:
+ /* Cannot happen, unless someone breaks us */
+ WARN_ON(true);
+ return NULL;
+ }
+
+ /* match cannot be NULL here */
+ if (match(sd->dev, asd))
+ return asd;
+ }
+
+ return NULL;
+}
+
+static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
+ struct v4l2_async_subdev_list *asdl,
+ struct v4l2_async_subdev *asd)
+{
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+ int ret;
+
+ /* Remove from the waiting list */
+ list_del(&asd->list);
+ asdl->asd = asd;
+ asdl->notifier = notifier;
+
+ if (notifier->bound) {
+ ret = notifier->bound(notifier, sd, asd);
+ if (ret < 0)
+ return ret;
+ }
+ /* Move from the global subdevice list to notifier's done */
+ list_move(&asdl->list, ¬ifier->done);
+
+ ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
+ if (ret < 0) {
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, asd);
+ return ret;
+ }
+
+ if (list_empty(¬ifier->waiting) && notifier->complete)
+ return notifier->complete(notifier);
+
+ return 0;
+}
+
+static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl)
+{
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+ v4l2_device_unregister_subdev(sd);
+ /* Subdevice driver will reprobe and put asdl back onto the list */
+ list_del_init(&asdl->list);
+ asdl->asd = NULL;
+ sd->dev = NULL;
+}
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+ struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_async_subdev_list *asdl, *tmp;
+ struct v4l2_async_subdev *asd;
+ int i;
+
+ if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
+ return -EINVAL;
+
+ notifier->v4l2_dev = v4l2_dev;
+ INIT_LIST_HEAD(¬ifier->waiting);
+ INIT_LIST_HEAD(¬ifier->done);
+
+ for (i = 0; i < notifier->num_subdevs; i++) {
+ asd = notifier->subdev[i];
+
+ switch (asd->bus_type) {
+ case V4L2_ASYNC_BUS_CUSTOM:
+ case V4L2_ASYNC_BUS_PLATFORM:
+ case V4L2_ASYNC_BUS_I2C:
+ break;
+ default:
+ dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
+ "Invalid bus-type %u on %p\n",
+ asd->bus_type, asd);
+ return -EINVAL;
+ }
+ list_add_tail(&asd->list, ¬ifier->waiting);
+ }
+
+ mutex_lock(&list_lock);
+
+ /* Keep also completed notifiers on the list */
+ list_add(¬ifier->list, ¬ifier_list);
+
+ list_for_each_entry_safe(asdl, tmp, &subdev_list, list) {
+ int ret;
+
+ asd = v4l2_async_belongs(notifier, asdl);
+ if (!asd)
+ continue;
+
+ ret = v4l2_async_test_notify(notifier, asdl, asd);
+ if (ret < 0) {
+ mutex_unlock(&list_lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_async_notifier_register);
+
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_async_subdev_list *asdl, *tmp;
+ unsigned int notif_n_subdev = notifier->num_subdevs;
+ unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
+ struct device *dev[n_subdev];
+ int i = 0;
+
+ mutex_lock(&list_lock);
+
+ list_del(¬ifier->list);
+
+ list_for_each_entry_safe(asdl, tmp, ¬ifier->done, list) {
+ struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl);
+
+ dev[i] = get_device(sd->dev);
+
+ v4l2_async_cleanup(asdl);
+
+ /* If we handled USB devices, we'd have to lock the parent too */
+ device_release_driver(dev[i++]);
+
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asdl.asd);
+ }
+
+ mutex_unlock(&list_lock);
+
+ while (i--) {
+ struct device *d = dev[i];
+
+ if (d && device_attach(d) < 0) {
+ const char *name = "(none)";
+ int lock = device_trylock(d);
+
+ if (lock && d->driver)
+ name = d->driver->name;
+ dev_err(d, "Failed to re-probe to %s\n", name);
+ if (lock)
+ device_unlock(d);
+ }
+ put_device(d);
+ }
+ /*
+ * Don't care about the waiting list, it is initialised and populated
+ * upon notifier registration.
+ */
+}
+EXPORT_SYMBOL(v4l2_async_notifier_unregister);
+
+int v4l2_async_register_subdev(struct v4l2_subdev *sd)
+{
+ struct v4l2_async_subdev_list *asdl = &sd->asdl;
+ struct v4l2_async_notifier *notifier;
+
+ mutex_lock(&list_lock);
+
+ INIT_LIST_HEAD(&asdl->list);
+
+ list_for_each_entry(notifier, ¬ifier_list, list) {
+ struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl);
+ if (asd) {
+ int ret = v4l2_async_test_notify(notifier, asdl, asd);
+ mutex_unlock(&list_lock);
+ return ret;
+ }
+ }
+
+ /* None matched, wait for hot-plugging */
+ list_add(&asdl->list, &subdev_list);
+
+ mutex_unlock(&list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_async_register_subdev);
+
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
+{
+ struct v4l2_async_subdev_list *asdl = &sd->asdl;
+ struct v4l2_async_notifier *notifier = asdl->notifier;
+
+ if (!asdl->asd) {
+ if (!list_empty(&asdl->list))
+ v4l2_async_cleanup(asdl);
+ return;
+ }
+
+ mutex_lock(&list_lock);
+
+ list_add(&asdl->asd->list, ¬ifier->waiting);
+
+ v4l2_async_cleanup(asdl);
+
+ if (notifier->unbind)
+ notifier->unbind(notifier, sd, sd->asdl.asd);
+
+ mutex_unlock(&list_lock);
+}
+EXPORT_SYMBOL(v4l2_async_unregister_subdev);
diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c
new file mode 100644
index 0000000..b67de86
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-clk.c
@@ -0,0 +1,242 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/atomic.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <media/v4l2-clk.h>
+#include <media/v4l2-subdev.h>
+
+static DEFINE_MUTEX(clk_lock);
+static LIST_HEAD(clk_list);
+
+static struct v4l2_clk *v4l2_clk_find(const char *dev_id, const char *id)
+{
+ struct v4l2_clk *clk;
+
+ list_for_each_entry(clk, &clk_list, list) {
+ if (strcmp(dev_id, clk->dev_id))
+ continue;
+
+ if (!id || !clk->id || !strcmp(clk->id, id))
+ return clk;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
+{
+ struct v4l2_clk *clk;
+
+ mutex_lock(&clk_lock);
+ clk = v4l2_clk_find(dev_name(dev), id);
+
+ if (!IS_ERR(clk))
+ atomic_inc(&clk->use_count);
+ mutex_unlock(&clk_lock);
+
+ return clk;
+}
+EXPORT_SYMBOL(v4l2_clk_get);
+
+void v4l2_clk_put(struct v4l2_clk *clk)
+{
+ struct v4l2_clk *tmp;
+
+ if (IS_ERR(clk))
+ return;
+
+ mutex_lock(&clk_lock);
+
+ list_for_each_entry(tmp, &clk_list, list)
+ if (tmp == clk)
+ atomic_dec(&clk->use_count);
+
+ mutex_unlock(&clk_lock);
+}
+EXPORT_SYMBOL(v4l2_clk_put);
+
+static int v4l2_clk_lock_driver(struct v4l2_clk *clk)
+{
+ struct v4l2_clk *tmp;
+ int ret = -ENODEV;
+
+ mutex_lock(&clk_lock);
+
+ list_for_each_entry(tmp, &clk_list, list)
+ if (tmp == clk) {
+ ret = !try_module_get(clk->ops->owner);
+ if (ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(&clk_lock);
+
+ return ret;
+}
+
+static void v4l2_clk_unlock_driver(struct v4l2_clk *clk)
+{
+ module_put(clk->ops->owner);
+}
+
+int v4l2_clk_enable(struct v4l2_clk *clk)
+{
+ int ret = v4l2_clk_lock_driver(clk);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&clk->lock);
+
+ if (++clk->enable == 1 && clk->ops->enable) {
+ ret = clk->ops->enable(clk);
+ if (ret < 0)
+ clk->enable--;
+ }
+
+ mutex_unlock(&clk->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_enable);
+
+/*
+ * You might Oops if you try to disabled a disabled clock, because then the
+ * driver isn't locked and could have been unloaded by now, so, don't do that
+ */
+void v4l2_clk_disable(struct v4l2_clk *clk)
+{
+ int enable;
+
+ mutex_lock(&clk->lock);
+
+ enable = --clk->enable;
+ if (WARN(enable < 0, "Unbalanced %s() on %s:%s!\n", __func__,
+ clk->dev_id, clk->id))
+ clk->enable++;
+ else if (!enable && clk->ops->disable)
+ clk->ops->disable(clk);
+
+ mutex_unlock(&clk->lock);
+
+ v4l2_clk_unlock_driver(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_disable);
+
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk)
+{
+ int ret = v4l2_clk_lock_driver(clk);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&clk->lock);
+ if (!clk->ops->get_rate)
+ ret = -ENOSYS;
+ else
+ ret = clk->ops->get_rate(clk);
+ mutex_unlock(&clk->lock);
+
+ v4l2_clk_unlock_driver(clk);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_get_rate);
+
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate)
+{
+ int ret = v4l2_clk_lock_driver(clk);
+
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&clk->lock);
+ if (!clk->ops->set_rate)
+ ret = -ENOSYS;
+ else
+ ret = clk->ops->set_rate(clk, rate);
+ mutex_unlock(&clk->lock);
+
+ v4l2_clk_unlock_driver(clk);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_clk_set_rate);
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+ const char *dev_id,
+ const char *id, void *priv)
+{
+ struct v4l2_clk *clk;
+ int ret;
+
+ if (!ops || !dev_id)
+ return ERR_PTR(-EINVAL);
+
+ clk = kzalloc(sizeof(struct v4l2_clk), GFP_KERNEL);
+ if (!clk)
+ return ERR_PTR(-ENOMEM);
+
+ clk->id = kstrdup(id, GFP_KERNEL);
+ clk->dev_id = kstrdup(dev_id, GFP_KERNEL);
+ if ((id && !clk->id) || !clk->dev_id) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+ clk->ops = ops;
+ clk->priv = priv;
+ atomic_set(&clk->use_count, 0);
+ mutex_init(&clk->lock);
+
+ mutex_lock(&clk_lock);
+ if (!IS_ERR(v4l2_clk_find(dev_id, id))) {
+ mutex_unlock(&clk_lock);
+ ret = -EEXIST;
+ goto eexist;
+ }
+ list_add_tail(&clk->list, &clk_list);
+ mutex_unlock(&clk_lock);
+
+ return clk;
+
+eexist:
+ealloc:
+ kfree(clk->id);
+ kfree(clk->dev_id);
+ kfree(clk);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(v4l2_clk_register);
+
+void v4l2_clk_unregister(struct v4l2_clk *clk)
+{
+ if (WARN(atomic_read(&clk->use_count),
+ "%s(): Refusing to unregister ref-counted %s:%s clock!\n",
+ __func__, clk->dev_id, clk->id))
+ return;
+
+ mutex_lock(&clk_lock);
+ list_del(&clk->list);
+ mutex_unlock(&clk_lock);
+
+ kfree(clk->id);
+ kfree(clk->dev_id);
+ kfree(clk);
+}
+EXPORT_SYMBOL(v4l2_clk_unregister);
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 3fed63f..a95e5e2 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -61,7 +61,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -227,62 +226,9 @@
}
EXPORT_SYMBOL(v4l2_ctrl_next);
-int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
-{
- switch (match->type) {
- case V4L2_CHIP_MATCH_BRIDGE:
- return match->addr == 0;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL(v4l2_chip_match_host);
-
-#if IS_ENABLED(CONFIG_I2C)
-int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match)
-{
- int len;
-
- if (c == NULL || match == NULL)
- return 0;
-
- switch (match->type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- if (c->driver == NULL || c->driver->driver.name == NULL)
- return 0;
- len = strlen(c->driver->driver.name);
- return len && !strncmp(c->driver->driver.name, match->name, len);
- case V4L2_CHIP_MATCH_I2C_ADDR:
- return c->addr == match->addr;
- case V4L2_CHIP_MATCH_SUBDEV:
- return 1;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
-
-int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
- u32 ident, u32 revision)
-{
- if (!v4l2_chip_match_i2c_client(c, &chip->match))
- return 0;
- if (chip->ident == V4L2_IDENT_NONE) {
- chip->ident = ident;
- chip->revision = revision;
- }
- else {
- chip->ident = V4L2_IDENT_AMBIGUOUS;
- chip->revision = 0;
- }
- return 0;
-}
-EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
-
-/* ----------------------------------------------------------------- */
-
/* I2C Helper functions */
+#if IS_ENABLED(CONFIG_I2C)
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
@@ -291,6 +237,7 @@
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->driver->driver.owner;
+ sd->dev = &client->dev;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);
i2c_set_clientdata(client, sd);
@@ -301,8 +248,6 @@
}
EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
-
-
/* Load an i2c sub-device. */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
@@ -426,6 +371,7 @@
sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
/* the owner is the same as the spi_device's driver owner */
sd->owner = spi->dev.driver->owner;
+ sd->dev = &spi->dev;
/* spi_device and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, spi);
spi_set_drvdata(spi, sd);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index f129551..8f7a6a4 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -1074,7 +1074,6 @@
case VIDIOC_TRY_DECODER_CMD:
case VIDIOC_DBG_S_REGISTER:
case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_G_CHIP_IDENT:
case VIDIOC_S_HW_FREQ_SEEK:
case VIDIOC_S_DV_TIMINGS:
case VIDIOC_G_DV_TIMINGS:
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 5923c5d..c8859d6 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -495,8 +495,8 @@
};
/**
- * get_index - assign stream index number based on parent device
- * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ * get_index - assign stream index number based on v4l2_dev
+ * @vdev: video_device to assign index number to, vdev->v4l2_dev should be assigned
*
* Note that when this is called the new device has not yet been registered
* in the video_device array, but it was able to obtain a minor number.
@@ -514,15 +514,11 @@
static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
int i;
- /* Some drivers do not set the parent. In that case always return 0. */
- if (vdev->parent == NULL)
- return 0;
-
bitmap_zero(used, VIDEO_NUM_DEVICES);
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
if (video_device[i] != NULL &&
- video_device[i]->parent == vdev->parent) {
+ video_device[i]->v4l2_dev == vdev->v4l2_dev) {
set_bit(video_device[i]->index, used);
}
}
@@ -596,7 +592,6 @@
set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
#endif
- SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
/* yes, really vidioc_subscribe_event */
SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
@@ -675,9 +670,8 @@
SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
if (ops->vidioc_s_std)
set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
- if (ops->vidioc_g_std || vdev->current_norm)
- set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
+ SET_VALID_IOCTL(ops, VIDIOC_G_STD, vidioc_g_std);
if (is_rx) {
SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
@@ -705,7 +699,7 @@
if (ops->vidioc_cropcap || ops->vidioc_g_selection)
set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
- (ops->vidioc_g_std || vdev->current_norm)))
+ ops->vidioc_g_std))
set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
@@ -777,6 +771,9 @@
/* the release callback MUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;
+ /* the v4l2_dev pointer MUST be present */
+ if (WARN_ON(!vdev->v4l2_dev))
+ return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
@@ -804,16 +801,14 @@
vdev->vfl_type = type;
vdev->cdev = NULL;
- if (vdev->v4l2_dev) {
- if (vdev->v4l2_dev->dev)
- vdev->parent = vdev->v4l2_dev->dev;
- if (vdev->ctrl_handler == NULL)
- vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
- /* If the prio state pointer is NULL, then use the v4l2_device
- prio state. */
- if (vdev->prio == NULL)
- vdev->prio = &vdev->v4l2_dev->prio;
- }
+ if (vdev->dev_parent == NULL)
+ vdev->dev_parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ /* If the prio state pointer is NULL, then use the v4l2_device
+ prio state. */
+ if (vdev->prio == NULL)
+ vdev->prio = &vdev->v4l2_dev->prio;
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
@@ -898,8 +893,7 @@
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
- if (vdev->parent)
- vdev->dev.parent = vdev->parent;
+ vdev->dev.parent = vdev->dev_parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 8ed5da2..02d1b63 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -44,7 +44,8 @@
v4l2_dev->dev = dev;
if (dev == NULL) {
/* If dev == NULL, then name must be filled in by the caller */
- WARN_ON(!v4l2_dev->name[0]);
+ if (WARN_ON(!v4l2_dev->name[0]))
+ return -EINVAL;
return 0;
}
@@ -105,7 +106,9 @@
{
struct v4l2_subdev *sd, *next;
- if (v4l2_dev == NULL)
+ /* Just return if v4l2_dev is NULL or if it was already
+ * unregistered before. */
+ if (v4l2_dev == NULL || !v4l2_dev->name[0])
return;
v4l2_device_disconnect(v4l2_dev);
@@ -135,6 +138,8 @@
}
#endif
}
+ /* Mark as unregistered, thus preventing duplicate unregistrations */
+ v4l2_dev->name[0] = '\0';
}
EXPORT_SYMBOL_GPL(v4l2_device_unregister);
@@ -269,8 +274,10 @@
sd->v4l2_dev = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev->mdev)
+ if (v4l2_dev->mdev) {
+ media_entity_remove_links(&sd->entity);
media_device_unregister_entity(&sd->entity);
+ }
#endif
video_unregister_device(sd->devnode);
module_put(sd->owner);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 7658586..68e6b5e 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -26,7 +26,6 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/videobuf2-core.h>
/* Zero out the end of the struct pointed to by p. Everything after, but
@@ -619,20 +618,6 @@
pr_info("pts=%llu\n", p->stop.pts);
}
-static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
-{
- const struct v4l2_dbg_chip_ident *p = arg;
-
- pr_cont("type=%u, ", p->match.type);
- if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
- pr_cont("name=%.*s, ",
- (int)sizeof(p->match.name), p->match.name);
- else
- pr_cont("addr=%u, ", p->match.addr);
- pr_cont("chip_ident=%u, revision=0x%x\n",
- p->ident, p->revision);
-}
-
static void v4l_print_dbg_chip_info(const void *arg, bool write_only)
{
const struct v4l2_dbg_chip_info *p = arg;
@@ -1359,40 +1344,18 @@
return 0;
}
-static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- v4l2_std_id *id = arg;
-
- /* Calls the specific handler */
- if (ops->vidioc_g_std)
- return ops->vidioc_g_std(file, fh, arg);
- if (vfd->current_norm) {
- *id = vfd->current_norm;
- return 0;
- }
- return -ENOTTY;
-}
-
static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct video_device *vfd = video_devdata(file);
v4l2_std_id id = *(v4l2_std_id *)arg, norm;
- int ret;
norm = id & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
return -EINVAL;
/* Calls the specific handler */
- ret = ops->vidioc_s_std(file, fh, norm);
-
- /* Updates standard information */
- if (ret >= 0)
- vfd->current_norm = norm;
- return ret;
+ return ops->vidioc_s_std(file, fh, norm);
}
static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
@@ -1402,10 +1365,10 @@
v4l2_std_id *p = arg;
/*
- * If nothing detected, it should return all supported
- * standard.
- * Drivers just need to mask the std argument, in order
- * to remove the standards that don't apply from the mask.
+ * If no signal is detected, then the driver should return
+ * V4L2_STD_UNKNOWN. Otherwise it should return tvnorms with
+ * any standards that do not apply removed.
+ *
* This means that tuners, audio and video decoders can join
* their efforts to improve the standards detection.
*/
@@ -1495,7 +1458,6 @@
static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
- struct video_device *vfd = video_devdata(file);
struct v4l2_streamparm *p = arg;
v4l2_std_id std;
int ret = check_fmt(file, p->type);
@@ -1504,16 +1466,13 @@
return ret;
if (ops->vidioc_g_parm)
return ops->vidioc_g_parm(file, fh, p);
- std = vfd->current_norm;
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
p->parm.capture.readbuffers = 2;
- if (is_valid_ioctl(vfd, VIDIOC_G_STD) && ops->vidioc_g_std)
- ret = ops->vidioc_g_std(file, fh, &std);
+ ret = ops->vidioc_g_std(file, fh, &std);
if (ret == 0)
- v4l2_video_std_frame_period(std,
- &p->parm.capture.timeperframe);
+ v4l2_video_std_frame_period(std, &p->parm.capture.timeperframe);
return ret;
}
@@ -1802,7 +1761,8 @@
return v4l2_subdev_call(sd, core, g_register, p);
return -EINVAL;
}
- if (ops->vidioc_g_register)
+ if (ops->vidioc_g_register && p->match.type == V4L2_CHIP_MATCH_BRIDGE &&
+ (ops->vidioc_g_chip_info || p->match.addr == 0))
return ops->vidioc_g_register(file, fh, p);
return -EINVAL;
#else
@@ -1829,7 +1789,8 @@
return v4l2_subdev_call(sd, core, s_register, p);
return -EINVAL;
}
- if (ops->vidioc_s_register)
+ if (ops->vidioc_s_register && p->match.type == V4L2_CHIP_MATCH_BRIDGE &&
+ (ops->vidioc_g_chip_info || p->match.addr == 0))
return ops->vidioc_s_register(file, fh, p);
return -EINVAL;
#else
@@ -1837,18 +1798,6 @@
#endif
}
-static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_dbg_chip_ident *p = arg;
-
- p->ident = V4L2_IDENT_NONE;
- p->revision = 0;
- if (p->match.type == V4L2_CHIP_MATCH_SUBDEV)
- return -EINVAL;
- return ops->vidioc_g_chip_ident(file, fh, p);
-}
-
static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
@@ -1864,12 +1813,7 @@
p->flags |= V4L2_CHIP_FL_WRITABLE;
if (ops->vidioc_g_register)
p->flags |= V4L2_CHIP_FL_READABLE;
- if (vfd->v4l2_dev)
- strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name));
- else if (vfd->parent)
- strlcpy(p->name, vfd->parent->driver->name, sizeof(p->name));
- else
- strlcpy(p->name, "bridge", sizeof(p->name));
+ strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name));
if (ops->vidioc_g_chip_info)
return ops->vidioc_g_chip_info(file, fh, arg);
if (p->match.addr)
@@ -2048,7 +1992,7 @@
IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
+ IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0),
IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
@@ -2098,7 +2042,6 @@
IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
index 67f572c..65411adc 100644
--- a/drivers/media/v4l2-core/videobuf-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf-dma-contig.c
@@ -66,11 +66,14 @@
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
- dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+ dev_dbg(q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
map, map->count, vma->vm_start, vma->vm_end);
+ videobuf_queue_lock(q);
map->count++;
+ videobuf_queue_unlock(q);
}
static void videobuf_vm_close(struct vm_area_struct *vma)
@@ -82,12 +85,11 @@
dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
map, map->count, vma->vm_start, vma->vm_end);
- map->count--;
- if (0 == map->count) {
+ videobuf_queue_lock(q);
+ if (!--map->count) {
struct videobuf_dma_contig_memory *mem;
dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
/* We need first to cancel streams, before unmapping */
if (q->streaming)
@@ -126,8 +128,8 @@
kfree(map);
- videobuf_queue_unlock(q);
}
+ videobuf_queue_unlock(q);
}
static const struct vm_operations_struct videobuf_vm_ops = {
@@ -303,14 +305,9 @@
goto error;
/* Try to remap memory */
-
size = vma->vm_end - vma->vm_start;
- size = (size < mem->size) ? size : mem->size;
-
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- retval = remap_pfn_range(vma, vma->vm_start,
- mem->dma_handle >> PAGE_SHIFT,
- size, vma->vm_page_prot);
+ retval = vm_iomap_memory(vma, vma->vm_start, size);
if (retval) {
dev_err(q->dev, "mmap: remap failed with error %d. ",
retval);
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 828e7c1..9db674c 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -338,11 +338,14 @@
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
+ videobuf_queue_lock(q);
map->count++;
+ videobuf_queue_unlock(q);
}
static void videobuf_vm_close(struct vm_area_struct *vma)
@@ -355,10 +358,9 @@
dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
- map->count--;
- if (0 == map->count) {
+ videobuf_queue_lock(q);
+ if (!--map->count) {
dprintk(1, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
@@ -374,9 +376,9 @@
q->bufs[i]->baddr = 0;
q->ops->buf_release(q, q->bufs[i]);
}
- videobuf_queue_unlock(q);
kfree(map);
}
+ videobuf_queue_unlock(q);
return;
}
diff --git a/drivers/media/v4l2-core/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
index 2ff7fcc..1365c65 100644
--- a/drivers/media/v4l2-core/videobuf-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf-vmalloc.c
@@ -54,11 +54,14 @@
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
+ videobuf_queue_lock(q);
map->count++;
+ videobuf_queue_unlock(q);
}
static void videobuf_vm_close(struct vm_area_struct *vma)
@@ -70,12 +73,11 @@
dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
- map->count--;
- if (0 == map->count) {
+ videobuf_queue_lock(q);
+ if (!--map->count) {
struct videobuf_vmalloc_memory *mem;
dprintk(1, "munmap %p q=%p\n", map, q);
- videobuf_queue_lock(q);
/* We need first to cancel streams, before unmapping */
if (q->streaming)
@@ -114,8 +116,8 @@
kfree(map);
- videobuf_queue_unlock(q);
}
+ videobuf_queue_unlock(q);
return;
}
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index e3bdc3b..9fc4bab 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -2194,8 +2194,10 @@
*/
for (i = 0; i < q->num_buffers; i++) {
fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
- if (fileio->bufs[i].vaddr == NULL)
+ if (fileio->bufs[i].vaddr == NULL) {
+ ret = -EINVAL;
goto err_reqbufs;
+ }
fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
}
diff --git a/drivers/net/ethernet/mellanox/Kconfig b/drivers/net/ethernet/mellanox/Kconfig
index bcdbc14..8cf7563 100644
--- a/drivers/net/ethernet/mellanox/Kconfig
+++ b/drivers/net/ethernet/mellanox/Kconfig
@@ -19,5 +19,6 @@
if NET_VENDOR_MELLANOX
source "drivers/net/ethernet/mellanox/mlx4/Kconfig"
+source "drivers/net/ethernet/mellanox/mlx5/core/Kconfig"
endif # NET_VENDOR_MELLANOX
diff --git a/drivers/net/ethernet/mellanox/Makefile b/drivers/net/ethernet/mellanox/Makefile
index 37afb96..38fe32ef 100644
--- a/drivers/net/ethernet/mellanox/Makefile
+++ b/drivers/net/ethernet/mellanox/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_MLX4_CORE) += mlx4/
+obj-$(CONFIG_MLX5_CORE) += mlx5/core/
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
new file mode 100644
index 0000000..2196282
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -0,0 +1,18 @@
+#
+# Mellanox driver configuration
+#
+
+config MLX5_CORE
+ tristate
+ depends on PCI && X86
+ default n
+
+config MLX5_DEBUG
+ bool "Verbose debugging output" if (MLX5_CORE && EXPERT)
+ depends on MLX5_CORE
+ default y
+ ---help---
+ This option causes debugging code to be compiled into the
+ mlx5_core driver. The output can be turned on via the
+ debug_mask module parameter (which can also be set after
+ the driver is loaded through sysfs).
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
new file mode 100644
index 0000000..105780b
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MLX5_CORE) += mlx5_core.o
+
+mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
+ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \
+ mad.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
new file mode 100644
index 0000000..b215742
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/export.h>
+#include <linux/bitmap.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/mlx5/driver.h>
+
+#include "mlx5_core.h"
+
+/* Handling for queue buffers -- we allocate a bunch of memory and
+ * register it in a memory region at HCA virtual address 0. If the
+ * requested size is > max_direct, we split the allocation into
+ * multiple pages, so we don't require too much contiguous memory.
+ */
+
+int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, int max_direct,
+ struct mlx5_buf *buf)
+{
+ dma_addr_t t;
+
+ buf->size = size;
+ if (size <= max_direct) {
+ buf->nbufs = 1;
+ buf->npages = 1;
+ buf->page_shift = get_order(size) + PAGE_SHIFT;
+ buf->direct.buf = dma_zalloc_coherent(&dev->pdev->dev,
+ size, &t, GFP_KERNEL);
+ if (!buf->direct.buf)
+ return -ENOMEM;
+
+ buf->direct.map = t;
+
+ while (t & ((1 << buf->page_shift) - 1)) {
+ --buf->page_shift;
+ buf->npages *= 2;
+ }
+ } else {
+ int i;
+
+ buf->direct.buf = NULL;
+ buf->nbufs = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+ buf->npages = buf->nbufs;
+ buf->page_shift = PAGE_SHIFT;
+ buf->page_list = kcalloc(buf->nbufs, sizeof(*buf->page_list),
+ GFP_KERNEL);
+ if (!buf->page_list)
+ return -ENOMEM;
+
+ for (i = 0; i < buf->nbufs; i++) {
+ buf->page_list[i].buf =
+ dma_zalloc_coherent(&dev->pdev->dev, PAGE_SIZE,
+ &t, GFP_KERNEL);
+ if (!buf->page_list[i].buf)
+ goto err_free;
+
+ buf->page_list[i].map = t;
+ }
+
+ if (BITS_PER_LONG == 64) {
+ struct page **pages;
+ pages = kmalloc(sizeof(*pages) * buf->nbufs, GFP_KERNEL);
+ if (!pages)
+ goto err_free;
+ for (i = 0; i < buf->nbufs; i++)
+ pages[i] = virt_to_page(buf->page_list[i].buf);
+ buf->direct.buf = vmap(pages, buf->nbufs, VM_MAP, PAGE_KERNEL);
+ kfree(pages);
+ if (!buf->direct.buf)
+ goto err_free;
+ }
+ }
+
+ return 0;
+
+err_free:
+ mlx5_buf_free(dev, buf);
+
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(mlx5_buf_alloc);
+
+void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf)
+{
+ int i;
+
+ if (buf->nbufs == 1)
+ dma_free_coherent(&dev->pdev->dev, buf->size, buf->direct.buf,
+ buf->direct.map);
+ else {
+ if (BITS_PER_LONG == 64 && buf->direct.buf)
+ vunmap(buf->direct.buf);
+
+ for (i = 0; i < buf->nbufs; i++)
+ if (buf->page_list[i].buf)
+ dma_free_coherent(&dev->pdev->dev, PAGE_SIZE,
+ buf->page_list[i].buf,
+ buf->page_list[i].map);
+ kfree(buf->page_list);
+ }
+}
+EXPORT_SYMBOL_GPL(mlx5_buf_free);
+
+static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct device *dma_device)
+{
+ struct mlx5_db_pgdir *pgdir;
+
+ pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL);
+ if (!pgdir)
+ return NULL;
+
+ bitmap_fill(pgdir->bitmap, MLX5_DB_PER_PAGE);
+ pgdir->db_page = dma_alloc_coherent(dma_device, PAGE_SIZE,
+ &pgdir->db_dma, GFP_KERNEL);
+ if (!pgdir->db_page) {
+ kfree(pgdir);
+ return NULL;
+ }
+
+ return pgdir;
+}
+
+static int mlx5_alloc_db_from_pgdir(struct mlx5_db_pgdir *pgdir,
+ struct mlx5_db *db)
+{
+ int offset;
+ int i;
+
+ i = find_first_bit(pgdir->bitmap, MLX5_DB_PER_PAGE);
+ if (i >= MLX5_DB_PER_PAGE)
+ return -ENOMEM;
+
+ __clear_bit(i, pgdir->bitmap);
+
+ db->u.pgdir = pgdir;
+ db->index = i;
+ offset = db->index * L1_CACHE_BYTES;
+ db->db = pgdir->db_page + offset / sizeof(*pgdir->db_page);
+ db->dma = pgdir->db_dma + offset;
+
+ return 0;
+}
+
+int mlx5_db_alloc(struct mlx5_core_dev *dev, struct mlx5_db *db)
+{
+ struct mlx5_db_pgdir *pgdir;
+ int ret = 0;
+
+ mutex_lock(&dev->priv.pgdir_mutex);
+
+ list_for_each_entry(pgdir, &dev->priv.pgdir_list, list)
+ if (!mlx5_alloc_db_from_pgdir(pgdir, db))
+ goto out;
+
+ pgdir = mlx5_alloc_db_pgdir(&(dev->pdev->dev));
+ if (!pgdir) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ list_add(&pgdir->list, &dev->priv.pgdir_list);
+
+ /* This should never fail -- we just allocated an empty page: */
+ WARN_ON(mlx5_alloc_db_from_pgdir(pgdir, db));
+
+out:
+ mutex_unlock(&dev->priv.pgdir_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mlx5_db_alloc);
+
+void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db)
+{
+ mutex_lock(&dev->priv.pgdir_mutex);
+
+ __set_bit(db->index, db->u.pgdir->bitmap);
+
+ if (bitmap_full(db->u.pgdir->bitmap, MLX5_DB_PER_PAGE)) {
+ dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE,
+ db->u.pgdir->db_page, db->u.pgdir->db_dma);
+ list_del(&db->u.pgdir->list);
+ kfree(db->u.pgdir);
+ }
+
+ mutex_unlock(&dev->priv.pgdir_mutex);
+}
+EXPORT_SYMBOL_GPL(mlx5_db_free);
+
+
+void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas)
+{
+ u64 addr;
+ int i;
+
+ for (i = 0; i < buf->npages; i++) {
+ if (buf->nbufs == 1)
+ addr = buf->direct.map + (i << buf->page_shift);
+ else
+ addr = buf->page_list[i].map;
+
+ pas[i] = cpu_to_be64(addr);
+ }
+}
+EXPORT_SYMBOL_GPL(mlx5_fill_page_array);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
new file mode 100644
index 0000000..205753a
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -0,0 +1,1515 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <asm-generic/kmap_types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/io-mapping.h>
+#include <linux/mlx5/driver.h>
+#include <linux/debugfs.h>
+
+#include "mlx5_core.h"
+
+enum {
+ CMD_IF_REV = 3,
+};
+
+enum {
+ CMD_MODE_POLLING,
+ CMD_MODE_EVENTS
+};
+
+enum {
+ NUM_LONG_LISTS = 2,
+ NUM_MED_LISTS = 64,
+ LONG_LIST_SIZE = (2ULL * 1024 * 1024 * 1024 / PAGE_SIZE) * 8 + 16 +
+ MLX5_CMD_DATA_BLOCK_SIZE,
+ MED_LIST_SIZE = 16 + MLX5_CMD_DATA_BLOCK_SIZE,
+};
+
+enum {
+ MLX5_CMD_DELIVERY_STAT_OK = 0x0,
+ MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR = 0x1,
+ MLX5_CMD_DELIVERY_STAT_TOK_ERR = 0x2,
+ MLX5_CMD_DELIVERY_STAT_BAD_BLK_NUM_ERR = 0x3,
+ MLX5_CMD_DELIVERY_STAT_OUT_PTR_ALIGN_ERR = 0x4,
+ MLX5_CMD_DELIVERY_STAT_IN_PTR_ALIGN_ERR = 0x5,
+ MLX5_CMD_DELIVERY_STAT_FW_ERR = 0x6,
+ MLX5_CMD_DELIVERY_STAT_IN_LENGTH_ERR = 0x7,
+ MLX5_CMD_DELIVERY_STAT_OUT_LENGTH_ERR = 0x8,
+ MLX5_CMD_DELIVERY_STAT_RES_FLD_NOT_CLR_ERR = 0x9,
+ MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR = 0x10,
+};
+
+enum {
+ MLX5_CMD_STAT_OK = 0x0,
+ MLX5_CMD_STAT_INT_ERR = 0x1,
+ MLX5_CMD_STAT_BAD_OP_ERR = 0x2,
+ MLX5_CMD_STAT_BAD_PARAM_ERR = 0x3,
+ MLX5_CMD_STAT_BAD_SYS_STATE_ERR = 0x4,
+ MLX5_CMD_STAT_BAD_RES_ERR = 0x5,
+ MLX5_CMD_STAT_RES_BUSY = 0x6,
+ MLX5_CMD_STAT_LIM_ERR = 0x8,
+ MLX5_CMD_STAT_BAD_RES_STATE_ERR = 0x9,
+ MLX5_CMD_STAT_IX_ERR = 0xa,
+ MLX5_CMD_STAT_NO_RES_ERR = 0xf,
+ MLX5_CMD_STAT_BAD_INP_LEN_ERR = 0x50,
+ MLX5_CMD_STAT_BAD_OUTP_LEN_ERR = 0x51,
+ MLX5_CMD_STAT_BAD_QP_STATE_ERR = 0x10,
+ MLX5_CMD_STAT_BAD_PKT_ERR = 0x30,
+ MLX5_CMD_STAT_BAD_SIZE_OUTS_CQES_ERR = 0x40,
+};
+
+static struct mlx5_cmd_work_ent *alloc_cmd(struct mlx5_cmd *cmd,
+ struct mlx5_cmd_msg *in,
+ struct mlx5_cmd_msg *out,
+ mlx5_cmd_cbk_t cbk,
+ void *context, int page_queue)
+{
+ gfp_t alloc_flags = cbk ? GFP_ATOMIC : GFP_KERNEL;
+ struct mlx5_cmd_work_ent *ent;
+
+ ent = kzalloc(sizeof(*ent), alloc_flags);
+ if (!ent)
+ return ERR_PTR(-ENOMEM);
+
+ ent->in = in;
+ ent->out = out;
+ ent->callback = cbk;
+ ent->context = context;
+ ent->cmd = cmd;
+ ent->page_queue = page_queue;
+
+ return ent;
+}
+
+static u8 alloc_token(struct mlx5_cmd *cmd)
+{
+ u8 token;
+
+ spin_lock(&cmd->token_lock);
+ token = cmd->token++ % 255 + 1;
+ spin_unlock(&cmd->token_lock);
+
+ return token;
+}
+
+static int alloc_ent(struct mlx5_cmd *cmd)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&cmd->alloc_lock, flags);
+ ret = find_first_bit(&cmd->bitmask, cmd->max_reg_cmds);
+ if (ret < cmd->max_reg_cmds)
+ clear_bit(ret, &cmd->bitmask);
+ spin_unlock_irqrestore(&cmd->alloc_lock, flags);
+
+ return ret < cmd->max_reg_cmds ? ret : -ENOMEM;
+}
+
+static void free_ent(struct mlx5_cmd *cmd, int idx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cmd->alloc_lock, flags);
+ set_bit(idx, &cmd->bitmask);
+ spin_unlock_irqrestore(&cmd->alloc_lock, flags);
+}
+
+static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx)
+{
+ return cmd->cmd_buf + (idx << cmd->log_stride);
+}
+
+static u8 xor8_buf(void *buf, int len)
+{
+ u8 *ptr = buf;
+ u8 sum = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ sum ^= ptr[i];
+
+ return sum;
+}
+
+static int verify_block_sig(struct mlx5_cmd_prot_block *block)
+{
+ if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff)
+ return -EINVAL;
+
+ if (xor8_buf(block, sizeof(*block)) != 0xff)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token)
+{
+ block->token = token;
+ block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 2);
+ block->sig = ~xor8_buf(block, sizeof(*block) - 1);
+}
+
+static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token)
+{
+ struct mlx5_cmd_mailbox *next = msg->next;
+
+ while (next) {
+ calc_block_sig(next->buf, token);
+ next = next->next;
+ }
+}
+
+static void set_signature(struct mlx5_cmd_work_ent *ent)
+{
+ ent->lay->sig = ~xor8_buf(ent->lay, sizeof(*ent->lay));
+ calc_chain_sig(ent->in, ent->token);
+ calc_chain_sig(ent->out, ent->token);
+}
+
+static void poll_timeout(struct mlx5_cmd_work_ent *ent)
+{
+ unsigned long poll_end = jiffies + msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC + 1000);
+ u8 own;
+
+ do {
+ own = ent->lay->status_own;
+ if (!(own & CMD_OWNER_HW)) {
+ ent->ret = 0;
+ return;
+ }
+ usleep_range(5000, 10000);
+ } while (time_before(jiffies, poll_end));
+
+ ent->ret = -ETIMEDOUT;
+}
+
+static void free_cmd(struct mlx5_cmd_work_ent *ent)
+{
+ kfree(ent);
+}
+
+
+static int verify_signature(struct mlx5_cmd_work_ent *ent)
+{
+ struct mlx5_cmd_mailbox *next = ent->out->next;
+ int err;
+ u8 sig;
+
+ sig = xor8_buf(ent->lay, sizeof(*ent->lay));
+ if (sig != 0xff)
+ return -EINVAL;
+
+ while (next) {
+ err = verify_block_sig(next->buf);
+ if (err)
+ return err;
+
+ next = next->next;
+ }
+
+ return 0;
+}
+
+static void dump_buf(void *buf, int size, int data_only, int offset)
+{
+ __be32 *p = buf;
+ int i;
+
+ for (i = 0; i < size; i += 16) {
+ pr_debug("%03x: %08x %08x %08x %08x\n", offset, be32_to_cpu(p[0]),
+ be32_to_cpu(p[1]), be32_to_cpu(p[2]),
+ be32_to_cpu(p[3]));
+ p += 4;
+ offset += 16;
+ }
+ if (!data_only)
+ pr_debug("\n");
+}
+
+const char *mlx5_command_str(int command)
+{
+ switch (command) {
+ case MLX5_CMD_OP_QUERY_HCA_CAP:
+ return "QUERY_HCA_CAP";
+
+ case MLX5_CMD_OP_SET_HCA_CAP:
+ return "SET_HCA_CAP";
+
+ case MLX5_CMD_OP_QUERY_ADAPTER:
+ return "QUERY_ADAPTER";
+
+ case MLX5_CMD_OP_INIT_HCA:
+ return "INIT_HCA";
+
+ case MLX5_CMD_OP_TEARDOWN_HCA:
+ return "TEARDOWN_HCA";
+
+ case MLX5_CMD_OP_QUERY_PAGES:
+ return "QUERY_PAGES";
+
+ case MLX5_CMD_OP_MANAGE_PAGES:
+ return "MANAGE_PAGES";
+
+ case MLX5_CMD_OP_CREATE_MKEY:
+ return "CREATE_MKEY";
+
+ case MLX5_CMD_OP_QUERY_MKEY:
+ return "QUERY_MKEY";
+
+ case MLX5_CMD_OP_DESTROY_MKEY:
+ return "DESTROY_MKEY";
+
+ case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS:
+ return "QUERY_SPECIAL_CONTEXTS";
+
+ case MLX5_CMD_OP_CREATE_EQ:
+ return "CREATE_EQ";
+
+ case MLX5_CMD_OP_DESTROY_EQ:
+ return "DESTROY_EQ";
+
+ case MLX5_CMD_OP_QUERY_EQ:
+ return "QUERY_EQ";
+
+ case MLX5_CMD_OP_CREATE_CQ:
+ return "CREATE_CQ";
+
+ case MLX5_CMD_OP_DESTROY_CQ:
+ return "DESTROY_CQ";
+
+ case MLX5_CMD_OP_QUERY_CQ:
+ return "QUERY_CQ";
+
+ case MLX5_CMD_OP_MODIFY_CQ:
+ return "MODIFY_CQ";
+
+ case MLX5_CMD_OP_CREATE_QP:
+ return "CREATE_QP";
+
+ case MLX5_CMD_OP_DESTROY_QP:
+ return "DESTROY_QP";
+
+ case MLX5_CMD_OP_RST2INIT_QP:
+ return "RST2INIT_QP";
+
+ case MLX5_CMD_OP_INIT2RTR_QP:
+ return "INIT2RTR_QP";
+
+ case MLX5_CMD_OP_RTR2RTS_QP:
+ return "RTR2RTS_QP";
+
+ case MLX5_CMD_OP_RTS2RTS_QP:
+ return "RTS2RTS_QP";
+
+ case MLX5_CMD_OP_SQERR2RTS_QP:
+ return "SQERR2RTS_QP";
+
+ case MLX5_CMD_OP_2ERR_QP:
+ return "2ERR_QP";
+
+ case MLX5_CMD_OP_RTS2SQD_QP:
+ return "RTS2SQD_QP";
+
+ case MLX5_CMD_OP_SQD2RTS_QP:
+ return "SQD2RTS_QP";
+
+ case MLX5_CMD_OP_2RST_QP:
+ return "2RST_QP";
+
+ case MLX5_CMD_OP_QUERY_QP:
+ return "QUERY_QP";
+
+ case MLX5_CMD_OP_CONF_SQP:
+ return "CONF_SQP";
+
+ case MLX5_CMD_OP_MAD_IFC:
+ return "MAD_IFC";
+
+ case MLX5_CMD_OP_INIT2INIT_QP:
+ return "INIT2INIT_QP";
+
+ case MLX5_CMD_OP_SUSPEND_QP:
+ return "SUSPEND_QP";
+
+ case MLX5_CMD_OP_UNSUSPEND_QP:
+ return "UNSUSPEND_QP";
+
+ case MLX5_CMD_OP_SQD2SQD_QP:
+ return "SQD2SQD_QP";
+
+ case MLX5_CMD_OP_ALLOC_QP_COUNTER_SET:
+ return "ALLOC_QP_COUNTER_SET";
+
+ case MLX5_CMD_OP_DEALLOC_QP_COUNTER_SET:
+ return "DEALLOC_QP_COUNTER_SET";
+
+ case MLX5_CMD_OP_QUERY_QP_COUNTER_SET:
+ return "QUERY_QP_COUNTER_SET";
+
+ case MLX5_CMD_OP_CREATE_PSV:
+ return "CREATE_PSV";
+
+ case MLX5_CMD_OP_DESTROY_PSV:
+ return "DESTROY_PSV";
+
+ case MLX5_CMD_OP_QUERY_PSV:
+ return "QUERY_PSV";
+
+ case MLX5_CMD_OP_QUERY_SIG_RULE_TABLE:
+ return "QUERY_SIG_RULE_TABLE";
+
+ case MLX5_CMD_OP_QUERY_BLOCK_SIZE_TABLE:
+ return "QUERY_BLOCK_SIZE_TABLE";
+
+ case MLX5_CMD_OP_CREATE_SRQ:
+ return "CREATE_SRQ";
+
+ case MLX5_CMD_OP_DESTROY_SRQ:
+ return "DESTROY_SRQ";
+
+ case MLX5_CMD_OP_QUERY_SRQ:
+ return "QUERY_SRQ";
+
+ case MLX5_CMD_OP_ARM_RQ:
+ return "ARM_RQ";
+
+ case MLX5_CMD_OP_RESIZE_SRQ:
+ return "RESIZE_SRQ";
+
+ case MLX5_CMD_OP_ALLOC_PD:
+ return "ALLOC_PD";
+
+ case MLX5_CMD_OP_DEALLOC_PD:
+ return "DEALLOC_PD";
+
+ case MLX5_CMD_OP_ALLOC_UAR:
+ return "ALLOC_UAR";
+
+ case MLX5_CMD_OP_DEALLOC_UAR:
+ return "DEALLOC_UAR";
+
+ case MLX5_CMD_OP_ATTACH_TO_MCG:
+ return "ATTACH_TO_MCG";
+
+ case MLX5_CMD_OP_DETACH_FROM_MCG:
+ return "DETACH_FROM_MCG";
+
+ case MLX5_CMD_OP_ALLOC_XRCD:
+ return "ALLOC_XRCD";
+
+ case MLX5_CMD_OP_DEALLOC_XRCD:
+ return "DEALLOC_XRCD";
+
+ case MLX5_CMD_OP_ACCESS_REG:
+ return "MLX5_CMD_OP_ACCESS_REG";
+
+ default: return "unknown command opcode";
+ }
+}
+
+static void dump_command(struct mlx5_core_dev *dev,
+ struct mlx5_cmd_work_ent *ent, int input)
+{
+ u16 op = be16_to_cpu(((struct mlx5_inbox_hdr *)(ent->lay->in))->opcode);
+ struct mlx5_cmd_msg *msg = input ? ent->in : ent->out;
+ struct mlx5_cmd_mailbox *next = msg->next;
+ int data_only;
+ int offset = 0;
+ int dump_len;
+
+ data_only = !!(mlx5_core_debug_mask & (1 << MLX5_CMD_DATA));
+
+ if (data_only)
+ mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_DATA,
+ "dump command data %s(0x%x) %s\n",
+ mlx5_command_str(op), op,
+ input ? "INPUT" : "OUTPUT");
+ else
+ mlx5_core_dbg(dev, "dump command %s(0x%x) %s\n",
+ mlx5_command_str(op), op,
+ input ? "INPUT" : "OUTPUT");
+
+ if (data_only) {
+ if (input) {
+ dump_buf(ent->lay->in, sizeof(ent->lay->in), 1, offset);
+ offset += sizeof(ent->lay->in);
+ } else {
+ dump_buf(ent->lay->out, sizeof(ent->lay->out), 1, offset);
+ offset += sizeof(ent->lay->out);
+ }
+ } else {
+ dump_buf(ent->lay, sizeof(*ent->lay), 0, offset);
+ offset += sizeof(*ent->lay);
+ }
+
+ while (next && offset < msg->len) {
+ if (data_only) {
+ dump_len = min_t(int, MLX5_CMD_DATA_BLOCK_SIZE, msg->len - offset);
+ dump_buf(next->buf, dump_len, 1, offset);
+ offset += MLX5_CMD_DATA_BLOCK_SIZE;
+ } else {
+ mlx5_core_dbg(dev, "command block:\n");
+ dump_buf(next->buf, sizeof(struct mlx5_cmd_prot_block), 0, offset);
+ offset += sizeof(struct mlx5_cmd_prot_block);
+ }
+ next = next->next;
+ }
+
+ if (data_only)
+ pr_debug("\n");
+}
+
+static void cmd_work_handler(struct work_struct *work)
+{
+ struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work);
+ struct mlx5_cmd *cmd = ent->cmd;
+ struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd);
+ struct mlx5_cmd_layout *lay;
+ struct semaphore *sem;
+
+ sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem;
+ down(sem);
+ if (!ent->page_queue) {
+ ent->idx = alloc_ent(cmd);
+ if (ent->idx < 0) {
+ mlx5_core_err(dev, "failed to allocate command entry\n");
+ up(sem);
+ return;
+ }
+ } else {
+ ent->idx = cmd->max_reg_cmds;
+ }
+
+ ent->token = alloc_token(cmd);
+ cmd->ent_arr[ent->idx] = ent;
+ lay = get_inst(cmd, ent->idx);
+ ent->lay = lay;
+ memset(lay, 0, sizeof(*lay));
+ memcpy(lay->in, ent->in->first.data, sizeof(lay->in));
+ if (ent->in->next)
+ lay->in_ptr = cpu_to_be64(ent->in->next->dma);
+ lay->inlen = cpu_to_be32(ent->in->len);
+ if (ent->out->next)
+ lay->out_ptr = cpu_to_be64(ent->out->next->dma);
+ lay->outlen = cpu_to_be32(ent->out->len);
+ lay->type = MLX5_PCI_CMD_XPORT;
+ lay->token = ent->token;
+ lay->status_own = CMD_OWNER_HW;
+ if (!cmd->checksum_disabled)
+ set_signature(ent);
+ dump_command(dev, ent, 1);
+ ktime_get_ts(&ent->ts1);
+
+ /* ring doorbell after the descriptor is valid */
+ wmb();
+ iowrite32be(1 << ent->idx, &dev->iseg->cmd_dbell);
+ mlx5_core_dbg(dev, "write 0x%x to command doorbell\n", 1 << ent->idx);
+ mmiowb();
+ if (cmd->mode == CMD_MODE_POLLING) {
+ poll_timeout(ent);
+ /* make sure we read the descriptor after ownership is SW */
+ rmb();
+ mlx5_cmd_comp_handler(dev, 1UL << ent->idx);
+ }
+}
+
+static const char *deliv_status_to_str(u8 status)
+{
+ switch (status) {
+ case MLX5_CMD_DELIVERY_STAT_OK:
+ return "no errors";
+ case MLX5_CMD_DELIVERY_STAT_SIGNAT_ERR:
+ return "signature error";
+ case MLX5_CMD_DELIVERY_STAT_TOK_ERR:
+ return "token error";
+ case MLX5_CMD_DELIVERY_STAT_BAD_BLK_NUM_ERR:
+ return "bad block number";
+ case MLX5_CMD_DELIVERY_STAT_OUT_PTR_ALIGN_ERR:
+ return "output pointer not aligned to block size";
+ case MLX5_CMD_DELIVERY_STAT_IN_PTR_ALIGN_ERR:
+ return "input pointer not aligned to block size";
+ case MLX5_CMD_DELIVERY_STAT_FW_ERR:
+ return "firmware internal error";
+ case MLX5_CMD_DELIVERY_STAT_IN_LENGTH_ERR:
+ return "command input length error";
+ case MLX5_CMD_DELIVERY_STAT_OUT_LENGTH_ERR:
+ return "command ouput length error";
+ case MLX5_CMD_DELIVERY_STAT_RES_FLD_NOT_CLR_ERR:
+ return "reserved fields not cleared";
+ case MLX5_CMD_DELIVERY_STAT_CMD_DESCR_ERR:
+ return "bad command descriptor type";
+ default:
+ return "unknown status code";
+ }
+}
+
+static u16 msg_to_opcode(struct mlx5_cmd_msg *in)
+{
+ struct mlx5_inbox_hdr *hdr = (struct mlx5_inbox_hdr *)(in->first.data);
+
+ return be16_to_cpu(hdr->opcode);
+}
+
+static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
+{
+ unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC);
+ struct mlx5_cmd *cmd = &dev->cmd;
+ int err;
+
+ if (cmd->mode == CMD_MODE_POLLING) {
+ wait_for_completion(&ent->done);
+ err = ent->ret;
+ } else {
+ if (!wait_for_completion_timeout(&ent->done, timeout))
+ err = -ETIMEDOUT;
+ else
+ err = 0;
+ }
+ if (err == -ETIMEDOUT) {
+ mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n",
+ mlx5_command_str(msg_to_opcode(ent->in)),
+ msg_to_opcode(ent->in));
+ }
+ mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n", err,
+ deliv_status_to_str(ent->status), ent->status);
+
+ return err;
+}
+
+/* Notes:
+ * 1. Callback functions may not sleep
+ * 2. page queue commands do not support asynchrous completion
+ */
+static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
+ struct mlx5_cmd_msg *out, mlx5_cmd_cbk_t callback,
+ void *context, int page_queue, u8 *status)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ struct mlx5_cmd_work_ent *ent;
+ ktime_t t1, t2, delta;
+ struct mlx5_cmd_stats *stats;
+ int err = 0;
+ s64 ds;
+ u16 op;
+
+ if (callback && page_queue)
+ return -EINVAL;
+
+ ent = alloc_cmd(cmd, in, out, callback, context, page_queue);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ if (!callback)
+ init_completion(&ent->done);
+
+ INIT_WORK(&ent->work, cmd_work_handler);
+ if (page_queue) {
+ cmd_work_handler(&ent->work);
+ } else if (!queue_work(cmd->wq, &ent->work)) {
+ mlx5_core_warn(dev, "failed to queue work\n");
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ if (!callback) {
+ err = wait_func(dev, ent);
+ if (err == -ETIMEDOUT)
+ goto out;
+
+ t1 = timespec_to_ktime(ent->ts1);
+ t2 = timespec_to_ktime(ent->ts2);
+ delta = ktime_sub(t2, t1);
+ ds = ktime_to_ns(delta);
+ op = be16_to_cpu(((struct mlx5_inbox_hdr *)in->first.data)->opcode);
+ if (op < ARRAY_SIZE(cmd->stats)) {
+ stats = &cmd->stats[op];
+ spin_lock(&stats->lock);
+ stats->sum += ds;
+ ++stats->n;
+ spin_unlock(&stats->lock);
+ }
+ mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME,
+ "fw exec time for %s is %lld nsec\n",
+ mlx5_command_str(op), ds);
+ *status = ent->status;
+ free_cmd(ent);
+ }
+
+ return err;
+
+out_free:
+ free_cmd(ent);
+out:
+ return err;
+}
+
+static ssize_t dbg_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mlx5_core_dev *dev = filp->private_data;
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+ char lbuf[3];
+ int err;
+
+ if (!dbg->in_msg || !dbg->out_msg)
+ return -ENOMEM;
+
+ if (copy_from_user(lbuf, buf, sizeof(lbuf)))
+ return -EFAULT;
+
+ lbuf[sizeof(lbuf) - 1] = 0;
+
+ if (strcmp(lbuf, "go"))
+ return -EINVAL;
+
+ err = mlx5_cmd_exec(dev, dbg->in_msg, dbg->inlen, dbg->out_msg, dbg->outlen);
+
+ return err ? err : count;
+}
+
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = dbg_write,
+};
+
+static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size)
+{
+ struct mlx5_cmd_prot_block *block;
+ struct mlx5_cmd_mailbox *next;
+ int copy;
+
+ if (!to || !from)
+ return -ENOMEM;
+
+ copy = min_t(int, size, sizeof(to->first.data));
+ memcpy(to->first.data, from, copy);
+ size -= copy;
+ from += copy;
+
+ next = to->next;
+ while (size) {
+ if (!next) {
+ /* this is a BUG */
+ return -ENOMEM;
+ }
+
+ copy = min_t(int, size, MLX5_CMD_DATA_BLOCK_SIZE);
+ block = next->buf;
+ memcpy(block->data, from, copy);
+ from += copy;
+ size -= copy;
+ next = next->next;
+ }
+
+ return 0;
+}
+
+static int mlx5_copy_from_msg(void *to, struct mlx5_cmd_msg *from, int size)
+{
+ struct mlx5_cmd_prot_block *block;
+ struct mlx5_cmd_mailbox *next;
+ int copy;
+
+ if (!to || !from)
+ return -ENOMEM;
+
+ copy = min_t(int, size, sizeof(from->first.data));
+ memcpy(to, from->first.data, copy);
+ size -= copy;
+ to += copy;
+
+ next = from->next;
+ while (size) {
+ if (!next) {
+ /* this is a BUG */
+ return -ENOMEM;
+ }
+
+ copy = min_t(int, size, MLX5_CMD_DATA_BLOCK_SIZE);
+ block = next->buf;
+ if (xor8_buf(block, sizeof(*block)) != 0xff)
+ return -EINVAL;
+
+ memcpy(to, block->data, copy);
+ to += copy;
+ size -= copy;
+ next = next->next;
+ }
+
+ return 0;
+}
+
+static struct mlx5_cmd_mailbox *alloc_cmd_box(struct mlx5_core_dev *dev,
+ gfp_t flags)
+{
+ struct mlx5_cmd_mailbox *mailbox;
+
+ mailbox = kmalloc(sizeof(*mailbox), flags);
+ if (!mailbox)
+ return ERR_PTR(-ENOMEM);
+
+ mailbox->buf = pci_pool_alloc(dev->cmd.pool, flags,
+ &mailbox->dma);
+ if (!mailbox->buf) {
+ mlx5_core_dbg(dev, "failed allocation\n");
+ kfree(mailbox);
+ return ERR_PTR(-ENOMEM);
+ }
+ memset(mailbox->buf, 0, sizeof(struct mlx5_cmd_prot_block));
+ mailbox->next = NULL;
+
+ return mailbox;
+}
+
+static void free_cmd_box(struct mlx5_core_dev *dev,
+ struct mlx5_cmd_mailbox *mailbox)
+{
+ pci_pool_free(dev->cmd.pool, mailbox->buf, mailbox->dma);
+ kfree(mailbox);
+}
+
+static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,
+ gfp_t flags, int size)
+{
+ struct mlx5_cmd_mailbox *tmp, *head = NULL;
+ struct mlx5_cmd_prot_block *block;
+ struct mlx5_cmd_msg *msg;
+ int blen;
+ int err;
+ int n;
+ int i;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return ERR_PTR(-ENOMEM);
+
+ blen = size - min_t(int, sizeof(msg->first.data), size);
+ n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1) / MLX5_CMD_DATA_BLOCK_SIZE;
+
+ for (i = 0; i < n; i++) {
+ tmp = alloc_cmd_box(dev, flags);
+ if (IS_ERR(tmp)) {
+ mlx5_core_warn(dev, "failed allocating block\n");
+ err = PTR_ERR(tmp);
+ goto err_alloc;
+ }
+
+ block = tmp->buf;
+ tmp->next = head;
+ block->next = cpu_to_be64(tmp->next ? tmp->next->dma : 0);
+ block->block_num = cpu_to_be32(n - i - 1);
+ head = tmp;
+ }
+ msg->next = head;
+ msg->len = size;
+ return msg;
+
+err_alloc:
+ while (head) {
+ tmp = head->next;
+ free_cmd_box(dev, head);
+ head = tmp;
+ }
+ kfree(msg);
+
+ return ERR_PTR(err);
+}
+
+static void mlx5_free_cmd_msg(struct mlx5_core_dev *dev,
+ struct mlx5_cmd_msg *msg)
+{
+ struct mlx5_cmd_mailbox *head = msg->next;
+ struct mlx5_cmd_mailbox *next;
+
+ while (head) {
+ next = head->next;
+ free_cmd_box(dev, head);
+ head = next;
+ }
+ kfree(msg);
+}
+
+static ssize_t data_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mlx5_core_dev *dev = filp->private_data;
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+ void *ptr;
+ int err;
+
+ if (*pos != 0)
+ return -EINVAL;
+
+ kfree(dbg->in_msg);
+ dbg->in_msg = NULL;
+ dbg->inlen = 0;
+
+ ptr = kzalloc(count, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ if (copy_from_user(ptr, buf, count)) {
+ err = -EFAULT;
+ goto out;
+ }
+ dbg->in_msg = ptr;
+ dbg->inlen = count;
+
+ *pos = count;
+
+ return count;
+
+out:
+ kfree(ptr);
+ return err;
+}
+
+static ssize_t data_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct mlx5_core_dev *dev = filp->private_data;
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+ int copy;
+
+ if (*pos)
+ return 0;
+
+ if (!dbg->out_msg)
+ return -ENOMEM;
+
+ copy = min_t(int, count, dbg->outlen);
+ if (copy_to_user(buf, dbg->out_msg, copy))
+ return -EFAULT;
+
+ *pos += copy;
+
+ return copy;
+}
+
+static const struct file_operations dfops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = data_write,
+ .read = data_read,
+};
+
+static ssize_t outlen_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct mlx5_core_dev *dev = filp->private_data;
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+ char outlen[8];
+ int err;
+
+ if (*pos)
+ return 0;
+
+ err = snprintf(outlen, sizeof(outlen), "%d", dbg->outlen);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(buf, &outlen, err))
+ return -EFAULT;
+
+ *pos += err;
+
+ return err;
+}
+
+static ssize_t outlen_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mlx5_core_dev *dev = filp->private_data;
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+ char outlen_str[8];
+ int outlen;
+ void *ptr;
+ int err;
+
+ if (*pos != 0 || count > 6)
+ return -EINVAL;
+
+ kfree(dbg->out_msg);
+ dbg->out_msg = NULL;
+ dbg->outlen = 0;
+
+ if (copy_from_user(outlen_str, buf, count))
+ return -EFAULT;
+
+ outlen_str[7] = 0;
+
+ err = sscanf(outlen_str, "%d", &outlen);
+ if (err < 0)
+ return err;
+
+ ptr = kzalloc(outlen, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ dbg->out_msg = ptr;
+ dbg->outlen = outlen;
+
+ *pos = count;
+
+ return count;
+}
+
+static const struct file_operations olfops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = outlen_write,
+ .read = outlen_read,
+};
+
+static void set_wqname(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+
+ snprintf(cmd->wq_name, sizeof(cmd->wq_name), "mlx5_cmd_%s",
+ dev_name(&dev->pdev->dev));
+}
+
+static void clean_debug_files(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+
+ if (!mlx5_debugfs_root)
+ return;
+
+ mlx5_cmdif_debugfs_cleanup(dev);
+ debugfs_remove_recursive(dbg->dbg_root);
+}
+
+static int create_debugfs_files(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_debug *dbg = &dev->cmd.dbg;
+ int err = -ENOMEM;
+
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ dbg->dbg_root = debugfs_create_dir("cmd", dev->priv.dbg_root);
+ if (!dbg->dbg_root)
+ return err;
+
+ dbg->dbg_in = debugfs_create_file("in", 0400, dbg->dbg_root,
+ dev, &dfops);
+ if (!dbg->dbg_in)
+ goto err_dbg;
+
+ dbg->dbg_out = debugfs_create_file("out", 0200, dbg->dbg_root,
+ dev, &dfops);
+ if (!dbg->dbg_out)
+ goto err_dbg;
+
+ dbg->dbg_outlen = debugfs_create_file("out_len", 0600, dbg->dbg_root,
+ dev, &olfops);
+ if (!dbg->dbg_outlen)
+ goto err_dbg;
+
+ dbg->dbg_status = debugfs_create_u8("status", 0600, dbg->dbg_root,
+ &dbg->status);
+ if (!dbg->dbg_status)
+ goto err_dbg;
+
+ dbg->dbg_run = debugfs_create_file("run", 0200, dbg->dbg_root, dev, &fops);
+ if (!dbg->dbg_run)
+ goto err_dbg;
+
+ mlx5_cmdif_debugfs_init(dev);
+
+ return 0;
+
+err_dbg:
+ clean_debug_files(dev);
+ return err;
+}
+
+void mlx5_cmd_use_events(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ int i;
+
+ for (i = 0; i < cmd->max_reg_cmds; i++)
+ down(&cmd->sem);
+
+ down(&cmd->pages_sem);
+
+ flush_workqueue(cmd->wq);
+
+ cmd->mode = CMD_MODE_EVENTS;
+
+ up(&cmd->pages_sem);
+ for (i = 0; i < cmd->max_reg_cmds; i++)
+ up(&cmd->sem);
+}
+
+void mlx5_cmd_use_polling(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ int i;
+
+ for (i = 0; i < cmd->max_reg_cmds; i++)
+ down(&cmd->sem);
+
+ down(&cmd->pages_sem);
+
+ flush_workqueue(cmd->wq);
+ cmd->mode = CMD_MODE_POLLING;
+
+ up(&cmd->pages_sem);
+ for (i = 0; i < cmd->max_reg_cmds; i++)
+ up(&cmd->sem);
+}
+
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ struct mlx5_cmd_work_ent *ent;
+ mlx5_cmd_cbk_t callback;
+ void *context;
+ int err;
+ int i;
+
+ for (i = 0; i < (1 << cmd->log_sz); i++) {
+ if (test_bit(i, &vector)) {
+ ent = cmd->ent_arr[i];
+ ktime_get_ts(&ent->ts2);
+ memcpy(ent->out->first.data, ent->lay->out, sizeof(ent->lay->out));
+ dump_command(dev, ent, 0);
+ if (!ent->ret) {
+ if (!cmd->checksum_disabled)
+ ent->ret = verify_signature(ent);
+ else
+ ent->ret = 0;
+ ent->status = ent->lay->status_own >> 1;
+ mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n",
+ ent->ret, deliv_status_to_str(ent->status), ent->status);
+ }
+ free_ent(cmd, ent->idx);
+ if (ent->callback) {
+ callback = ent->callback;
+ context = ent->context;
+ err = ent->ret;
+ free_cmd(ent);
+ callback(err, context);
+ } else {
+ complete(&ent->done);
+ }
+ if (ent->page_queue)
+ up(&cmd->pages_sem);
+ else
+ up(&cmd->sem);
+ }
+ }
+}
+EXPORT_SYMBOL(mlx5_cmd_comp_handler);
+
+static int status_to_err(u8 status)
+{
+ return status ? -1 : 0; /* TBD more meaningful codes */
+}
+
+static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size)
+{
+ struct mlx5_cmd_msg *msg = ERR_PTR(-ENOMEM);
+ struct mlx5_cmd *cmd = &dev->cmd;
+ struct cache_ent *ent = NULL;
+
+ if (in_size > MED_LIST_SIZE && in_size <= LONG_LIST_SIZE)
+ ent = &cmd->cache.large;
+ else if (in_size > 16 && in_size <= MED_LIST_SIZE)
+ ent = &cmd->cache.med;
+
+ if (ent) {
+ spin_lock(&ent->lock);
+ if (!list_empty(&ent->head)) {
+ msg = list_entry(ent->head.next, typeof(*msg), list);
+ /* For cached lists, we must explicitly state what is
+ * the real size
+ */
+ msg->len = in_size;
+ list_del(&msg->list);
+ }
+ spin_unlock(&ent->lock);
+ }
+
+ if (IS_ERR(msg))
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, in_size);
+
+ return msg;
+}
+
+static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
+{
+ if (msg->cache) {
+ spin_lock(&msg->cache->lock);
+ list_add_tail(&msg->list, &msg->cache->head);
+ spin_unlock(&msg->cache->lock);
+ } else {
+ mlx5_free_cmd_msg(dev, msg);
+ }
+}
+
+static int is_manage_pages(struct mlx5_inbox_hdr *in)
+{
+ return be16_to_cpu(in->opcode) == MLX5_CMD_OP_MANAGE_PAGES;
+}
+
+int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
+ int out_size)
+{
+ struct mlx5_cmd_msg *inb;
+ struct mlx5_cmd_msg *outb;
+ int pages_queue;
+ int err;
+ u8 status = 0;
+
+ pages_queue = is_manage_pages(in);
+
+ inb = alloc_msg(dev, in_size);
+ if (IS_ERR(inb)) {
+ err = PTR_ERR(inb);
+ return err;
+ }
+
+ err = mlx5_copy_to_msg(inb, in, in_size);
+ if (err) {
+ mlx5_core_warn(dev, "err %d\n", err);
+ goto out_in;
+ }
+
+ outb = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, out_size);
+ if (IS_ERR(outb)) {
+ err = PTR_ERR(outb);
+ goto out_in;
+ }
+
+ err = mlx5_cmd_invoke(dev, inb, outb, NULL, NULL, pages_queue, &status);
+ if (err)
+ goto out_out;
+
+ mlx5_core_dbg(dev, "err %d, status %d\n", err, status);
+ if (status) {
+ err = status_to_err(status);
+ goto out_out;
+ }
+
+ err = mlx5_copy_from_msg(out, outb, out_size);
+
+out_out:
+ mlx5_free_cmd_msg(dev, outb);
+
+out_in:
+ free_msg(dev, inb);
+ return err;
+}
+EXPORT_SYMBOL(mlx5_cmd_exec);
+
+static void destroy_msg_cache(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ struct mlx5_cmd_msg *msg;
+ struct mlx5_cmd_msg *n;
+
+ list_for_each_entry_safe(msg, n, &cmd->cache.large.head, list) {
+ list_del(&msg->list);
+ mlx5_free_cmd_msg(dev, msg);
+ }
+
+ list_for_each_entry_safe(msg, n, &cmd->cache.med.head, list) {
+ list_del(&msg->list);
+ mlx5_free_cmd_msg(dev, msg);
+ }
+}
+
+static int create_msg_cache(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ struct mlx5_cmd_msg *msg;
+ int err;
+ int i;
+
+ spin_lock_init(&cmd->cache.large.lock);
+ INIT_LIST_HEAD(&cmd->cache.large.head);
+ spin_lock_init(&cmd->cache.med.lock);
+ INIT_LIST_HEAD(&cmd->cache.med.head);
+
+ for (i = 0; i < NUM_LONG_LISTS; i++) {
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE);
+ if (IS_ERR(msg)) {
+ err = PTR_ERR(msg);
+ goto ex_err;
+ }
+ msg->cache = &cmd->cache.large;
+ list_add_tail(&msg->list, &cmd->cache.large.head);
+ }
+
+ for (i = 0; i < NUM_MED_LISTS; i++) {
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE);
+ if (IS_ERR(msg)) {
+ err = PTR_ERR(msg);
+ goto ex_err;
+ }
+ msg->cache = &cmd->cache.med;
+ list_add_tail(&msg->list, &cmd->cache.med.head);
+ }
+
+ return 0;
+
+ex_err:
+ destroy_msg_cache(dev);
+ return err;
+}
+
+int mlx5_cmd_init(struct mlx5_core_dev *dev)
+{
+ int size = sizeof(struct mlx5_cmd_prot_block);
+ int align = roundup_pow_of_two(size);
+ struct mlx5_cmd *cmd = &dev->cmd;
+ u32 cmd_h, cmd_l;
+ u16 cmd_if_rev;
+ int err;
+ int i;
+
+ cmd_if_rev = cmdif_rev(dev);
+ if (cmd_if_rev != CMD_IF_REV) {
+ dev_err(&dev->pdev->dev,
+ "Driver cmdif rev(%d) differs from firmware's(%d)\n",
+ CMD_IF_REV, cmd_if_rev);
+ return -EINVAL;
+ }
+
+ cmd->pool = pci_pool_create("mlx5_cmd", dev->pdev, size, align, 0);
+ if (!cmd->pool)
+ return -ENOMEM;
+
+ cmd->cmd_buf = (void *)__get_free_pages(GFP_ATOMIC, 0);
+ if (!cmd->cmd_buf) {
+ err = -ENOMEM;
+ goto err_free_pool;
+ }
+ cmd->dma = dma_map_single(&dev->pdev->dev, cmd->cmd_buf, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&dev->pdev->dev, cmd->dma)) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff;
+ cmd->log_sz = cmd_l >> 4 & 0xf;
+ cmd->log_stride = cmd_l & 0xf;
+ if (1 << cmd->log_sz > MLX5_MAX_COMMANDS) {
+ dev_err(&dev->pdev->dev, "firmware reports too many outstanding commands %d\n",
+ 1 << cmd->log_sz);
+ err = -EINVAL;
+ goto err_map;
+ }
+
+ if (cmd->log_sz + cmd->log_stride > PAGE_SHIFT) {
+ dev_err(&dev->pdev->dev, "command queue size overflow\n");
+ err = -EINVAL;
+ goto err_map;
+ }
+
+ cmd->max_reg_cmds = (1 << cmd->log_sz) - 1;
+ cmd->bitmask = (1 << cmd->max_reg_cmds) - 1;
+
+ cmd->cmdif_rev = ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16;
+ if (cmd->cmdif_rev > CMD_IF_REV) {
+ dev_err(&dev->pdev->dev, "driver does not support command interface version. driver %d, firmware %d\n",
+ CMD_IF_REV, cmd->cmdif_rev);
+ err = -ENOTSUPP;
+ goto err_map;
+ }
+
+ spin_lock_init(&cmd->alloc_lock);
+ spin_lock_init(&cmd->token_lock);
+ for (i = 0; i < ARRAY_SIZE(cmd->stats); i++)
+ spin_lock_init(&cmd->stats[i].lock);
+
+ sema_init(&cmd->sem, cmd->max_reg_cmds);
+ sema_init(&cmd->pages_sem, 1);
+
+ cmd_h = (u32)((u64)(cmd->dma) >> 32);
+ cmd_l = (u32)(cmd->dma);
+ if (cmd_l & 0xfff) {
+ dev_err(&dev->pdev->dev, "invalid command queue address\n");
+ err = -ENOMEM;
+ goto err_map;
+ }
+
+ iowrite32be(cmd_h, &dev->iseg->cmdq_addr_h);
+ iowrite32be(cmd_l, &dev->iseg->cmdq_addr_l_sz);
+
+ /* Make sure firmware sees the complete address before we proceed */
+ wmb();
+
+ mlx5_core_dbg(dev, "descriptor at dma 0x%llx\n", (unsigned long long)(cmd->dma));
+
+ cmd->mode = CMD_MODE_POLLING;
+
+ err = create_msg_cache(dev);
+ if (err) {
+ dev_err(&dev->pdev->dev, "failed to create command cache\n");
+ goto err_map;
+ }
+
+ set_wqname(dev);
+ cmd->wq = create_singlethread_workqueue(cmd->wq_name);
+ if (!cmd->wq) {
+ dev_err(&dev->pdev->dev, "failed to create command workqueue\n");
+ err = -ENOMEM;
+ goto err_cache;
+ }
+
+ err = create_debugfs_files(dev);
+ if (err) {
+ err = -ENOMEM;
+ goto err_wq;
+ }
+
+ return 0;
+
+err_wq:
+ destroy_workqueue(cmd->wq);
+
+err_cache:
+ destroy_msg_cache(dev);
+
+err_map:
+ dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+err_free:
+ free_pages((unsigned long)cmd->cmd_buf, 0);
+
+err_free_pool:
+ pci_pool_destroy(cmd->pool);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_cmd_init);
+
+void mlx5_cmd_cleanup(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+
+ clean_debug_files(dev);
+ destroy_workqueue(cmd->wq);
+ destroy_msg_cache(dev);
+ dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ free_pages((unsigned long)cmd->cmd_buf, 0);
+ pci_pool_destroy(cmd->pool);
+}
+EXPORT_SYMBOL(mlx5_cmd_cleanup);
+
+static const char *cmd_status_str(u8 status)
+{
+ switch (status) {
+ case MLX5_CMD_STAT_OK:
+ return "OK";
+ case MLX5_CMD_STAT_INT_ERR:
+ return "internal error";
+ case MLX5_CMD_STAT_BAD_OP_ERR:
+ return "bad operation";
+ case MLX5_CMD_STAT_BAD_PARAM_ERR:
+ return "bad parameter";
+ case MLX5_CMD_STAT_BAD_SYS_STATE_ERR:
+ return "bad system state";
+ case MLX5_CMD_STAT_BAD_RES_ERR:
+ return "bad resource";
+ case MLX5_CMD_STAT_RES_BUSY:
+ return "resource busy";
+ case MLX5_CMD_STAT_LIM_ERR:
+ return "limits exceeded";
+ case MLX5_CMD_STAT_BAD_RES_STATE_ERR:
+ return "bad resource state";
+ case MLX5_CMD_STAT_IX_ERR:
+ return "bad index";
+ case MLX5_CMD_STAT_NO_RES_ERR:
+ return "no resources";
+ case MLX5_CMD_STAT_BAD_INP_LEN_ERR:
+ return "bad input length";
+ case MLX5_CMD_STAT_BAD_OUTP_LEN_ERR:
+ return "bad output length";
+ case MLX5_CMD_STAT_BAD_QP_STATE_ERR:
+ return "bad QP state";
+ case MLX5_CMD_STAT_BAD_PKT_ERR:
+ return "bad packet (discarded)";
+ case MLX5_CMD_STAT_BAD_SIZE_OUTS_CQES_ERR:
+ return "bad size too many outstanding CQEs";
+ default:
+ return "unknown status";
+ }
+}
+
+int mlx5_cmd_status_to_err(struct mlx5_outbox_hdr *hdr)
+{
+ if (!hdr->status)
+ return 0;
+
+ pr_warn("command failed, status %s(0x%x), syndrome 0x%x\n",
+ cmd_status_str(hdr->status), hdr->status,
+ be32_to_cpu(hdr->syndrome));
+
+ switch (hdr->status) {
+ case MLX5_CMD_STAT_OK: return 0;
+ case MLX5_CMD_STAT_INT_ERR: return -EIO;
+ case MLX5_CMD_STAT_BAD_OP_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_BAD_PARAM_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_BAD_SYS_STATE_ERR: return -EIO;
+ case MLX5_CMD_STAT_BAD_RES_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_RES_BUSY: return -EBUSY;
+ case MLX5_CMD_STAT_LIM_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_BAD_RES_STATE_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_IX_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_NO_RES_ERR: return -EAGAIN;
+ case MLX5_CMD_STAT_BAD_INP_LEN_ERR: return -EIO;
+ case MLX5_CMD_STAT_BAD_OUTP_LEN_ERR: return -EIO;
+ case MLX5_CMD_STAT_BAD_QP_STATE_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_BAD_PKT_ERR: return -EINVAL;
+ case MLX5_CMD_STAT_BAD_SIZE_OUTS_CQES_ERR: return -EINVAL;
+ default: return -EIO;
+ }
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
new file mode 100644
index 0000000..c2d660b
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hardirq.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include <rdma/ib_verbs.h>
+#include <linux/mlx5/cq.h>
+#include "mlx5_core.h"
+
+void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn)
+{
+ struct mlx5_core_cq *cq;
+ struct mlx5_cq_table *table = &dev->priv.cq_table;
+
+ spin_lock(&table->lock);
+ cq = radix_tree_lookup(&table->tree, cqn);
+ if (likely(cq))
+ atomic_inc(&cq->refcount);
+ spin_unlock(&table->lock);
+
+ if (!cq) {
+ mlx5_core_warn(dev, "Completion event for bogus CQ 0x%x\n", cqn);
+ return;
+ }
+
+ ++cq->arm_sn;
+
+ cq->comp(cq);
+
+ if (atomic_dec_and_test(&cq->refcount))
+ complete(&cq->free);
+}
+
+void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type)
+{
+ struct mlx5_cq_table *table = &dev->priv.cq_table;
+ struct mlx5_core_cq *cq;
+
+ spin_lock(&table->lock);
+
+ cq = radix_tree_lookup(&table->tree, cqn);
+ if (cq)
+ atomic_inc(&cq->refcount);
+
+ spin_unlock(&table->lock);
+
+ if (!cq) {
+ mlx5_core_warn(dev, "Async event for bogus CQ 0x%x\n", cqn);
+ return;
+ }
+
+ cq->event(cq, event_type);
+
+ if (atomic_dec_and_test(&cq->refcount))
+ complete(&cq->free);
+}
+
+
+int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ struct mlx5_create_cq_mbox_in *in, int inlen)
+{
+ int err;
+ struct mlx5_cq_table *table = &dev->priv.cq_table;
+ struct mlx5_create_cq_mbox_out out;
+ struct mlx5_destroy_cq_mbox_in din;
+ struct mlx5_destroy_cq_mbox_out dout;
+
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_CQ);
+ memset(&out, 0, sizeof(out));
+ err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ cq->cqn = be32_to_cpu(out.cqn) & 0xffffff;
+ cq->cons_index = 0;
+ cq->arm_sn = 0;
+ atomic_set(&cq->refcount, 1);
+ init_completion(&cq->free);
+
+ spin_lock_irq(&table->lock);
+ err = radix_tree_insert(&table->tree, cq->cqn, cq);
+ spin_unlock_irq(&table->lock);
+ if (err)
+ goto err_cmd;
+
+ cq->pid = current->pid;
+ err = mlx5_debug_cq_add(dev, cq);
+ if (err)
+ mlx5_core_dbg(dev, "failed adding CP 0x%x to debug file system\n",
+ cq->cqn);
+
+ return 0;
+
+err_cmd:
+ memset(&din, 0, sizeof(din));
+ memset(&dout, 0, sizeof(dout));
+ din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_CQ);
+ mlx5_cmd_exec(dev, &din, sizeof(din), &dout, sizeof(dout));
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_create_cq);
+
+int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
+{
+ struct mlx5_cq_table *table = &dev->priv.cq_table;
+ struct mlx5_destroy_cq_mbox_in in;
+ struct mlx5_destroy_cq_mbox_out out;
+ struct mlx5_core_cq *tmp;
+ int err;
+
+ spin_lock_irq(&table->lock);
+ tmp = radix_tree_delete(&table->tree, cq->cqn);
+ spin_unlock_irq(&table->lock);
+ if (!tmp) {
+ mlx5_core_warn(dev, "cq 0x%x not found in tree\n", cq->cqn);
+ return -EINVAL;
+ }
+ if (tmp != cq) {
+ mlx5_core_warn(dev, "corruption on srqn 0x%x\n", cq->cqn);
+ return -EINVAL;
+ }
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_CQ);
+ in.cqn = cpu_to_be32(cq->cqn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ synchronize_irq(cq->irqn);
+
+ mlx5_debug_cq_remove(dev, cq);
+ if (atomic_dec_and_test(&cq->refcount))
+ complete(&cq->free);
+ wait_for_completion(&cq->free);
+
+ return 0;
+}
+EXPORT_SYMBOL(mlx5_core_destroy_cq);
+
+int mlx5_core_query_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ struct mlx5_query_cq_mbox_out *out)
+{
+ struct mlx5_query_cq_mbox_in in;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(out, 0, sizeof(*out));
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_CQ);
+ in.cqn = cpu_to_be32(cq->cqn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out));
+ if (err)
+ return err;
+
+ if (out->hdr.status)
+ return mlx5_cmd_status_to_err(&out->hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_query_cq);
+
+
+int mlx5_core_modify_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ int type, struct mlx5_cq_modify_params *params)
+{
+ return -ENOSYS;
+}
+
+int mlx5_init_cq_table(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cq_table *table = &dev->priv.cq_table;
+ int err;
+
+ spin_lock_init(&table->lock);
+ INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
+ err = mlx5_cq_debugfs_init(dev);
+
+ return err;
+}
+
+void mlx5_cleanup_cq_table(struct mlx5_core_dev *dev)
+{
+ mlx5_cq_debugfs_cleanup(dev);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
new file mode 100644
index 0000000..4273c06
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/mlx5/qp.h>
+#include <linux/mlx5/cq.h>
+#include <linux/mlx5/driver.h>
+#include "mlx5_core.h"
+
+enum {
+ QP_PID,
+ QP_STATE,
+ QP_XPORT,
+ QP_MTU,
+ QP_N_RECV,
+ QP_RECV_SZ,
+ QP_N_SEND,
+ QP_LOG_PG_SZ,
+ QP_RQPN,
+};
+
+static char *qp_fields[] = {
+ [QP_PID] = "pid",
+ [QP_STATE] = "state",
+ [QP_XPORT] = "transport",
+ [QP_MTU] = "mtu",
+ [QP_N_RECV] = "num_recv",
+ [QP_RECV_SZ] = "rcv_wqe_sz",
+ [QP_N_SEND] = "num_send",
+ [QP_LOG_PG_SZ] = "log2_page_sz",
+ [QP_RQPN] = "remote_qpn",
+};
+
+enum {
+ EQ_NUM_EQES,
+ EQ_INTR,
+ EQ_LOG_PG_SZ,
+};
+
+static char *eq_fields[] = {
+ [EQ_NUM_EQES] = "num_eqes",
+ [EQ_INTR] = "intr",
+ [EQ_LOG_PG_SZ] = "log_page_size",
+};
+
+enum {
+ CQ_PID,
+ CQ_NUM_CQES,
+ CQ_LOG_PG_SZ,
+};
+
+static char *cq_fields[] = {
+ [CQ_PID] = "pid",
+ [CQ_NUM_CQES] = "num_cqes",
+ [CQ_LOG_PG_SZ] = "log_page_size",
+};
+
+struct dentry *mlx5_debugfs_root;
+EXPORT_SYMBOL(mlx5_debugfs_root);
+
+void mlx5_register_debugfs(void)
+{
+ mlx5_debugfs_root = debugfs_create_dir("mlx5", NULL);
+ if (IS_ERR_OR_NULL(mlx5_debugfs_root))
+ mlx5_debugfs_root = NULL;
+}
+
+void mlx5_unregister_debugfs(void)
+{
+ debugfs_remove(mlx5_debugfs_root);
+}
+
+int mlx5_qp_debugfs_init(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ atomic_set(&dev->num_qps, 0);
+
+ dev->priv.qp_debugfs = debugfs_create_dir("QPs", dev->priv.dbg_root);
+ if (!dev->priv.qp_debugfs)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ debugfs_remove_recursive(dev->priv.qp_debugfs);
+}
+
+int mlx5_eq_debugfs_init(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ dev->priv.eq_debugfs = debugfs_create_dir("EQs", dev->priv.dbg_root);
+ if (!dev->priv.eq_debugfs)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ debugfs_remove_recursive(dev->priv.eq_debugfs);
+}
+
+static ssize_t average_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct mlx5_cmd_stats *stats;
+ u64 field = 0;
+ int ret;
+ char tbuf[22];
+
+ if (*pos)
+ return 0;
+
+ stats = filp->private_data;
+ spin_lock(&stats->lock);
+ if (stats->n)
+ field = stats->sum / stats->n;
+ spin_unlock(&stats->lock);
+ ret = snprintf(tbuf, sizeof(tbuf), "%llu\n", field);
+ if (ret > 0) {
+ if (copy_to_user(buf, tbuf, ret))
+ return -EFAULT;
+ }
+
+ *pos += ret;
+ return ret;
+}
+
+
+static ssize_t average_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct mlx5_cmd_stats *stats;
+
+ stats = filp->private_data;
+ spin_lock(&stats->lock);
+ stats->sum = 0;
+ stats->n = 0;
+ spin_unlock(&stats->lock);
+
+ *pos += count;
+
+ return count;
+}
+
+static const struct file_operations stats_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = average_read,
+ .write = average_write,
+};
+
+int mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_stats *stats;
+ struct dentry **cmd;
+ const char *namep;
+ int err;
+ int i;
+
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ cmd = &dev->priv.cmdif_debugfs;
+ *cmd = debugfs_create_dir("commands", dev->priv.dbg_root);
+ if (!*cmd)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(dev->cmd.stats); i++) {
+ stats = &dev->cmd.stats[i];
+ namep = mlx5_command_str(i);
+ if (strcmp(namep, "unknown command opcode")) {
+ stats->root = debugfs_create_dir(namep, *cmd);
+ if (!stats->root) {
+ mlx5_core_warn(dev, "failed adding command %d\n",
+ i);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ stats->avg = debugfs_create_file("average", 0400,
+ stats->root, stats,
+ &stats_fops);
+ if (!stats->avg) {
+ mlx5_core_warn(dev, "failed creating debugfs file\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ stats->count = debugfs_create_u64("n", 0400,
+ stats->root,
+ &stats->n);
+ if (!stats->count) {
+ mlx5_core_warn(dev, "failed creating debugfs file\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ }
+
+ return 0;
+out:
+ debugfs_remove_recursive(dev->priv.cmdif_debugfs);
+ return err;
+}
+
+void mlx5_cmdif_debugfs_cleanup(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ debugfs_remove_recursive(dev->priv.cmdif_debugfs);
+}
+
+int mlx5_cq_debugfs_init(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ dev->priv.cq_debugfs = debugfs_create_dir("CQs", dev->priv.dbg_root);
+ if (!dev->priv.cq_debugfs)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ debugfs_remove_recursive(dev->priv.cq_debugfs);
+}
+
+static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
+ int index)
+{
+ struct mlx5_query_qp_mbox_out *out;
+ struct mlx5_qp_context *ctx;
+ u64 param = 0;
+ int err;
+ int no_sq;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ return param;
+
+ err = mlx5_core_qp_query(dev, qp, out, sizeof(*out));
+ if (err) {
+ mlx5_core_warn(dev, "failed to query qp\n");
+ goto out;
+ }
+
+ ctx = &out->ctx;
+ switch (index) {
+ case QP_PID:
+ param = qp->pid;
+ break;
+ case QP_STATE:
+ param = be32_to_cpu(ctx->flags) >> 28;
+ break;
+ case QP_XPORT:
+ param = (be32_to_cpu(ctx->flags) >> 16) & 0xff;
+ break;
+ case QP_MTU:
+ param = ctx->mtu_msgmax >> 5;
+ break;
+ case QP_N_RECV:
+ param = 1 << ((ctx->rq_size_stride >> 3) & 0xf);
+ break;
+ case QP_RECV_SZ:
+ param = 1 << ((ctx->rq_size_stride & 7) + 4);
+ break;
+ case QP_N_SEND:
+ no_sq = be16_to_cpu(ctx->sq_crq_size) >> 15;
+ if (!no_sq)
+ param = 1 << (be16_to_cpu(ctx->sq_crq_size) >> 11);
+ else
+ param = 0;
+ break;
+ case QP_LOG_PG_SZ:
+ param = (be32_to_cpu(ctx->log_pg_sz_remote_qpn) >> 24) & 0x1f;
+ param += 12;
+ break;
+ case QP_RQPN:
+ param = be32_to_cpu(ctx->log_pg_sz_remote_qpn) & 0xffffff;
+ break;
+ }
+
+out:
+ kfree(out);
+ return param;
+}
+
+static u64 eq_read_field(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
+ int index)
+{
+ struct mlx5_query_eq_mbox_out *out;
+ struct mlx5_eq_context *ctx;
+ u64 param = 0;
+ int err;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ return param;
+
+ ctx = &out->ctx;
+
+ err = mlx5_core_eq_query(dev, eq, out, sizeof(*out));
+ if (err) {
+ mlx5_core_warn(dev, "failed to query eq\n");
+ goto out;
+ }
+
+ switch (index) {
+ case EQ_NUM_EQES:
+ param = 1 << ((be32_to_cpu(ctx->log_sz_usr_page) >> 24) & 0x1f);
+ break;
+ case EQ_INTR:
+ param = ctx->intr;
+ break;
+ case EQ_LOG_PG_SZ:
+ param = (ctx->log_page_size & 0x1f) + 12;
+ break;
+ }
+
+out:
+ kfree(out);
+ return param;
+}
+
+static u64 cq_read_field(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ int index)
+{
+ struct mlx5_query_cq_mbox_out *out;
+ struct mlx5_cq_context *ctx;
+ u64 param = 0;
+ int err;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ return param;
+
+ ctx = &out->ctx;
+
+ err = mlx5_core_query_cq(dev, cq, out);
+ if (err) {
+ mlx5_core_warn(dev, "failed to query cq\n");
+ goto out;
+ }
+
+ switch (index) {
+ case CQ_PID:
+ param = cq->pid;
+ break;
+ case CQ_NUM_CQES:
+ param = 1 << ((be32_to_cpu(ctx->log_sz_usr_page) >> 24) & 0x1f);
+ break;
+ case CQ_LOG_PG_SZ:
+ param = (ctx->log_pg_sz & 0x1f) + 12;
+ break;
+ }
+
+out:
+ kfree(out);
+ return param;
+}
+
+static ssize_t dbg_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct mlx5_field_desc *desc;
+ struct mlx5_rsc_debug *d;
+ char tbuf[18];
+ u64 field;
+ int ret;
+
+ if (*pos)
+ return 0;
+
+ desc = filp->private_data;
+ d = (void *)(desc - desc->i) - sizeof(*d);
+ switch (d->type) {
+ case MLX5_DBG_RSC_QP:
+ field = qp_read_field(d->dev, d->object, desc->i);
+ break;
+
+ case MLX5_DBG_RSC_EQ:
+ field = eq_read_field(d->dev, d->object, desc->i);
+ break;
+
+ case MLX5_DBG_RSC_CQ:
+ field = cq_read_field(d->dev, d->object, desc->i);
+ break;
+
+ default:
+ mlx5_core_warn(d->dev, "invalid resource type %d\n", d->type);
+ return -EINVAL;
+ }
+
+ ret = snprintf(tbuf, sizeof(tbuf), "0x%llx\n", field);
+ if (ret > 0) {
+ if (copy_to_user(buf, tbuf, ret))
+ return -EFAULT;
+ }
+
+ *pos += ret;
+ return ret;
+}
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = dbg_read,
+};
+
+static int add_res_tree(struct mlx5_core_dev *dev, enum dbg_rsc_type type,
+ struct dentry *root, struct mlx5_rsc_debug **dbg,
+ int rsn, char **field, int nfile, void *data)
+{
+ struct mlx5_rsc_debug *d;
+ char resn[32];
+ int err;
+ int i;
+
+ d = kzalloc(sizeof(*d) + nfile * sizeof(d->fields[0]), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ d->dev = dev;
+ d->object = data;
+ d->type = type;
+ sprintf(resn, "0x%x", rsn);
+ d->root = debugfs_create_dir(resn, root);
+ if (!d->root) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ for (i = 0; i < nfile; i++) {
+ d->fields[i].i = i;
+ d->fields[i].dent = debugfs_create_file(field[i], 0400,
+ d->root, &d->fields[i],
+ &fops);
+ if (!d->fields[i].dent) {
+ err = -ENOMEM;
+ goto out_rem;
+ }
+ }
+ *dbg = d;
+
+ return 0;
+out_rem:
+ debugfs_remove_recursive(d->root);
+
+out_free:
+ kfree(d);
+ return err;
+}
+
+static void rem_res_tree(struct mlx5_rsc_debug *d)
+{
+ debugfs_remove_recursive(d->root);
+ kfree(d);
+}
+
+int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp)
+{
+ int err;
+
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ err = add_res_tree(dev, MLX5_DBG_RSC_QP, dev->priv.qp_debugfs,
+ &qp->dbg, qp->qpn, qp_fields,
+ ARRAY_SIZE(qp_fields), qp);
+ if (err)
+ qp->dbg = NULL;
+
+ return err;
+}
+
+void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ if (qp->dbg)
+ rem_res_tree(qp->dbg);
+}
+
+
+int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+{
+ int err;
+
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ err = add_res_tree(dev, MLX5_DBG_RSC_EQ, dev->priv.eq_debugfs,
+ &eq->dbg, eq->eqn, eq_fields,
+ ARRAY_SIZE(eq_fields), eq);
+ if (err)
+ eq->dbg = NULL;
+
+ return err;
+}
+
+void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ if (eq->dbg)
+ rem_res_tree(eq->dbg);
+}
+
+int mlx5_debug_cq_add(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
+{
+ int err;
+
+ if (!mlx5_debugfs_root)
+ return 0;
+
+ err = add_res_tree(dev, MLX5_DBG_RSC_CQ, dev->priv.cq_debugfs,
+ &cq->dbg, cq->cqn, cq_fields,
+ ARRAY_SIZE(cq_fields), cq);
+ if (err)
+ cq->dbg = NULL;
+
+ return err;
+}
+
+void mlx5_debug_cq_remove(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
+{
+ if (!mlx5_debugfs_root)
+ return;
+
+ if (cq->dbg)
+ rem_res_tree(cq->dbg);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
new file mode 100644
index 0000000..c02cbcf
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+enum {
+ MLX5_EQE_SIZE = sizeof(struct mlx5_eqe),
+ MLX5_EQE_OWNER_INIT_VAL = 0x1,
+};
+
+enum {
+ MLX5_EQ_STATE_ARMED = 0x9,
+ MLX5_EQ_STATE_FIRED = 0xa,
+ MLX5_EQ_STATE_ALWAYS_ARMED = 0xb,
+};
+
+enum {
+ MLX5_NUM_SPARE_EQE = 0x80,
+ MLX5_NUM_ASYNC_EQE = 0x100,
+ MLX5_NUM_CMD_EQE = 32,
+};
+
+enum {
+ MLX5_EQ_DOORBEL_OFFSET = 0x40,
+};
+
+#define MLX5_ASYNC_EVENT_MASK ((1ull << MLX5_EVENT_TYPE_PATH_MIG) | \
+ (1ull << MLX5_EVENT_TYPE_COMM_EST) | \
+ (1ull << MLX5_EVENT_TYPE_SQ_DRAINED) | \
+ (1ull << MLX5_EVENT_TYPE_CQ_ERROR) | \
+ (1ull << MLX5_EVENT_TYPE_WQ_CATAS_ERROR) | \
+ (1ull << MLX5_EVENT_TYPE_PATH_MIG_FAILED) | \
+ (1ull << MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \
+ (1ull << MLX5_EVENT_TYPE_WQ_ACCESS_ERROR) | \
+ (1ull << MLX5_EVENT_TYPE_PORT_CHANGE) | \
+ (1ull << MLX5_EVENT_TYPE_SRQ_CATAS_ERROR) | \
+ (1ull << MLX5_EVENT_TYPE_SRQ_LAST_WQE) | \
+ (1ull << MLX5_EVENT_TYPE_SRQ_RQ_LIMIT))
+
+struct map_eq_in {
+ u64 mask;
+ u32 reserved;
+ u32 unmap_eqn;
+};
+
+struct cre_des_eq {
+ u8 reserved[15];
+ u8 eqn;
+};
+
+static int mlx5_cmd_destroy_eq(struct mlx5_core_dev *dev, u8 eqn)
+{
+ struct mlx5_destroy_eq_mbox_in in;
+ struct mlx5_destroy_eq_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_EQ);
+ in.eqn = eqn;
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (!err)
+ goto ex;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ex:
+ return err;
+}
+
+static struct mlx5_eqe *get_eqe(struct mlx5_eq *eq, u32 entry)
+{
+ return mlx5_buf_offset(&eq->buf, entry * MLX5_EQE_SIZE);
+}
+
+static struct mlx5_eqe *next_eqe_sw(struct mlx5_eq *eq)
+{
+ struct mlx5_eqe *eqe = get_eqe(eq, eq->cons_index & (eq->nent - 1));
+
+ return ((eqe->owner & 1) ^ !!(eq->cons_index & eq->nent)) ? NULL : eqe;
+}
+
+static const char *eqe_type_str(u8 type)
+{
+ switch (type) {
+ case MLX5_EVENT_TYPE_COMP:
+ return "MLX5_EVENT_TYPE_COMP";
+ case MLX5_EVENT_TYPE_PATH_MIG:
+ return "MLX5_EVENT_TYPE_PATH_MIG";
+ case MLX5_EVENT_TYPE_COMM_EST:
+ return "MLX5_EVENT_TYPE_COMM_EST";
+ case MLX5_EVENT_TYPE_SQ_DRAINED:
+ return "MLX5_EVENT_TYPE_SQ_DRAINED";
+ case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
+ return "MLX5_EVENT_TYPE_SRQ_LAST_WQE";
+ case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
+ return "MLX5_EVENT_TYPE_SRQ_RQ_LIMIT";
+ case MLX5_EVENT_TYPE_CQ_ERROR:
+ return "MLX5_EVENT_TYPE_CQ_ERROR";
+ case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
+ return "MLX5_EVENT_TYPE_WQ_CATAS_ERROR";
+ case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
+ return "MLX5_EVENT_TYPE_PATH_MIG_FAILED";
+ case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+ return "MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR";
+ case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
+ return "MLX5_EVENT_TYPE_WQ_ACCESS_ERROR";
+ case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
+ return "MLX5_EVENT_TYPE_SRQ_CATAS_ERROR";
+ case MLX5_EVENT_TYPE_INTERNAL_ERROR:
+ return "MLX5_EVENT_TYPE_INTERNAL_ERROR";
+ case MLX5_EVENT_TYPE_PORT_CHANGE:
+ return "MLX5_EVENT_TYPE_PORT_CHANGE";
+ case MLX5_EVENT_TYPE_GPIO_EVENT:
+ return "MLX5_EVENT_TYPE_GPIO_EVENT";
+ case MLX5_EVENT_TYPE_REMOTE_CONFIG:
+ return "MLX5_EVENT_TYPE_REMOTE_CONFIG";
+ case MLX5_EVENT_TYPE_DB_BF_CONGESTION:
+ return "MLX5_EVENT_TYPE_DB_BF_CONGESTION";
+ case MLX5_EVENT_TYPE_STALL_EVENT:
+ return "MLX5_EVENT_TYPE_STALL_EVENT";
+ case MLX5_EVENT_TYPE_CMD:
+ return "MLX5_EVENT_TYPE_CMD";
+ case MLX5_EVENT_TYPE_PAGE_REQUEST:
+ return "MLX5_EVENT_TYPE_PAGE_REQUEST";
+ default:
+ return "Unrecognized event";
+ }
+}
+
+static enum mlx5_dev_event port_subtype_event(u8 subtype)
+{
+ switch (subtype) {
+ case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
+ return MLX5_DEV_EVENT_PORT_DOWN;
+ case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
+ return MLX5_DEV_EVENT_PORT_UP;
+ case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED:
+ return MLX5_DEV_EVENT_PORT_INITIALIZED;
+ case MLX5_PORT_CHANGE_SUBTYPE_LID:
+ return MLX5_DEV_EVENT_LID_CHANGE;
+ case MLX5_PORT_CHANGE_SUBTYPE_PKEY:
+ return MLX5_DEV_EVENT_PKEY_CHANGE;
+ case MLX5_PORT_CHANGE_SUBTYPE_GUID:
+ return MLX5_DEV_EVENT_GUID_CHANGE;
+ case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
+ return MLX5_DEV_EVENT_CLIENT_REREG;
+ }
+ return -1;
+}
+
+static void eq_update_ci(struct mlx5_eq *eq, int arm)
+{
+ __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2);
+ u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24);
+ __raw_writel((__force u32) cpu_to_be32(val), addr);
+ /* We still want ordering, just not swabbing, so add a barrier */
+ mb();
+}
+
+static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+{
+ struct mlx5_eqe *eqe;
+ int eqes_found = 0;
+ int set_ci = 0;
+ u32 cqn;
+ u32 srqn;
+ u8 port;
+
+ while ((eqe = next_eqe_sw(eq))) {
+ /*
+ * Make sure we read EQ entry contents after we've
+ * checked the ownership bit.
+ */
+ rmb();
+
+ mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n", eq->eqn, eqe_type_str(eqe->type));
+ switch (eqe->type) {
+ case MLX5_EVENT_TYPE_COMP:
+ cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff;
+ mlx5_cq_completion(dev, cqn);
+ break;
+
+ case MLX5_EVENT_TYPE_PATH_MIG:
+ case MLX5_EVENT_TYPE_COMM_EST:
+ case MLX5_EVENT_TYPE_SQ_DRAINED:
+ case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
+ case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
+ case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
+ case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+ case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
+ mlx5_core_dbg(dev, "event %s(%d) arrived\n",
+ eqe_type_str(eqe->type), eqe->type);
+ mlx5_qp_event(dev, be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff,
+ eqe->type);
+ break;
+
+ case MLX5_EVENT_TYPE_SRQ_RQ_LIMIT:
+ case MLX5_EVENT_TYPE_SRQ_CATAS_ERROR:
+ srqn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
+ mlx5_core_dbg(dev, "SRQ event %s(%d): srqn 0x%x\n",
+ eqe_type_str(eqe->type), eqe->type, srqn);
+ mlx5_srq_event(dev, srqn, eqe->type);
+ break;
+
+ case MLX5_EVENT_TYPE_CMD:
+ mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector));
+ break;
+
+ case MLX5_EVENT_TYPE_PORT_CHANGE:
+ port = (eqe->data.port.port >> 4) & 0xf;
+ switch (eqe->sub_type) {
+ case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
+ case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
+ case MLX5_PORT_CHANGE_SUBTYPE_LID:
+ case MLX5_PORT_CHANGE_SUBTYPE_PKEY:
+ case MLX5_PORT_CHANGE_SUBTYPE_GUID:
+ case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG:
+ case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED:
+ dev->event(dev, port_subtype_event(eqe->sub_type), &port);
+ break;
+ default:
+ mlx5_core_warn(dev, "Port event with unrecognized subtype: port %d, sub_type %d\n",
+ port, eqe->sub_type);
+ }
+ break;
+ case MLX5_EVENT_TYPE_CQ_ERROR:
+ cqn = be32_to_cpu(eqe->data.cq_err.cqn) & 0xffffff;
+ mlx5_core_warn(dev, "CQ error on CQN 0x%x, syndrom 0x%x\n",
+ cqn, eqe->data.cq_err.syndrome);
+ mlx5_cq_event(dev, cqn, eqe->type);
+ break;
+
+ case MLX5_EVENT_TYPE_PAGE_REQUEST:
+ {
+ u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id);
+ s16 npages = be16_to_cpu(eqe->data.req_pages.num_pages);
+
+ mlx5_core_dbg(dev, "page request for func 0x%x, napges %d\n", func_id, npages);
+ mlx5_core_req_pages_handler(dev, func_id, npages);
+ }
+ break;
+
+
+ default:
+ mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n", eqe->type, eq->eqn);
+ break;
+ }
+
+ ++eq->cons_index;
+ eqes_found = 1;
+ ++set_ci;
+
+ /* The HCA will think the queue has overflowed if we
+ * don't tell it we've been processing events. We
+ * create our EQs with MLX5_NUM_SPARE_EQE extra
+ * entries, so we must update our consumer index at
+ * least that often.
+ */
+ if (unlikely(set_ci >= MLX5_NUM_SPARE_EQE)) {
+ eq_update_ci(eq, 0);
+ set_ci = 0;
+ }
+ }
+
+ eq_update_ci(eq, 1);
+
+ return eqes_found;
+}
+
+static irqreturn_t mlx5_msix_handler(int irq, void *eq_ptr)
+{
+ struct mlx5_eq *eq = eq_ptr;
+ struct mlx5_core_dev *dev = eq->dev;
+
+ mlx5_eq_int(dev, eq);
+
+ /* MSI-X vectors always belong to us */
+ return IRQ_HANDLED;
+}
+
+static void init_eq_buf(struct mlx5_eq *eq)
+{
+ struct mlx5_eqe *eqe;
+ int i;
+
+ for (i = 0; i < eq->nent; i++) {
+ eqe = get_eqe(eq, i);
+ eqe->owner = MLX5_EQE_OWNER_INIT_VAL;
+ }
+}
+
+int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
+ int nent, u64 mask, const char *name, struct mlx5_uar *uar)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ struct mlx5_create_eq_mbox_in *in;
+ struct mlx5_create_eq_mbox_out out;
+ int err;
+ int inlen;
+
+ eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE);
+ err = mlx5_buf_alloc(dev, eq->nent * MLX5_EQE_SIZE, 2 * PAGE_SIZE,
+ &eq->buf);
+ if (err)
+ return err;
+
+ init_eq_buf(eq);
+
+ inlen = sizeof(*in) + sizeof(in->pas[0]) * eq->buf.npages;
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_buf;
+ }
+ memset(&out, 0, sizeof(out));
+
+ mlx5_fill_page_array(&eq->buf, in->pas);
+
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_EQ);
+ in->ctx.log_sz_usr_page = cpu_to_be32(ilog2(eq->nent) << 24 | uar->index);
+ in->ctx.intr = vecidx;
+ in->ctx.log_page_size = PAGE_SHIFT - 12;
+ in->events_mask = cpu_to_be64(mask);
+
+ err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ if (err)
+ goto err_in;
+
+ if (out.hdr.status) {
+ err = mlx5_cmd_status_to_err(&out.hdr);
+ goto err_in;
+ }
+
+ eq->eqn = out.eq_number;
+ err = request_irq(table->msix_arr[vecidx].vector, mlx5_msix_handler, 0,
+ name, eq);
+ if (err)
+ goto err_eq;
+
+ eq->irqn = vecidx;
+ eq->dev = dev;
+ eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET;
+
+ err = mlx5_debug_eq_add(dev, eq);
+ if (err)
+ goto err_irq;
+
+ /* EQs are created in ARMED state
+ */
+ eq_update_ci(eq, 1);
+
+ mlx5_vfree(in);
+ return 0;
+
+err_irq:
+ free_irq(table->msix_arr[vecidx].vector, eq);
+
+err_eq:
+ mlx5_cmd_destroy_eq(dev, eq->eqn);
+
+err_in:
+ mlx5_vfree(in);
+
+err_buf:
+ mlx5_buf_free(dev, &eq->buf);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_create_map_eq);
+
+int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ int err;
+
+ mlx5_debug_eq_remove(dev, eq);
+ free_irq(table->msix_arr[eq->irqn].vector, eq);
+ err = mlx5_cmd_destroy_eq(dev, eq->eqn);
+ if (err)
+ mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n",
+ eq->eqn);
+ mlx5_buf_free(dev, &eq->buf);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_destroy_unmap_eq);
+
+int mlx5_eq_init(struct mlx5_core_dev *dev)
+{
+ int err;
+
+ spin_lock_init(&dev->priv.eq_table.lock);
+
+ err = mlx5_eq_debugfs_init(dev);
+
+ return err;
+}
+
+
+void mlx5_eq_cleanup(struct mlx5_core_dev *dev)
+{
+ mlx5_eq_debugfs_cleanup(dev);
+}
+
+int mlx5_start_eqs(struct mlx5_core_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ int err;
+
+ err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD,
+ MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD,
+ "mlx5_cmd_eq", &dev->priv.uuari.uars[0]);
+ if (err) {
+ mlx5_core_warn(dev, "failed to create cmd EQ %d\n", err);
+ return err;
+ }
+
+ mlx5_cmd_use_events(dev);
+
+ err = mlx5_create_map_eq(dev, &table->async_eq, MLX5_EQ_VEC_ASYNC,
+ MLX5_NUM_ASYNC_EQE, MLX5_ASYNC_EVENT_MASK,
+ "mlx5_async_eq", &dev->priv.uuari.uars[0]);
+ if (err) {
+ mlx5_core_warn(dev, "failed to create async EQ %d\n", err);
+ goto err1;
+ }
+
+ err = mlx5_create_map_eq(dev, &table->pages_eq,
+ MLX5_EQ_VEC_PAGES,
+ dev->caps.max_vf + 1,
+ 1 << MLX5_EVENT_TYPE_PAGE_REQUEST, "mlx5_pages_eq",
+ &dev->priv.uuari.uars[0]);
+ if (err) {
+ mlx5_core_warn(dev, "failed to create pages EQ %d\n", err);
+ goto err2;
+ }
+
+ return err;
+
+err2:
+ mlx5_destroy_unmap_eq(dev, &table->async_eq);
+
+err1:
+ mlx5_cmd_use_polling(dev);
+ mlx5_destroy_unmap_eq(dev, &table->cmd_eq);
+ return err;
+}
+
+int mlx5_stop_eqs(struct mlx5_core_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ int err;
+
+ err = mlx5_destroy_unmap_eq(dev, &table->pages_eq);
+ if (err)
+ return err;
+
+ mlx5_destroy_unmap_eq(dev, &table->async_eq);
+ mlx5_cmd_use_polling(dev);
+
+ err = mlx5_destroy_unmap_eq(dev, &table->cmd_eq);
+ if (err)
+ mlx5_cmd_use_events(dev);
+
+ return err;
+}
+
+int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
+ struct mlx5_query_eq_mbox_out *out, int outlen)
+{
+ struct mlx5_query_eq_mbox_in in;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(out, 0, outlen);
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_EQ);
+ in.eqn = eq->eqn;
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
+ if (err)
+ return err;
+
+ if (out->hdr.status)
+ err = mlx5_cmd_status_to_err(&out->hdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_eq_query);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
new file mode 100644
index 0000000..72a5222
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include <linux/module.h>
+#include "mlx5_core.h"
+
+int mlx5_cmd_query_adapter(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_query_adapter_mbox_out *out;
+ struct mlx5_cmd_query_adapter_mbox_in in;
+ int err;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ memset(&in, 0, sizeof(in));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_ADAPTER);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out));
+ if (err)
+ goto out_out;
+
+ if (out->hdr.status) {
+ err = mlx5_cmd_status_to_err(&out->hdr);
+ goto out_out;
+ }
+
+ memcpy(dev->board_id, out->vsd_psid, sizeof(out->vsd_psid));
+
+out_out:
+ kfree(out);
+
+ return err;
+}
+
+int mlx5_cmd_query_hca_cap(struct mlx5_core_dev *dev,
+ struct mlx5_caps *caps)
+{
+ struct mlx5_cmd_query_hca_cap_mbox_out *out;
+ struct mlx5_cmd_query_hca_cap_mbox_in in;
+ struct mlx5_query_special_ctxs_mbox_out ctx_out;
+ struct mlx5_query_special_ctxs_mbox_in ctx_in;
+ int err;
+ u16 t16;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ memset(&in, 0, sizeof(in));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_HCA_CAP);
+ in.hdr.opmod = cpu_to_be16(0x1);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out));
+ if (err)
+ goto out_out;
+
+ if (out->hdr.status) {
+ err = mlx5_cmd_status_to_err(&out->hdr);
+ goto out_out;
+ }
+
+
+ caps->log_max_eq = out->hca_cap.log_max_eq & 0xf;
+ caps->max_cqes = 1 << out->hca_cap.log_max_cq_sz;
+ caps->max_wqes = 1 << out->hca_cap.log_max_qp_sz;
+ caps->max_sq_desc_sz = be16_to_cpu(out->hca_cap.max_desc_sz_sq);
+ caps->max_rq_desc_sz = be16_to_cpu(out->hca_cap.max_desc_sz_rq);
+ caps->flags = be64_to_cpu(out->hca_cap.flags);
+ caps->stat_rate_support = be16_to_cpu(out->hca_cap.stat_rate_support);
+ caps->log_max_msg = out->hca_cap.log_max_msg & 0x1f;
+ caps->num_ports = out->hca_cap.num_ports & 0xf;
+ caps->log_max_cq = out->hca_cap.log_max_cq & 0x1f;
+ if (caps->num_ports > MLX5_MAX_PORTS) {
+ mlx5_core_err(dev, "device has %d ports while the driver supports max %d ports\n",
+ caps->num_ports, MLX5_MAX_PORTS);
+ err = -EINVAL;
+ goto out_out;
+ }
+ caps->log_max_qp = out->hca_cap.log_max_qp & 0x1f;
+ caps->log_max_mkey = out->hca_cap.log_max_mkey & 0x3f;
+ caps->log_max_pd = out->hca_cap.log_max_pd & 0x1f;
+ caps->log_max_srq = out->hca_cap.log_max_srqs & 0x1f;
+ caps->local_ca_ack_delay = out->hca_cap.local_ca_ack_delay & 0x1f;
+ caps->log_max_mcg = out->hca_cap.log_max_mcg;
+ caps->max_qp_mcg = be16_to_cpu(out->hca_cap.max_qp_mcg);
+ caps->max_ra_res_qp = 1 << (out->hca_cap.log_max_ra_res_qp & 0x3f);
+ caps->max_ra_req_qp = 1 << (out->hca_cap.log_max_ra_req_qp & 0x3f);
+ caps->max_srq_wqes = 1 << out->hca_cap.log_max_srq_sz;
+ t16 = be16_to_cpu(out->hca_cap.bf_log_bf_reg_size);
+ if (t16 & 0x8000) {
+ caps->bf_reg_size = 1 << (t16 & 0x1f);
+ caps->bf_regs_per_page = MLX5_BF_REGS_PER_PAGE;
+ } else {
+ caps->bf_reg_size = 0;
+ caps->bf_regs_per_page = 0;
+ }
+ caps->min_page_sz = ~(u32)((1 << out->hca_cap.log_pg_sz) - 1);
+
+ memset(&ctx_in, 0, sizeof(ctx_in));
+ memset(&ctx_out, 0, sizeof(ctx_out));
+ ctx_in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS);
+ err = mlx5_cmd_exec(dev, &ctx_in, sizeof(ctx_in),
+ &ctx_out, sizeof(ctx_out));
+ if (err)
+ goto out_out;
+
+ if (ctx_out.hdr.status)
+ err = mlx5_cmd_status_to_err(&ctx_out.hdr);
+
+ caps->reserved_lkey = be32_to_cpu(ctx_out.reserved_lkey);
+
+out_out:
+ kfree(out);
+
+ return err;
+}
+
+int mlx5_cmd_init_hca(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_init_hca_mbox_in in;
+ struct mlx5_cmd_init_hca_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_INIT_HCA);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+
+int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_teardown_hca_mbox_in in;
+ struct mlx5_cmd_teardown_hca_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_TEARDOWN_HCA);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c
new file mode 100644
index 0000000..748f10a
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/vmalloc.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+enum {
+ MLX5_HEALTH_POLL_INTERVAL = 2 * HZ,
+ MAX_MISSES = 3,
+};
+
+enum {
+ MLX5_HEALTH_SYNDR_FW_ERR = 0x1,
+ MLX5_HEALTH_SYNDR_IRISC_ERR = 0x7,
+ MLX5_HEALTH_SYNDR_CRC_ERR = 0x9,
+ MLX5_HEALTH_SYNDR_FETCH_PCI_ERR = 0xa,
+ MLX5_HEALTH_SYNDR_HW_FTL_ERR = 0xb,
+ MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR = 0xc,
+ MLX5_HEALTH_SYNDR_EQ_ERR = 0xd,
+ MLX5_HEALTH_SYNDR_FFSER_ERR = 0xf,
+};
+
+static DEFINE_SPINLOCK(health_lock);
+
+static LIST_HEAD(health_list);
+static struct work_struct health_work;
+
+static health_handler_t reg_handler;
+int mlx5_register_health_report_handler(health_handler_t handler)
+{
+ spin_lock_irq(&health_lock);
+ if (reg_handler) {
+ spin_unlock_irq(&health_lock);
+ return -EEXIST;
+ }
+ reg_handler = handler;
+ spin_unlock_irq(&health_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mlx5_register_health_report_handler);
+
+void mlx5_unregister_health_report_handler(void)
+{
+ spin_lock_irq(&health_lock);
+ reg_handler = NULL;
+ spin_unlock_irq(&health_lock);
+}
+EXPORT_SYMBOL(mlx5_unregister_health_report_handler);
+
+static void health_care(struct work_struct *work)
+{
+ struct mlx5_core_health *health, *n;
+ struct mlx5_core_dev *dev;
+ struct mlx5_priv *priv;
+ LIST_HEAD(tlist);
+
+ spin_lock_irq(&health_lock);
+ list_splice_init(&health_list, &tlist);
+
+ spin_unlock_irq(&health_lock);
+
+ list_for_each_entry_safe(health, n, &tlist, list) {
+ priv = container_of(health, struct mlx5_priv, health);
+ dev = container_of(priv, struct mlx5_core_dev, priv);
+ mlx5_core_warn(dev, "handling bad device here\n");
+ spin_lock_irq(&health_lock);
+ if (reg_handler)
+ reg_handler(dev->pdev, health->health,
+ sizeof(health->health));
+
+ list_del_init(&health->list);
+ spin_unlock_irq(&health_lock);
+ }
+}
+
+static const char *hsynd_str(u8 synd)
+{
+ switch (synd) {
+ case MLX5_HEALTH_SYNDR_FW_ERR:
+ return "firmware internal error";
+ case MLX5_HEALTH_SYNDR_IRISC_ERR:
+ return "irisc not responding";
+ case MLX5_HEALTH_SYNDR_CRC_ERR:
+ return "firmware CRC error";
+ case MLX5_HEALTH_SYNDR_FETCH_PCI_ERR:
+ return "ICM fetch PCI error";
+ case MLX5_HEALTH_SYNDR_HW_FTL_ERR:
+ return "HW fatal error\n";
+ case MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR:
+ return "async EQ buffer overrun";
+ case MLX5_HEALTH_SYNDR_EQ_ERR:
+ return "EQ error";
+ case MLX5_HEALTH_SYNDR_FFSER_ERR:
+ return "FFSER error";
+ default:
+ return "unrecognized error";
+ }
+}
+
+static u16 read_be16(__be16 __iomem *p)
+{
+ return swab16(readl((__force u16 __iomem *) p));
+}
+
+static u32 read_be32(__be32 __iomem *p)
+{
+ return swab32(readl((__force u32 __iomem *) p));
+}
+
+static void print_health_info(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_health *health = &dev->priv.health;
+ struct health_buffer __iomem *h = health->health;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(h->assert_var); i++)
+ pr_info("assert_var[%d] 0x%08x\n", i, read_be32(h->assert_var + i));
+
+ pr_info("assert_exit_ptr 0x%08x\n", read_be32(&h->assert_exit_ptr));
+ pr_info("assert_callra 0x%08x\n", read_be32(&h->assert_callra));
+ pr_info("fw_ver 0x%08x\n", read_be32(&h->fw_ver));
+ pr_info("hw_id 0x%08x\n", read_be32(&h->hw_id));
+ pr_info("irisc_index %d\n", readb(&h->irisc_index));
+ pr_info("synd 0x%x: %s\n", readb(&h->synd), hsynd_str(readb(&h->synd)));
+ pr_info("ext_sync 0x%04x\n", read_be16(&h->ext_sync));
+}
+
+static void poll_health(unsigned long data)
+{
+ struct mlx5_core_dev *dev = (struct mlx5_core_dev *)data;
+ struct mlx5_core_health *health = &dev->priv.health;
+ unsigned long next;
+ u32 count;
+
+ count = ioread32be(health->health_counter);
+ if (count == health->prev)
+ ++health->miss_counter;
+ else
+ health->miss_counter = 0;
+
+ health->prev = count;
+ if (health->miss_counter == MAX_MISSES) {
+ mlx5_core_err(dev, "device's health compromised\n");
+ print_health_info(dev);
+ spin_lock_irq(&health_lock);
+ list_add_tail(&health->list, &health_list);
+ spin_unlock_irq(&health_lock);
+
+ queue_work(mlx5_core_wq, &health_work);
+ } else {
+ get_random_bytes(&next, sizeof(next));
+ next %= HZ;
+ next += jiffies + MLX5_HEALTH_POLL_INTERVAL;
+ mod_timer(&health->timer, next);
+ }
+}
+
+void mlx5_start_health_poll(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_health *health = &dev->priv.health;
+
+ INIT_LIST_HEAD(&health->list);
+ init_timer(&health->timer);
+ health->health = &dev->iseg->health;
+ health->health_counter = &dev->iseg->health_counter;
+
+ health->timer.data = (unsigned long)dev;
+ health->timer.function = poll_health;
+ health->timer.expires = round_jiffies(jiffies + MLX5_HEALTH_POLL_INTERVAL);
+ add_timer(&health->timer);
+}
+
+void mlx5_stop_health_poll(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_health *health = &dev->priv.health;
+
+ del_timer_sync(&health->timer);
+
+ spin_lock_irq(&health_lock);
+ if (!list_empty(&health->list))
+ list_del_init(&health->list);
+ spin_unlock_irq(&health_lock);
+}
+
+void mlx5_health_cleanup(void)
+{
+}
+
+void __init mlx5_health_init(void)
+{
+ INIT_WORK(&health_work, health_care);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c
new file mode 100644
index 0000000..18d6fd5
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mad.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb,
+ u16 opmod, int port)
+{
+ struct mlx5_mad_ifc_mbox_in *in = NULL;
+ struct mlx5_mad_ifc_mbox_out *out = NULL;
+ int err;
+
+ in = kzalloc(sizeof(*in), GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
+ if (!out) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MAD_IFC);
+ in->hdr.opmod = cpu_to_be16(opmod);
+ in->port = port;
+
+ memcpy(in->data, inb, sizeof(in->data));
+
+ err = mlx5_cmd_exec(dev, in, sizeof(*in), out, sizeof(*out));
+ if (err)
+ goto out;
+
+ if (out->hdr.status) {
+ err = mlx5_cmd_status_to_err(&out->hdr);
+ goto out;
+ }
+
+ memcpy(outb, out->data, sizeof(out->data));
+
+out:
+ kfree(out);
+ kfree(in);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_mad_ifc);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
new file mode 100644
index 0000000..12242de
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <asm-generic/kmap_types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/io-mapping.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cq.h>
+#include <linux/mlx5/qp.h>
+#include <linux/mlx5/srq.h>
+#include <linux/debugfs.h>
+#include "mlx5_core.h"
+
+#define DRIVER_NAME "mlx5_core"
+#define DRIVER_VERSION "1.0"
+#define DRIVER_RELDATE "June 2013"
+
+MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox ConnectX-IB HCA core library");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+int mlx5_core_debug_mask;
+module_param_named(debug_mask, mlx5_core_debug_mask, int, 0644);
+MODULE_PARM_DESC(debug_mask, "debug mask: 1 = dump cmd data, 2 = dump cmd exec time, 3 = both. Default=0");
+
+struct workqueue_struct *mlx5_core_wq;
+
+static int set_dma_caps(struct pci_dev *pdev)
+{
+ int err;
+
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (err) {
+ dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n");
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n");
+ return err;
+ }
+ }
+
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (err) {
+ dev_warn(&pdev->dev,
+ "Warning: couldn't set 64-bit consistent PCI DMA mask.\n");
+ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pdev->dev,
+ "Can't set consistent PCI DMA mask, aborting.\n");
+ return err;
+ }
+ }
+
+ dma_set_max_seg_size(&pdev->dev, 2u * 1024 * 1024 * 1024);
+ return err;
+}
+
+static int request_bar(struct pci_dev *pdev)
+{
+ int err = 0;
+
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+ dev_err(&pdev->dev, "Missing registers BAR, aborting.\n");
+ return -ENODEV;
+ }
+
+ err = pci_request_regions(pdev, DRIVER_NAME);
+ if (err)
+ dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n");
+
+ return err;
+}
+
+static void release_bar(struct pci_dev *pdev)
+{
+ pci_release_regions(pdev);
+}
+
+static int mlx5_enable_msix(struct mlx5_core_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ int num_eqs = 1 << dev->caps.log_max_eq;
+ int nvec;
+ int err;
+ int i;
+
+ nvec = dev->caps.num_ports * num_online_cpus() + MLX5_EQ_VEC_COMP_BASE;
+ nvec = min_t(int, nvec, num_eqs);
+ if (nvec <= MLX5_EQ_VEC_COMP_BASE)
+ return -ENOMEM;
+
+ table->msix_arr = kzalloc(nvec * sizeof(*table->msix_arr), GFP_KERNEL);
+ if (!table->msix_arr)
+ return -ENOMEM;
+
+ for (i = 0; i < nvec; i++)
+ table->msix_arr[i].entry = i;
+
+retry:
+ table->num_comp_vectors = nvec - MLX5_EQ_VEC_COMP_BASE;
+ err = pci_enable_msix(dev->pdev, table->msix_arr, nvec);
+ if (err <= 0) {
+ return err;
+ } else if (err > 2) {
+ nvec = err;
+ goto retry;
+ }
+
+ mlx5_core_dbg(dev, "received %d MSI vectors out of %d requested\n", err, nvec);
+
+ return 0;
+}
+
+static void mlx5_disable_msix(struct mlx5_core_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+
+ pci_disable_msix(dev->pdev);
+ kfree(table->msix_arr);
+}
+
+struct mlx5_reg_host_endianess {
+ u8 he;
+ u8 rsvd[15];
+};
+
+static int handle_hca_cap(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd_query_hca_cap_mbox_out *query_out = NULL;
+ struct mlx5_cmd_set_hca_cap_mbox_in *set_ctx = NULL;
+ struct mlx5_cmd_query_hca_cap_mbox_in query_ctx;
+ struct mlx5_cmd_set_hca_cap_mbox_out set_out;
+ struct mlx5_profile *prof = dev->profile;
+ u64 flags;
+ int csum = 1;
+ int err;
+
+ memset(&query_ctx, 0, sizeof(query_ctx));
+ query_out = kzalloc(sizeof(*query_out), GFP_KERNEL);
+ if (!query_out)
+ return -ENOMEM;
+
+ set_ctx = kzalloc(sizeof(*set_ctx), GFP_KERNEL);
+ if (!set_ctx) {
+ err = -ENOMEM;
+ goto query_ex;
+ }
+
+ query_ctx.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_HCA_CAP);
+ query_ctx.hdr.opmod = cpu_to_be16(0x1);
+ err = mlx5_cmd_exec(dev, &query_ctx, sizeof(query_ctx),
+ query_out, sizeof(*query_out));
+ if (err)
+ goto query_ex;
+
+ err = mlx5_cmd_status_to_err(&query_out->hdr);
+ if (err) {
+ mlx5_core_warn(dev, "query hca cap failed, %d\n", err);
+ goto query_ex;
+ }
+
+ memcpy(&set_ctx->hca_cap, &query_out->hca_cap,
+ sizeof(set_ctx->hca_cap));
+
+ if (prof->mask & MLX5_PROF_MASK_CMDIF_CSUM) {
+ csum = !!prof->cmdif_csum;
+ flags = be64_to_cpu(set_ctx->hca_cap.flags);
+ if (csum)
+ flags |= MLX5_DEV_CAP_FLAG_CMDIF_CSUM;
+ else
+ flags &= ~MLX5_DEV_CAP_FLAG_CMDIF_CSUM;
+
+ set_ctx->hca_cap.flags = cpu_to_be64(flags);
+ }
+
+ if (dev->profile->mask & MLX5_PROF_MASK_QP_SIZE)
+ set_ctx->hca_cap.log_max_qp = dev->profile->log_max_qp;
+
+ memset(&set_out, 0, sizeof(set_out));
+ set_ctx->hca_cap.log_uar_page_sz = cpu_to_be16(PAGE_SHIFT - 12);
+ set_ctx->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_SET_HCA_CAP);
+ err = mlx5_cmd_exec(dev, set_ctx, sizeof(*set_ctx),
+ &set_out, sizeof(set_out));
+ if (err) {
+ mlx5_core_warn(dev, "set hca cap failed, %d\n", err);
+ goto query_ex;
+ }
+
+ err = mlx5_cmd_status_to_err(&set_out.hdr);
+ if (err)
+ goto query_ex;
+
+ if (!csum)
+ dev->cmd.checksum_disabled = 1;
+
+query_ex:
+ kfree(query_out);
+ kfree(set_ctx);
+
+ return err;
+}
+
+static int set_hca_ctrl(struct mlx5_core_dev *dev)
+{
+ struct mlx5_reg_host_endianess he_in;
+ struct mlx5_reg_host_endianess he_out;
+ int err;
+
+ memset(&he_in, 0, sizeof(he_in));
+ he_in.he = MLX5_SET_HOST_ENDIANNESS;
+ err = mlx5_core_access_reg(dev, &he_in, sizeof(he_in),
+ &he_out, sizeof(he_out),
+ MLX5_REG_HOST_ENDIANNESS, 0, 1);
+ return err;
+}
+
+int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
+{
+ struct mlx5_priv *priv = &dev->priv;
+ int err;
+
+ dev->pdev = pdev;
+ pci_set_drvdata(dev->pdev, dev);
+ strncpy(priv->name, dev_name(&pdev->dev), MLX5_MAX_NAME_LEN);
+ priv->name[MLX5_MAX_NAME_LEN - 1] = 0;
+
+ mutex_init(&priv->pgdir_mutex);
+ INIT_LIST_HEAD(&priv->pgdir_list);
+ spin_lock_init(&priv->mkey_lock);
+
+ priv->dbg_root = debugfs_create_dir(dev_name(&pdev->dev), mlx5_debugfs_root);
+ if (!priv->dbg_root)
+ return -ENOMEM;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot enable PCI device, aborting.\n");
+ goto err_dbg;
+ }
+
+ err = request_bar(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "error requesting BARs, aborting.\n");
+ goto err_disable;
+ }
+
+ pci_set_master(pdev);
+
+ err = set_dma_caps(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed setting DMA capabilities mask, aborting\n");
+ goto err_clr_master;
+ }
+
+ dev->iseg_base = pci_resource_start(dev->pdev, 0);
+ dev->iseg = ioremap(dev->iseg_base, sizeof(*dev->iseg));
+ if (!dev->iseg) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev, "Failed mapping initialization segment, aborting\n");
+ goto err_clr_master;
+ }
+ dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev),
+ fw_rev_min(dev), fw_rev_sub(dev));
+
+ err = mlx5_cmd_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed initializing command interface, aborting\n");
+ goto err_unmap;
+ }
+
+ mlx5_pagealloc_init(dev);
+ err = set_hca_ctrl(dev);
+ if (err) {
+ dev_err(&pdev->dev, "set_hca_ctrl failed\n");
+ goto err_pagealloc_cleanup;
+ }
+
+ err = handle_hca_cap(dev);
+ if (err) {
+ dev_err(&pdev->dev, "handle_hca_cap failed\n");
+ goto err_pagealloc_cleanup;
+ }
+
+ err = mlx5_satisfy_startup_pages(dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to allocate startup pages\n");
+ goto err_pagealloc_cleanup;
+ }
+
+ err = mlx5_pagealloc_start(dev);
+ if (err) {
+ dev_err(&pdev->dev, "mlx5_pagealloc_start failed\n");
+ goto err_reclaim_pages;
+ }
+
+ err = mlx5_cmd_init_hca(dev);
+ if (err) {
+ dev_err(&pdev->dev, "init hca failed\n");
+ goto err_pagealloc_stop;
+ }
+
+ mlx5_start_health_poll(dev);
+
+ err = mlx5_cmd_query_hca_cap(dev, &dev->caps);
+ if (err) {
+ dev_err(&pdev->dev, "query hca failed\n");
+ goto err_stop_poll;
+ }
+
+ err = mlx5_cmd_query_adapter(dev);
+ if (err) {
+ dev_err(&pdev->dev, "query adapter failed\n");
+ goto err_stop_poll;
+ }
+
+ err = mlx5_enable_msix(dev);
+ if (err) {
+ dev_err(&pdev->dev, "enable msix failed\n");
+ goto err_stop_poll;
+ }
+
+ err = mlx5_eq_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to initialize eq\n");
+ goto disable_msix;
+ }
+
+ err = mlx5_alloc_uuars(dev, &priv->uuari);
+ if (err) {
+ dev_err(&pdev->dev, "Failed allocating uar, aborting\n");
+ goto err_eq_cleanup;
+ }
+
+ err = mlx5_start_eqs(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to start pages and async EQs\n");
+ goto err_free_uar;
+ }
+
+ MLX5_INIT_DOORBELL_LOCK(&priv->cq_uar_lock);
+
+ mlx5_init_cq_table(dev);
+ mlx5_init_qp_table(dev);
+ mlx5_init_srq_table(dev);
+
+ return 0;
+
+err_free_uar:
+ mlx5_free_uuars(dev, &priv->uuari);
+
+err_eq_cleanup:
+ mlx5_eq_cleanup(dev);
+
+disable_msix:
+ mlx5_disable_msix(dev);
+
+err_stop_poll:
+ mlx5_stop_health_poll(dev);
+ mlx5_cmd_teardown_hca(dev);
+
+err_pagealloc_stop:
+ mlx5_pagealloc_stop(dev);
+
+err_reclaim_pages:
+ mlx5_reclaim_startup_pages(dev);
+
+err_pagealloc_cleanup:
+ mlx5_pagealloc_cleanup(dev);
+ mlx5_cmd_cleanup(dev);
+
+err_unmap:
+ iounmap(dev->iseg);
+
+err_clr_master:
+ pci_clear_master(dev->pdev);
+ release_bar(dev->pdev);
+
+err_disable:
+ pci_disable_device(dev->pdev);
+
+err_dbg:
+ debugfs_remove(priv->dbg_root);
+ return err;
+}
+EXPORT_SYMBOL(mlx5_dev_init);
+
+void mlx5_dev_cleanup(struct mlx5_core_dev *dev)
+{
+ struct mlx5_priv *priv = &dev->priv;
+
+ mlx5_cleanup_srq_table(dev);
+ mlx5_cleanup_qp_table(dev);
+ mlx5_cleanup_cq_table(dev);
+ mlx5_stop_eqs(dev);
+ mlx5_free_uuars(dev, &priv->uuari);
+ mlx5_eq_cleanup(dev);
+ mlx5_disable_msix(dev);
+ mlx5_stop_health_poll(dev);
+ mlx5_cmd_teardown_hca(dev);
+ mlx5_pagealloc_stop(dev);
+ mlx5_reclaim_startup_pages(dev);
+ mlx5_pagealloc_cleanup(dev);
+ mlx5_cmd_cleanup(dev);
+ iounmap(dev->iseg);
+ pci_clear_master(dev->pdev);
+ release_bar(dev->pdev);
+ pci_disable_device(dev->pdev);
+ debugfs_remove(priv->dbg_root);
+}
+EXPORT_SYMBOL(mlx5_dev_cleanup);
+
+static int __init init(void)
+{
+ int err;
+
+ mlx5_register_debugfs();
+ mlx5_core_wq = create_singlethread_workqueue("mlx5_core_wq");
+ if (!mlx5_core_wq) {
+ err = -ENOMEM;
+ goto err_debug;
+ }
+ mlx5_health_init();
+
+ return 0;
+
+ mlx5_health_cleanup();
+err_debug:
+ mlx5_unregister_debugfs();
+ return err;
+}
+
+static void __exit cleanup(void)
+{
+ mlx5_health_cleanup();
+ destroy_workqueue(mlx5_core_wq);
+ mlx5_unregister_debugfs();
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c
new file mode 100644
index 0000000..4483764
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include <rdma/ib_verbs.h>
+#include "mlx5_core.h"
+
+struct mlx5_attach_mcg_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 qpn;
+ __be32 rsvd;
+ u8 gid[16];
+};
+
+struct mlx5_attach_mcg_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvf[8];
+};
+
+struct mlx5_detach_mcg_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 qpn;
+ __be32 rsvd;
+ u8 gid[16];
+};
+
+struct mlx5_detach_mcg_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvf[8];
+};
+
+int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn)
+{
+ struct mlx5_attach_mcg_mbox_in in;
+ struct mlx5_attach_mcg_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ATTACH_TO_MCG);
+ memcpy(in.gid, mgid, sizeof(*mgid));
+ in.qpn = cpu_to_be32(qpn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_attach_mcg);
+
+int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn)
+{
+ struct mlx5_detach_mcg_mbox_in in;
+ struct mlx5_detach_mcg_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DETACH_FROM_MCG);
+ memcpy(in.gid, mgid, sizeof(*mgid));
+ in.qpn = cpu_to_be32(qpn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_detach_mcg);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
new file mode 100644
index 0000000..68b74e1
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef __MLX5_CORE_H__
+#define __MLX5_CORE_H__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+extern int mlx5_core_debug_mask;
+
+#define mlx5_core_dbg(dev, format, arg...) \
+pr_debug("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__, \
+ current->pid, ##arg)
+
+#define mlx5_core_dbg_mask(dev, mask, format, arg...) \
+do { \
+ if ((mask) & mlx5_core_debug_mask) \
+ pr_debug("%s:%s:%d:(pid %d): " format, (dev)->priv.name, \
+ __func__, __LINE__, current->pid, ##arg); \
+} while (0)
+
+#define mlx5_core_err(dev, format, arg...) \
+pr_err("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__, \
+ current->pid, ##arg)
+
+#define mlx5_core_warn(dev, format, arg...) \
+pr_warn("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__, \
+ current->pid, ##arg)
+
+enum {
+ MLX5_CMD_DATA, /* print command payload only */
+ MLX5_CMD_TIME, /* print command execution time */
+};
+
+
+int mlx5_cmd_query_hca_cap(struct mlx5_core_dev *dev,
+ struct mlx5_caps *caps);
+int mlx5_cmd_query_adapter(struct mlx5_core_dev *dev);
+int mlx5_cmd_init_hca(struct mlx5_core_dev *dev);
+int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev);
+
+#endif /* __MLX5_CORE_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c
new file mode 100644
index 0000000..5b44e2e
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
+ struct mlx5_create_mkey_mbox_in *in, int inlen)
+{
+ struct mlx5_create_mkey_mbox_out out;
+ int err;
+ u8 key;
+
+ memset(&out, 0, sizeof(out));
+ spin_lock(&dev->priv.mkey_lock);
+ key = dev->priv.mkey_key++;
+ spin_unlock(&dev->priv.mkey_lock);
+ in->seg.qpn_mkey7_0 |= cpu_to_be32(key);
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_MKEY);
+ err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ if (err) {
+ mlx5_core_dbg(dev, "cmd exec faile %d\n", err);
+ return err;
+ }
+
+ if (out.hdr.status) {
+ mlx5_core_dbg(dev, "status %d\n", out.hdr.status);
+ return mlx5_cmd_status_to_err(&out.hdr);
+ }
+
+ mr->key = mlx5_idx_to_mkey(be32_to_cpu(out.mkey) & 0xffffff) | key;
+ mlx5_core_dbg(dev, "out 0x%x, key 0x%x, mkey 0x%x\n", be32_to_cpu(out.mkey), key, mr->key);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_create_mkey);
+
+int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr)
+{
+ struct mlx5_destroy_mkey_mbox_in in;
+ struct mlx5_destroy_mkey_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_MKEY);
+ in.mkey = cpu_to_be32(mlx5_mkey_to_idx(mr->key));
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_destroy_mkey);
+
+int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
+ struct mlx5_query_mkey_mbox_out *out, int outlen)
+{
+ struct mlx5_destroy_mkey_mbox_in in;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(out, 0, outlen);
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_MKEY);
+ in.mkey = cpu_to_be32(mlx5_mkey_to_idx(mr->key));
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
+ if (err)
+ return err;
+
+ if (out->hdr.status)
+ return mlx5_cmd_status_to_err(&out->hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_query_mkey);
+
+int mlx5_core_dump_fill_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
+ u32 *mkey)
+{
+ struct mlx5_query_special_ctxs_mbox_in in;
+ struct mlx5_query_special_ctxs_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ *mkey = be32_to_cpu(out.dump_fill_mkey);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_dump_fill_mkey);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
new file mode 100644
index 0000000..f0bf463
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <asm-generic/kmap_types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+enum {
+ MLX5_PAGES_CANT_GIVE = 0,
+ MLX5_PAGES_GIVE = 1,
+ MLX5_PAGES_TAKE = 2
+};
+
+struct mlx5_pages_req {
+ struct mlx5_core_dev *dev;
+ u32 func_id;
+ s16 npages;
+ struct work_struct work;
+};
+
+struct fw_page {
+ struct rb_node rb_node;
+ u64 addr;
+ struct page *page;
+ u16 func_id;
+};
+
+struct mlx5_query_pages_inbox {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_query_pages_outbox {
+ struct mlx5_outbox_hdr hdr;
+ u8 reserved[2];
+ __be16 func_id;
+ __be16 init_pages;
+ __be16 num_pages;
+};
+
+struct mlx5_manage_pages_inbox {
+ struct mlx5_inbox_hdr hdr;
+ __be16 rsvd0;
+ __be16 func_id;
+ __be16 rsvd1;
+ __be16 num_entries;
+ u8 rsvd2[16];
+ __be64 pas[0];
+};
+
+struct mlx5_manage_pages_outbox {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[2];
+ __be16 num_entries;
+ u8 rsvd1[20];
+ __be64 pas[0];
+};
+
+static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u16 func_id)
+{
+ struct rb_root *root = &dev->priv.page_root;
+ struct rb_node **new = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct fw_page *nfp;
+ struct fw_page *tfp;
+
+ while (*new) {
+ parent = *new;
+ tfp = rb_entry(parent, struct fw_page, rb_node);
+ if (tfp->addr < addr)
+ new = &parent->rb_left;
+ else if (tfp->addr > addr)
+ new = &parent->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ nfp = kmalloc(sizeof(*nfp), GFP_KERNEL);
+ if (!nfp)
+ return -ENOMEM;
+
+ nfp->addr = addr;
+ nfp->page = page;
+ nfp->func_id = func_id;
+
+ rb_link_node(&nfp->rb_node, parent, new);
+ rb_insert_color(&nfp->rb_node, root);
+
+ return 0;
+}
+
+static struct page *remove_page(struct mlx5_core_dev *dev, u64 addr)
+{
+ struct rb_root *root = &dev->priv.page_root;
+ struct rb_node *tmp = root->rb_node;
+ struct page *result = NULL;
+ struct fw_page *tfp;
+
+ while (tmp) {
+ tfp = rb_entry(tmp, struct fw_page, rb_node);
+ if (tfp->addr < addr) {
+ tmp = tmp->rb_left;
+ } else if (tfp->addr > addr) {
+ tmp = tmp->rb_right;
+ } else {
+ rb_erase(&tfp->rb_node, root);
+ result = tfp->page;
+ kfree(tfp);
+ break;
+ }
+ }
+
+ return result;
+}
+
+static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id,
+ s16 *pages, s16 *init_pages)
+{
+ struct mlx5_query_pages_inbox in;
+ struct mlx5_query_pages_outbox out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_PAGES);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ if (pages)
+ *pages = be16_to_cpu(out.num_pages);
+ if (init_pages)
+ *init_pages = be16_to_cpu(out.init_pages);
+ *func_id = be16_to_cpu(out.func_id);
+
+ return err;
+}
+
+static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
+ int notify_fail)
+{
+ struct mlx5_manage_pages_inbox *in;
+ struct mlx5_manage_pages_outbox out;
+ struct page *page;
+ int inlen;
+ u64 addr;
+ int err;
+ int i;
+
+ inlen = sizeof(*in) + npages * sizeof(in->pas[0]);
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_warn(dev, "vzalloc failed %d\n", inlen);
+ return -ENOMEM;
+ }
+ memset(&out, 0, sizeof(out));
+
+ for (i = 0; i < npages; i++) {
+ page = alloc_page(GFP_HIGHUSER);
+ if (!page) {
+ err = -ENOMEM;
+ mlx5_core_warn(dev, "failed to allocate page\n");
+ goto out_alloc;
+ }
+ addr = dma_map_page(&dev->pdev->dev, page, 0,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(&dev->pdev->dev, addr)) {
+ mlx5_core_warn(dev, "failed dma mapping page\n");
+ __free_page(page);
+ err = -ENOMEM;
+ goto out_alloc;
+ }
+ err = insert_page(dev, addr, page, func_id);
+ if (err) {
+ mlx5_core_err(dev, "failed to track allocated page\n");
+ dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ __free_page(page);
+ err = -ENOMEM;
+ goto out_alloc;
+ }
+ in->pas[i] = cpu_to_be64(addr);
+ }
+
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
+ in->hdr.opmod = cpu_to_be16(MLX5_PAGES_GIVE);
+ in->func_id = cpu_to_be16(func_id);
+ in->num_entries = cpu_to_be16(npages);
+ err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ mlx5_core_dbg(dev, "err %d\n", err);
+ if (err) {
+ mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err);
+ goto out_alloc;
+ }
+ dev->priv.fw_pages += npages;
+
+ if (out.hdr.status) {
+ err = mlx5_cmd_status_to_err(&out.hdr);
+ if (err) {
+ mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", func_id, npages, out.hdr.status);
+ goto out_alloc;
+ }
+ }
+
+ mlx5_core_dbg(dev, "err %d\n", err);
+
+ goto out_free;
+
+out_alloc:
+ if (notify_fail) {
+ memset(in, 0, inlen);
+ memset(&out, 0, sizeof(out));
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
+ in->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE);
+ if (mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out)))
+ mlx5_core_warn(dev, "\n");
+ }
+ for (i--; i >= 0; i--) {
+ addr = be64_to_cpu(in->pas[i]);
+ page = remove_page(dev, addr);
+ if (!page) {
+ mlx5_core_err(dev, "BUG: can't remove page at addr 0x%llx\n",
+ addr);
+ continue;
+ }
+ dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ __free_page(page);
+ }
+
+out_free:
+ mlx5_vfree(in);
+ return err;
+}
+
+static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
+ int *nclaimed)
+{
+ struct mlx5_manage_pages_inbox in;
+ struct mlx5_manage_pages_outbox *out;
+ struct page *page;
+ int num_claimed;
+ int outlen;
+ u64 addr;
+ int err;
+ int i;
+
+ memset(&in, 0, sizeof(in));
+ outlen = sizeof(*out) + npages * sizeof(out->pas[0]);
+ out = mlx5_vzalloc(outlen);
+ if (!out)
+ return -ENOMEM;
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES);
+ in.hdr.opmod = cpu_to_be16(MLX5_PAGES_TAKE);
+ in.func_id = cpu_to_be16(func_id);
+ in.num_entries = cpu_to_be16(npages);
+ mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
+ if (err) {
+ mlx5_core_err(dev, "failed recliaming pages\n");
+ goto out_free;
+ }
+ dev->priv.fw_pages -= npages;
+
+ if (out->hdr.status) {
+ err = mlx5_cmd_status_to_err(&out->hdr);
+ goto out_free;
+ }
+
+ num_claimed = be16_to_cpu(out->num_entries);
+ if (nclaimed)
+ *nclaimed = num_claimed;
+
+ for (i = 0; i < num_claimed; i++) {
+ addr = be64_to_cpu(out->pas[i]);
+ page = remove_page(dev, addr);
+ if (!page) {
+ mlx5_core_warn(dev, "FW reported unknown DMA address 0x%llx\n", addr);
+ } else {
+ dma_unmap_page(&dev->pdev->dev, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ __free_page(page);
+ }
+ }
+
+out_free:
+ mlx5_vfree(out);
+ return err;
+}
+
+static void pages_work_handler(struct work_struct *work)
+{
+ struct mlx5_pages_req *req = container_of(work, struct mlx5_pages_req, work);
+ struct mlx5_core_dev *dev = req->dev;
+ int err = 0;
+
+ if (req->npages < 0)
+ err = reclaim_pages(dev, req->func_id, -1 * req->npages, NULL);
+ else if (req->npages > 0)
+ err = give_pages(dev, req->func_id, req->npages, 1);
+
+ if (err)
+ mlx5_core_warn(dev, "%s fail %d\n", req->npages < 0 ?
+ "reclaim" : "give", err);
+
+ kfree(req);
+}
+
+void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
+ s16 npages)
+{
+ struct mlx5_pages_req *req;
+
+ req = kzalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ mlx5_core_warn(dev, "failed to allocate pages request\n");
+ return;
+ }
+
+ req->dev = dev;
+ req->func_id = func_id;
+ req->npages = npages;
+ INIT_WORK(&req->work, pages_work_handler);
+ queue_work(dev->priv.pg_wq, &req->work);
+}
+
+int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev)
+{
+ s16 uninitialized_var(init_pages);
+ u16 uninitialized_var(func_id);
+ int err;
+
+ err = mlx5_cmd_query_pages(dev, &func_id, NULL, &init_pages);
+ if (err)
+ return err;
+
+ mlx5_core_dbg(dev, "requested %d init pages for func_id 0x%x\n", init_pages, func_id);
+
+ return give_pages(dev, func_id, init_pages, 0);
+}
+
+static int optimal_reclaimed_pages(void)
+{
+ struct mlx5_cmd_prot_block *block;
+ struct mlx5_cmd_layout *lay;
+ int ret;
+
+ ret = (sizeof(lay->in) + sizeof(block->data) -
+ sizeof(struct mlx5_manage_pages_outbox)) / 8;
+
+ return ret;
+}
+
+int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(5000);
+ struct fw_page *fwp;
+ struct rb_node *p;
+ int err;
+
+ do {
+ p = rb_first(&dev->priv.page_root);
+ if (p) {
+ fwp = rb_entry(p, struct fw_page, rb_node);
+ err = reclaim_pages(dev, fwp->func_id, optimal_reclaimed_pages(), NULL);
+ if (err) {
+ mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", err);
+ return err;
+ }
+ }
+ if (time_after(jiffies, end)) {
+ mlx5_core_warn(dev, "FW did not return all pages. giving up...\n");
+ break;
+ }
+ } while (p);
+
+ return 0;
+}
+
+void mlx5_pagealloc_init(struct mlx5_core_dev *dev)
+{
+ dev->priv.page_root = RB_ROOT;
+}
+
+void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev)
+{
+ /* nothing */
+}
+
+int mlx5_pagealloc_start(struct mlx5_core_dev *dev)
+{
+ dev->priv.pg_wq = create_singlethread_workqueue("mlx5_page_allocator");
+ if (!dev->priv.pg_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void mlx5_pagealloc_stop(struct mlx5_core_dev *dev)
+{
+ destroy_workqueue(dev->priv.pg_wq);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pd.c b/drivers/net/ethernet/mellanox/mlx5/core/pd.c
new file mode 100644
index 0000000..790da5c
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pd.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+struct mlx5_alloc_pd_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_alloc_pd_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 pdn;
+ u8 rsvd[4];
+};
+
+struct mlx5_dealloc_pd_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 pdn;
+ u8 rsvd[4];
+};
+
+struct mlx5_dealloc_pd_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn)
+{
+ struct mlx5_alloc_pd_mbox_in in;
+ struct mlx5_alloc_pd_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ALLOC_PD);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ *pdn = be32_to_cpu(out.pdn) & 0xffffff;
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_alloc_pd);
+
+int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn)
+{
+ struct mlx5_dealloc_pd_mbox_in in;
+ struct mlx5_dealloc_pd_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DEALLOC_PD);
+ in.pdn = cpu_to_be32(pdn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_dealloc_pd);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c
new file mode 100644
index 0000000..f6afe7b
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in,
+ int size_in, void *data_out, int size_out,
+ u16 reg_num, int arg, int write)
+{
+ struct mlx5_access_reg_mbox_in *in = NULL;
+ struct mlx5_access_reg_mbox_out *out = NULL;
+ int err = -ENOMEM;
+
+ in = mlx5_vzalloc(sizeof(*in) + size_in);
+ if (!in)
+ return -ENOMEM;
+
+ out = mlx5_vzalloc(sizeof(*out) + size_out);
+ if (!out)
+ goto ex1;
+
+ memcpy(in->data, data_in, size_in);
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ACCESS_REG);
+ in->hdr.opmod = cpu_to_be16(!write);
+ in->arg = cpu_to_be32(arg);
+ in->register_id = cpu_to_be16(reg_num);
+ err = mlx5_cmd_exec(dev, in, sizeof(*in) + size_in, out,
+ sizeof(out) + size_out);
+ if (err)
+ goto ex2;
+
+ if (out->hdr.status)
+ err = mlx5_cmd_status_to_err(&out->hdr);
+
+ if (!err)
+ memcpy(data_out, out->data, size_out);
+
+ex2:
+ mlx5_vfree(out);
+ex1:
+ mlx5_vfree(in);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_access_reg);
+
+
+struct mlx5_reg_pcap {
+ u8 rsvd0;
+ u8 port_num;
+ u8 rsvd1[2];
+ __be32 caps_127_96;
+ __be32 caps_95_64;
+ __be32 caps_63_32;
+ __be32 caps_31_0;
+};
+
+int mlx5_set_port_caps(struct mlx5_core_dev *dev, int port_num, u32 caps)
+{
+ struct mlx5_reg_pcap in;
+ struct mlx5_reg_pcap out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ in.caps_127_96 = cpu_to_be32(caps);
+ in.port_num = port_num;
+
+ err = mlx5_core_access_reg(dev, &in, sizeof(in), &out,
+ sizeof(out), MLX5_REG_PCAP, 0, 1);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_set_port_caps);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
new file mode 100644
index 0000000..54faf8b
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+
+#include <linux/gfp.h>
+#include <linux/export.h>
+#include <linux/mlx5/cmd.h>
+#include <linux/mlx5/qp.h>
+#include <linux/mlx5/driver.h>
+
+#include "mlx5_core.h"
+
+void mlx5_qp_event(struct mlx5_core_dev *dev, u32 qpn, int event_type)
+{
+ struct mlx5_qp_table *table = &dev->priv.qp_table;
+ struct mlx5_core_qp *qp;
+
+ spin_lock(&table->lock);
+
+ qp = radix_tree_lookup(&table->tree, qpn);
+ if (qp)
+ atomic_inc(&qp->refcount);
+
+ spin_unlock(&table->lock);
+
+ if (!qp) {
+ mlx5_core_warn(dev, "Async event for bogus QP 0x%x\n", qpn);
+ return;
+ }
+
+ qp->event(qp, event_type);
+
+ if (atomic_dec_and_test(&qp->refcount))
+ complete(&qp->free);
+}
+
+int mlx5_core_create_qp(struct mlx5_core_dev *dev,
+ struct mlx5_core_qp *qp,
+ struct mlx5_create_qp_mbox_in *in,
+ int inlen)
+{
+ struct mlx5_qp_table *table = &dev->priv.qp_table;
+ struct mlx5_create_qp_mbox_out out;
+ struct mlx5_destroy_qp_mbox_in din;
+ struct mlx5_destroy_qp_mbox_out dout;
+ int err;
+
+ memset(&dout, 0, sizeof(dout));
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_QP);
+
+ err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ if (err) {
+ mlx5_core_warn(dev, "ret %d", err);
+ return err;
+ }
+
+ if (out.hdr.status) {
+ pr_warn("current num of QPs 0x%x\n", atomic_read(&dev->num_qps));
+ return mlx5_cmd_status_to_err(&out.hdr);
+ }
+
+ qp->qpn = be32_to_cpu(out.qpn) & 0xffffff;
+ mlx5_core_dbg(dev, "qpn = 0x%x\n", qp->qpn);
+
+ spin_lock_irq(&table->lock);
+ err = radix_tree_insert(&table->tree, qp->qpn, qp);
+ spin_unlock_irq(&table->lock);
+ if (err) {
+ mlx5_core_warn(dev, "err %d", err);
+ goto err_cmd;
+ }
+
+ err = mlx5_debug_qp_add(dev, qp);
+ if (err)
+ mlx5_core_dbg(dev, "failed adding QP 0x%x to debug file system\n",
+ qp->qpn);
+
+ qp->pid = current->pid;
+ atomic_set(&qp->refcount, 1);
+ atomic_inc(&dev->num_qps);
+ init_completion(&qp->free);
+
+ return 0;
+
+err_cmd:
+ memset(&din, 0, sizeof(din));
+ memset(&dout, 0, sizeof(dout));
+ din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_QP);
+ din.qpn = cpu_to_be32(qp->qpn);
+ mlx5_cmd_exec(dev, &din, sizeof(din), &out, sizeof(dout));
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_create_qp);
+
+int mlx5_core_destroy_qp(struct mlx5_core_dev *dev,
+ struct mlx5_core_qp *qp)
+{
+ struct mlx5_destroy_qp_mbox_in in;
+ struct mlx5_destroy_qp_mbox_out out;
+ struct mlx5_qp_table *table = &dev->priv.qp_table;
+ unsigned long flags;
+ int err;
+
+ mlx5_debug_qp_remove(dev, qp);
+
+ spin_lock_irqsave(&table->lock, flags);
+ radix_tree_delete(&table->tree, qp->qpn);
+ spin_unlock_irqrestore(&table->lock, flags);
+
+ if (atomic_dec_and_test(&qp->refcount))
+ complete(&qp->free);
+ wait_for_completion(&qp->free);
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_QP);
+ in.qpn = cpu_to_be32(qp->qpn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ atomic_dec(&dev->num_qps);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_destroy_qp);
+
+int mlx5_core_qp_modify(struct mlx5_core_dev *dev, enum mlx5_qp_state cur_state,
+ enum mlx5_qp_state new_state,
+ struct mlx5_modify_qp_mbox_in *in, int sqd_event,
+ struct mlx5_core_qp *qp)
+{
+ static const u16 optab[MLX5_QP_NUM_STATE][MLX5_QP_NUM_STATE] = {
+ [MLX5_QP_STATE_RST] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ [MLX5_QP_STATE_INIT] = MLX5_CMD_OP_RST2INIT_QP,
+ },
+ [MLX5_QP_STATE_INIT] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ [MLX5_QP_STATE_INIT] = MLX5_CMD_OP_INIT2INIT_QP,
+ [MLX5_QP_STATE_RTR] = MLX5_CMD_OP_INIT2RTR_QP,
+ },
+ [MLX5_QP_STATE_RTR] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ [MLX5_QP_STATE_RTS] = MLX5_CMD_OP_RTR2RTS_QP,
+ },
+ [MLX5_QP_STATE_RTS] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ [MLX5_QP_STATE_RTS] = MLX5_CMD_OP_RTS2RTS_QP,
+ [MLX5_QP_STATE_SQD] = MLX5_CMD_OP_RTS2SQD_QP,
+ },
+ [MLX5_QP_STATE_SQD] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ [MLX5_QP_STATE_RTS] = MLX5_CMD_OP_SQD2RTS_QP,
+ [MLX5_QP_STATE_SQD] = MLX5_CMD_OP_SQD2SQD_QP,
+ },
+ [MLX5_QP_STATE_SQER] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ [MLX5_QP_STATE_RTS] = MLX5_CMD_OP_SQERR2RTS_QP,
+ },
+ [MLX5_QP_STATE_ERR] = {
+ [MLX5_QP_STATE_RST] = MLX5_CMD_OP_2RST_QP,
+ [MLX5_QP_STATE_ERR] = MLX5_CMD_OP_2ERR_QP,
+ }
+ };
+
+ struct mlx5_modify_qp_mbox_out out;
+ int err = 0;
+ u16 op;
+
+ if (cur_state >= MLX5_QP_NUM_STATE || new_state >= MLX5_QP_NUM_STATE ||
+ !optab[cur_state][new_state])
+ return -EINVAL;
+
+ memset(&out, 0, sizeof(out));
+ op = optab[cur_state][new_state];
+ in->hdr.opcode = cpu_to_be16(op);
+ in->qpn = cpu_to_be32(qp->qpn);
+ err = mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ return mlx5_cmd_status_to_err(&out.hdr);
+}
+EXPORT_SYMBOL_GPL(mlx5_core_qp_modify);
+
+void mlx5_init_qp_table(struct mlx5_core_dev *dev)
+{
+ struct mlx5_qp_table *table = &dev->priv.qp_table;
+
+ spin_lock_init(&table->lock);
+ INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
+ mlx5_qp_debugfs_init(dev);
+}
+
+void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev)
+{
+ mlx5_qp_debugfs_cleanup(dev);
+}
+
+int mlx5_core_qp_query(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
+ struct mlx5_query_qp_mbox_out *out, int outlen)
+{
+ struct mlx5_query_qp_mbox_in in;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(out, 0, outlen);
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_QP);
+ in.qpn = cpu_to_be32(qp->qpn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
+ if (err)
+ return err;
+
+ if (out->hdr.status)
+ return mlx5_cmd_status_to_err(&out->hdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_qp_query);
+
+int mlx5_core_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn)
+{
+ struct mlx5_alloc_xrcd_mbox_in in;
+ struct mlx5_alloc_xrcd_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ALLOC_XRCD);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+ else
+ *xrcdn = be32_to_cpu(out.xrcdn);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_xrcd_alloc);
+
+int mlx5_core_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn)
+{
+ struct mlx5_dealloc_xrcd_mbox_in in;
+ struct mlx5_dealloc_xrcd_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DEALLOC_XRCD);
+ in.xrcdn = cpu_to_be32(xrcdn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_core_xrcd_dealloc);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c
new file mode 100644
index 0000000..38bce93
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/srq.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include <linux/mlx5/srq.h>
+#include <rdma/ib_verbs.h>
+#include "mlx5_core.h"
+
+void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type)
+{
+ struct mlx5_srq_table *table = &dev->priv.srq_table;
+ struct mlx5_core_srq *srq;
+
+ spin_lock(&table->lock);
+
+ srq = radix_tree_lookup(&table->tree, srqn);
+ if (srq)
+ atomic_inc(&srq->refcount);
+
+ spin_unlock(&table->lock);
+
+ if (!srq) {
+ mlx5_core_warn(dev, "Async event for bogus SRQ 0x%08x\n", srqn);
+ return;
+ }
+
+ srq->event(srq, event_type);
+
+ if (atomic_dec_and_test(&srq->refcount))
+ complete(&srq->free);
+}
+
+struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn)
+{
+ struct mlx5_srq_table *table = &dev->priv.srq_table;
+ struct mlx5_core_srq *srq;
+
+ spin_lock(&table->lock);
+
+ srq = radix_tree_lookup(&table->tree, srqn);
+ if (srq)
+ atomic_inc(&srq->refcount);
+
+ spin_unlock(&table->lock);
+
+ return srq;
+}
+EXPORT_SYMBOL(mlx5_core_get_srq);
+
+int mlx5_core_create_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
+ struct mlx5_create_srq_mbox_in *in, int inlen)
+{
+ struct mlx5_create_srq_mbox_out out;
+ struct mlx5_srq_table *table = &dev->priv.srq_table;
+ struct mlx5_destroy_srq_mbox_in din;
+ struct mlx5_destroy_srq_mbox_out dout;
+ int err;
+
+ memset(&out, 0, sizeof(out));
+ in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_SRQ);
+ err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ srq->srqn = be32_to_cpu(out.srqn) & 0xffffff;
+
+ atomic_set(&srq->refcount, 1);
+ init_completion(&srq->free);
+
+ spin_lock_irq(&table->lock);
+ err = radix_tree_insert(&table->tree, srq->srqn, srq);
+ spin_unlock_irq(&table->lock);
+ if (err) {
+ mlx5_core_warn(dev, "err %d, srqn 0x%x\n", err, srq->srqn);
+ goto err_cmd;
+ }
+
+ return 0;
+
+err_cmd:
+ memset(&din, 0, sizeof(din));
+ memset(&dout, 0, sizeof(dout));
+ din.srqn = cpu_to_be32(srq->srqn);
+ din.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_SRQ);
+ mlx5_cmd_exec(dev, &din, sizeof(din), &dout, sizeof(dout));
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_create_srq);
+
+int mlx5_core_destroy_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq)
+{
+ struct mlx5_destroy_srq_mbox_in in;
+ struct mlx5_destroy_srq_mbox_out out;
+ struct mlx5_srq_table *table = &dev->priv.srq_table;
+ struct mlx5_core_srq *tmp;
+ int err;
+
+ spin_lock_irq(&table->lock);
+ tmp = radix_tree_delete(&table->tree, srq->srqn);
+ spin_unlock_irq(&table->lock);
+ if (!tmp) {
+ mlx5_core_warn(dev, "srq 0x%x not found in tree\n", srq->srqn);
+ return -EINVAL;
+ }
+ if (tmp != srq) {
+ mlx5_core_warn(dev, "corruption on srqn 0x%x\n", srq->srqn);
+ return -EINVAL;
+ }
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DESTROY_SRQ);
+ in.srqn = cpu_to_be32(srq->srqn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ if (atomic_dec_and_test(&srq->refcount))
+ complete(&srq->free);
+ wait_for_completion(&srq->free);
+
+ return 0;
+}
+EXPORT_SYMBOL(mlx5_core_destroy_srq);
+
+int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
+ struct mlx5_query_srq_mbox_out *out)
+{
+ struct mlx5_query_srq_mbox_in in;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(out, 0, sizeof(*out));
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_QUERY_SRQ);
+ in.srqn = cpu_to_be32(srq->srqn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), out, sizeof(*out));
+ if (err)
+ return err;
+
+ if (out->hdr.status)
+ return mlx5_cmd_status_to_err(&out->hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_query_srq);
+
+int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
+ u16 lwm, int is_srq)
+{
+ struct mlx5_arm_srq_mbox_in in;
+ struct mlx5_arm_srq_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ARM_RQ);
+ in.hdr.opmod = cpu_to_be16(!!is_srq);
+ in.srqn = cpu_to_be32(srq->srqn);
+ in.lwm = cpu_to_be16(lwm);
+
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return mlx5_cmd_status_to_err(&out.hdr);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_core_arm_srq);
+
+void mlx5_init_srq_table(struct mlx5_core_dev *dev)
+{
+ struct mlx5_srq_table *table = &dev->priv.srq_table;
+
+ spin_lock_init(&table->lock);
+ INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
+}
+
+void mlx5_cleanup_srq_table(struct mlx5_core_dev *dev)
+{
+ /* nothing */
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c
new file mode 100644
index 0000000..71d4a39
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/cmd.h>
+#include "mlx5_core.h"
+
+enum {
+ NUM_DRIVER_UARS = 4,
+ NUM_LOW_LAT_UUARS = 4,
+};
+
+
+struct mlx5_alloc_uar_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_alloc_uar_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 uarn;
+ u8 rsvd[4];
+};
+
+struct mlx5_free_uar_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 uarn;
+ u8 rsvd[4];
+};
+
+struct mlx5_free_uar_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+int mlx5_cmd_alloc_uar(struct mlx5_core_dev *dev, u32 *uarn)
+{
+ struct mlx5_alloc_uar_mbox_in in;
+ struct mlx5_alloc_uar_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ALLOC_UAR);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ goto ex;
+
+ if (out.hdr.status) {
+ err = mlx5_cmd_status_to_err(&out.hdr);
+ goto ex;
+ }
+
+ *uarn = be32_to_cpu(out.uarn) & 0xffffff;
+
+ex:
+ return err;
+}
+EXPORT_SYMBOL(mlx5_cmd_alloc_uar);
+
+int mlx5_cmd_free_uar(struct mlx5_core_dev *dev, u32 uarn)
+{
+ struct mlx5_free_uar_mbox_in in;
+ struct mlx5_free_uar_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DEALLOC_UAR);
+ in.uarn = cpu_to_be32(uarn);
+ err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ goto ex;
+
+ if (out.hdr.status)
+ err = mlx5_cmd_status_to_err(&out.hdr);
+
+ex:
+ return err;
+}
+EXPORT_SYMBOL(mlx5_cmd_free_uar);
+
+static int need_uuar_lock(int uuarn)
+{
+ int tot_uuars = NUM_DRIVER_UARS * MLX5_BF_REGS_PER_PAGE;
+
+ if (uuarn == 0 || tot_uuars - NUM_LOW_LAT_UUARS)
+ return 0;
+
+ return 1;
+}
+
+int mlx5_alloc_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari)
+{
+ int tot_uuars = NUM_DRIVER_UARS * MLX5_BF_REGS_PER_PAGE;
+ struct mlx5_bf *bf;
+ phys_addr_t addr;
+ int err;
+ int i;
+
+ uuari->num_uars = NUM_DRIVER_UARS;
+ uuari->num_low_latency_uuars = NUM_LOW_LAT_UUARS;
+
+ mutex_init(&uuari->lock);
+ uuari->uars = kcalloc(uuari->num_uars, sizeof(*uuari->uars), GFP_KERNEL);
+ if (!uuari->uars)
+ return -ENOMEM;
+
+ uuari->bfs = kcalloc(tot_uuars, sizeof(*uuari->bfs), GFP_KERNEL);
+ if (!uuari->bfs) {
+ err = -ENOMEM;
+ goto out_uars;
+ }
+
+ uuari->bitmap = kcalloc(BITS_TO_LONGS(tot_uuars), sizeof(*uuari->bitmap),
+ GFP_KERNEL);
+ if (!uuari->bitmap) {
+ err = -ENOMEM;
+ goto out_bfs;
+ }
+
+ uuari->count = kcalloc(tot_uuars, sizeof(*uuari->count), GFP_KERNEL);
+ if (!uuari->count) {
+ err = -ENOMEM;
+ goto out_bitmap;
+ }
+
+ for (i = 0; i < uuari->num_uars; i++) {
+ err = mlx5_cmd_alloc_uar(dev, &uuari->uars[i].index);
+ if (err)
+ goto out_count;
+
+ addr = dev->iseg_base + ((phys_addr_t)(uuari->uars[i].index) << PAGE_SHIFT);
+ uuari->uars[i].map = ioremap(addr, PAGE_SIZE);
+ if (!uuari->uars[i].map) {
+ mlx5_cmd_free_uar(dev, uuari->uars[i].index);
+ goto out_count;
+ }
+ mlx5_core_dbg(dev, "allocated uar index 0x%x, mmaped at %p\n",
+ uuari->uars[i].index, uuari->uars[i].map);
+ }
+
+ for (i = 0; i < tot_uuars; i++) {
+ bf = &uuari->bfs[i];
+
+ bf->buf_size = dev->caps.bf_reg_size / 2;
+ bf->uar = &uuari->uars[i / MLX5_BF_REGS_PER_PAGE];
+ bf->regreg = uuari->uars[i / MLX5_BF_REGS_PER_PAGE].map;
+ bf->reg = NULL; /* Add WC support */
+ bf->offset = (i % MLX5_BF_REGS_PER_PAGE) * dev->caps.bf_reg_size +
+ MLX5_BF_OFFSET;
+ bf->need_lock = need_uuar_lock(i);
+ spin_lock_init(&bf->lock);
+ spin_lock_init(&bf->lock32);
+ bf->uuarn = i;
+ }
+
+ return 0;
+
+out_count:
+ for (i--; i >= 0; i--) {
+ iounmap(uuari->uars[i].map);
+ mlx5_cmd_free_uar(dev, uuari->uars[i].index);
+ }
+ kfree(uuari->count);
+
+out_bitmap:
+ kfree(uuari->bitmap);
+
+out_bfs:
+ kfree(uuari->bfs);
+
+out_uars:
+ kfree(uuari->uars);
+ return err;
+}
+
+int mlx5_free_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari)
+{
+ int i = uuari->num_uars;
+
+ for (i--; i >= 0; i--) {
+ iounmap(uuari->uars[i].map);
+ mlx5_cmd_free_uar(dev, uuari->uars[i].index);
+ }
+
+ kfree(uuari->count);
+ kfree(uuari->bitmap);
+ kfree(uuari->bfs);
+ kfree(uuari->uars);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/octeon/Kconfig b/drivers/net/ethernet/octeon/Kconfig
index 3de52ff..a7aa280 100644
--- a/drivers/net/ethernet/octeon/Kconfig
+++ b/drivers/net/ethernet/octeon/Kconfig
@@ -4,7 +4,7 @@
config OCTEON_MGMT_ETHERNET
tristate "Octeon Management port ethernet driver (CN5XXX, CN6XXX)"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
select PHYLIB
select MDIO_OCTEON
default y
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index 1df0ff3..3df5684 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -1239,6 +1239,8 @@
dev_set_drvdata(&vdev->dev, NULL);
kfree(port);
+
+ unregister_netdev(vp->dev);
}
return 0;
}
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 3a316b3..342561a 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -135,7 +135,7 @@
config MDIO_OCTEON
tristate "Support for MDIO buses on Octeon SOCs"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
default y
help
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 42d670a..3d2a90a 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -902,7 +902,6 @@
struct scatterlist sg;
struct virtio_net_ctrl_mq s;
struct net_device *dev = vi->dev;
- int i;
if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ))
return 0;
@@ -916,10 +915,8 @@
queue_pairs);
return -EINVAL;
} else {
- for (i = vi->curr_queue_pairs; i < queue_pairs; i++)
- if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
- schedule_delayed_work(&vi->refill, 0);
vi->curr_queue_pairs = queue_pairs;
+ schedule_delayed_work(&vi->refill, 0);
}
return 0;
diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c
index 00a71eb..9f7fe21 100644
--- a/drivers/rapidio/switches/idt_gen2.c
+++ b/drivers/rapidio/switches/idt_gen2.c
@@ -16,6 +16,8 @@
#include <linux/rio_drv.h>
#include <linux/rio_ids.h>
#include <linux/delay.h>
+
+#include <asm/page.h>
#include "../rio.h"
#define LOCAL_RTE_CONF_DESTID_SEL 0x010070
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 022dc63..3cd85a6 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -762,13 +762,6 @@
kfree(entry);
}
- /* clean up carveout allocations */
- list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
- dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma);
- list_del(&entry->node);
- kfree(entry);
- }
-
/* clean up iommu mapping entries */
list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) {
size_t unmapped;
@@ -783,6 +776,13 @@
list_del(&entry->node);
kfree(entry);
}
+
+ /* clean up carveout allocations */
+ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
+ dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma);
+ list_del(&entry->node);
+ kfree(entry);
+ }
}
/*
@@ -815,18 +815,17 @@
}
rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
+ ret = -EINVAL;
/* look for the resource table */
table = rproc_find_rsc_table(rproc, fw, &tablesz);
if (!table) {
- ret = -EINVAL;
goto clean_up;
}
/* Verify that resource table in loaded fw is unchanged */
if (rproc->table_csum != crc32(0, table, tablesz)) {
dev_err(dev, "resource checksum failed, fw changed?\n");
- ret = -EINVAL;
goto clean_up;
}
@@ -852,8 +851,10 @@
* copy this information to device memory.
*/
loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
- if (!loaded_table)
+ if (!loaded_table) {
+ ret = -EINVAL;
goto clean_up;
+ }
memcpy(loaded_table, rproc->cached_table, tablesz);
@@ -913,11 +914,10 @@
* will be stored in the cached_table. Before the device is started,
* cached_table will be copied into devic memory.
*/
- rproc->cached_table = kmalloc(tablesz, GFP_KERNEL);
+ rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
if (!rproc->cached_table)
goto out;
- memcpy(rproc->cached_table, table, tablesz);
rproc->table_ptr = rproc->cached_table;
/* count the number of notify-ids */
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
index 157a573..9d30809 100644
--- a/drivers/remoteproc/remoteproc_debugfs.c
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -248,6 +248,5 @@
void __exit rproc_exit_debugfs(void)
{
- if (rproc_dbg)
- debugfs_remove(rproc_dbg);
+ debugfs_remove(rproc_dbg);
}
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 157e762..70701a5 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -107,12 +107,12 @@
static inline
struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
- const struct firmware *fw)
+ const struct firmware *fw)
{
if (rproc->fw_ops->find_loaded_rsc_table)
return rproc->fw_ops->find_loaded_rsc_table(rproc, fw);
- return NULL;
+ return NULL;
}
extern const struct rproc_fw_ops rproc_elf_fw_ops;
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 76e4c03..d35a5d6 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -1,10 +1,10 @@
-/*
+/*
* ASCII values for a number of symbolic constants, printing functions,
* etc.
* Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422)
* Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002)
* by D. Gilbert and aeb (20020609)
- * Update to SPC-4 T10/1713-D Rev 20, 22 May 2009, D. Gilbert 20090624
+ * Updated to SPC-4 T10/1713-D Rev 36g, D. Gilbert 20130701
*/
#include <linux/blkdev.h>
@@ -21,12 +21,13 @@
/* Commands with service actions that change the command name */
-#define MAINTENANCE_IN 0xa3
-#define MAINTENANCE_OUT 0xa4
#define SERVICE_ACTION_IN_12 0xab
#define SERVICE_ACTION_OUT_12 0xa9
+#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
#define SERVICE_ACTION_IN_16 0x9e
#define SERVICE_ACTION_OUT_16 0x9f
+#define THIRD_PARTY_COPY_OUT 0x83
+#define THIRD_PARTY_COPY_IN 0x84
@@ -36,11 +37,11 @@
/* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL,
"Reassign Blocks",
/* 08-0d */ "Read(6)", NULL, "Write(6)", "Seek(6)", NULL, NULL,
-/* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
/* 13-16 */ "Verify(6)", "Recover Buffered Data", "Mode Select(6)",
"Reserve(6)",
/* 17-1a */ "Release(6)", "Copy", "Erase", "Mode Sense(6)",
-/* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic",
+/* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic",
/* 1e-1f */ "Prevent/Allow Medium Removal", NULL,
/* 20-22 */ NULL, NULL, NULL,
/* 23-28 */ "Read Format Capacities", "Set Window",
@@ -48,16 +49,16 @@
/* 29-2d */ "Read Generation", "Write(10)", "Seek(10)", "Erase(10)",
"Read updated block",
/* 2e-31 */ "Write Verify(10)", "Verify(10)", "Search High", "Search Equal",
-/* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position",
/* 35-37 */ "Synchronize Cache(10)", "Lock/Unlock Cache(10)",
- "Read Defect Data(10)",
-/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
- "Read Buffer",
+ "Read Defect Data(10)",
+/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
+ "Read Buffer",
/* 3d-3f */ "Update Block", "Read Long(10)", "Write Long(10)",
/* 40-41 */ "Change Definition", "Write Same(10)",
/* 42-48 */ "Unmap/Read sub-channel", "Read TOC/PMA/ATIP",
"Read density support", "Play audio(10)", "Get configuration",
- "Play audio msf", "Play audio track/index",
+ "Play audio msf", "Sanitize/Play audio track/index",
/* 49-4f */ "Play track relative(10)", "Get event status notification",
"Pause/resume", "Log Select", "Log Sense", "Stop play/scan",
NULL,
@@ -72,17 +73,17 @@
/* 70-77 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, "Extended CDB",
"Variable length",
-/* 80-84 */ "Xdwrite(16)", "Rebuild(16)", "Regenerate(16)", "Extended copy",
- "Receive copy results",
+/* 80-84 */ "Xdwrite(16)", "Rebuild(16)", "Regenerate(16)",
+ "Third party copy out", "Third party copy in",
/* 85-89 */ "ATA command pass through(16)", "Access control in",
- "Access control out", "Read(16)", "Memory Export Out(16)",
+ "Access control out", "Read(16)", "Compare and Write",
/* 8a-8f */ "Write(16)", "ORWrite", "Read attributes", "Write attributes",
"Write and verify(16)", "Verify(16)",
/* 90-94 */ "Pre-fetch(16)", "Synchronize cache(16)",
"Lock/unlock cache(16)", "Write same(16)", NULL,
/* 95-99 */ NULL, NULL, NULL, NULL, NULL,
-/* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in(16)",
- "Service action out(16)",
+/* 9a-9f */ NULL, NULL, NULL, "Service action bidirectional",
+ "Service action in(16)", "Service action out(16)",
/* a0-a5 */ "Report luns", "ATA command pass through(12)/Blank",
"Security protocol in", "Maintenance in", "Maintenance out",
"Move medium/play audio(12)",
@@ -122,6 +123,7 @@
{0x6, "Set identifying information"},
{0xa, "Set target port groups"},
{0xb, "Change aliases"},
+ {0xc, "Remove I_T nexus"},
{0xe, "Set priority"},
{0xf, "Set timestamp"},
{0x10, "Management protocol out"},
@@ -138,10 +140,16 @@
};
#define SERV_OUT12_SZ ARRAY_SIZE(serv_out12_arr)
+static const struct value_name_pair serv_bidi_arr[] = {
+ {-1, "dummy entry"},
+};
+#define SERV_BIDI_SZ ARRAY_SIZE(serv_bidi_arr)
+
static const struct value_name_pair serv_in16_arr[] = {
{0x10, "Read capacity(16)"},
{0x11, "Read long(16)"},
{0x12, "Get LBA status"},
+ {0x13, "Report referrals"},
};
#define SERV_IN16_SZ ARRAY_SIZE(serv_in16_arr)
@@ -151,6 +159,51 @@
};
#define SERV_OUT16_SZ ARRAY_SIZE(serv_out16_arr)
+static const struct value_name_pair pr_in_arr[] = {
+ {0x0, "Persistent reserve in, read keys"},
+ {0x1, "Persistent reserve in, read reservation"},
+ {0x2, "Persistent reserve in, report capabilities"},
+ {0x3, "Persistent reserve in, read full status"},
+};
+#define PR_IN_SZ ARRAY_SIZE(pr_in_arr)
+
+static const struct value_name_pair pr_out_arr[] = {
+ {0x0, "Persistent reserve out, register"},
+ {0x1, "Persistent reserve out, reserve"},
+ {0x2, "Persistent reserve out, release"},
+ {0x3, "Persistent reserve out, clear"},
+ {0x4, "Persistent reserve out, preempt"},
+ {0x5, "Persistent reserve out, preempt and abort"},
+ {0x6, "Persistent reserve out, register and ignore existing key"},
+ {0x7, "Persistent reserve out, register and move"},
+};
+#define PR_OUT_SZ ARRAY_SIZE(pr_out_arr)
+
+/* SPC-4 rev 34 renamed the Extended Copy opcode to Third Party Copy Out.
+ LID1 (List Identifier length: 1 byte) is the Extended Copy found in SPC-2
+ and SPC-3 */
+static const struct value_name_pair tpc_out_arr[] = {
+ {0x0, "Extended copy(LID1)"},
+ {0x1, "Extended copy(LID4)"},
+ {0x10, "Populate token"},
+ {0x11, "Write using token"},
+ {0x1c, "Copy operation abort"},
+};
+#define TPC_OUT_SZ ARRAY_SIZE(tpc_out_arr)
+
+static const struct value_name_pair tpc_in_arr[] = {
+ {0x0, "Receive copy status(LID1)"},
+ {0x1, "Receive copy data(LID1)"},
+ {0x3, "Receive copy operating parameters"},
+ {0x4, "Receive copy failure details(LID1)"},
+ {0x5, "Receive copy status(LID4)"},
+ {0x6, "Receive copy data(LID4)"},
+ {0x7, "Receive ROD token information"},
+ {0x8, "Report all ROD tokens"},
+};
+#define TPC_IN_SZ ARRAY_SIZE(tpc_in_arr)
+
+
static const struct value_name_pair variable_length_arr[] = {
{0x1, "Rebuild(32)"},
{0x2, "Regenerate(32)"},
@@ -207,6 +260,7 @@
static void print_opcode_name(unsigned char * cdbp, int cdb_len)
{
int sa, len, cdb0;
+ int fin_name = 0;
const char * name;
cdb0 = cdbp[0];
@@ -219,7 +273,8 @@
break;
}
sa = (cdbp[8] << 8) + cdbp[9];
- name = get_sa_name(variable_length_arr, VARIABLE_LENGTH_SZ, sa);
+ name = get_sa_name(variable_length_arr, VARIABLE_LENGTH_SZ,
+ sa);
if (name)
printk("%s", name);
else
@@ -232,50 +287,57 @@
case MAINTENANCE_IN:
sa = cdbp[1] & 0x1f;
name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ fin_name = 1;
break;
case MAINTENANCE_OUT:
sa = cdbp[1] & 0x1f;
name = get_sa_name(maint_out_arr, MAINT_OUT_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ fin_name = 1;
+ break;
+ case PERSISTENT_RESERVE_IN:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(pr_in_arr, PR_IN_SZ, sa);
+ fin_name = 1;
+ break;
+ case PERSISTENT_RESERVE_OUT:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(pr_out_arr, PR_OUT_SZ, sa);
+ fin_name = 1;
break;
case SERVICE_ACTION_IN_12:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_in12_arr, SERV_IN12_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ fin_name = 1;
break;
case SERVICE_ACTION_OUT_12:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_out12_arr, SERV_OUT12_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ fin_name = 1;
+ break;
+ case SERVICE_ACTION_BIDIRECTIONAL:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(serv_bidi_arr, SERV_BIDI_SZ, sa);
+ fin_name = 1;
break;
case SERVICE_ACTION_IN_16:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_in16_arr, SERV_IN16_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ fin_name = 1;
break;
case SERVICE_ACTION_OUT_16:
sa = cdbp[1] & 0x1f;
name = get_sa_name(serv_out16_arr, SERV_OUT16_SZ, sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ fin_name = 1;
+ break;
+ case THIRD_PARTY_COPY_IN:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(tpc_in_arr, TPC_IN_SZ, sa);
+ fin_name = 1;
+ break;
+ case THIRD_PARTY_COPY_OUT:
+ sa = cdbp[1] & 0x1f;
+ name = get_sa_name(tpc_out_arr, TPC_OUT_SZ, sa);
+ fin_name = 1;
break;
default:
if (cdb0 < 0xc0) {
@@ -288,6 +350,12 @@
printk("cdb[0]=0x%x (vendor)", cdb0);
break;
}
+ if (fin_name) {
+ if (name)
+ printk("%s", name);
+ else
+ printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ }
}
#else /* ifndef CONFIG_SCSI_CONSTANTS */
@@ -312,10 +380,15 @@
break;
case MAINTENANCE_IN:
case MAINTENANCE_OUT:
+ case PERSISTENT_RESERVE_IN:
+ case PERSISTENT_RESERVE_OUT:
case SERVICE_ACTION_IN_12:
case SERVICE_ACTION_OUT_12:
+ case SERVICE_ACTION_BIDIRECTIONAL:
case SERVICE_ACTION_IN_16:
case SERVICE_ACTION_OUT_16:
+ case THIRD_PARTY_COPY_IN:
+ case THIRD_PARTY_COPY_OUT:
sa = cdbp[1] & 0x1f;
printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
break;
@@ -327,7 +400,7 @@
break;
}
}
-#endif
+#endif
void __scsi_print_command(unsigned char *cdb)
{
@@ -336,7 +409,7 @@
print_opcode_name(cdb, 0);
len = scsi_command_size(cdb);
/* print out all bytes in cdb */
- for (k = 0; k < len; ++k)
+ for (k = 0; k < len; ++k)
printk(" %02x", cdb[k]);
printk("\n");
}
@@ -404,8 +477,9 @@
/*
* The canonical list of T10 Additional Sense Codes is available at:
- * http://www.t10.org/lists/asc-num.txt
+ * http://www.t10.org/lists/asc-num.txt [most recent: 20130605]
*/
+
static const struct error_info additional[] =
{
{0x0000, "No additional sense information"},
@@ -430,6 +504,8 @@
{0x001C, "Verify operation in progress"},
{0x001D, "ATA pass through information available"},
{0x001E, "Conflicting SA creation request"},
+ {0x001F, "Logical unit transitioning to another power condition"},
+ {0x0020, "Extended copy information available"},
{0x0100, "No index/sector signal"},
@@ -460,6 +536,17 @@
{0x0412, "Logical unit not ready, offline"},
{0x0413, "Logical unit not ready, SA creation in progress"},
{0x0414, "Logical unit not ready, space allocation in progress"},
+ {0x0415, "Logical unit not ready, robotics disabled"},
+ {0x0416, "Logical unit not ready, configuration required"},
+ {0x0417, "Logical unit not ready, calibration required"},
+ {0x0418, "Logical unit not ready, a door is open"},
+ {0x0419, "Logical unit not ready, operating in sequential mode"},
+ {0x041A, "Logical unit not ready, start stop unit command in "
+ "progress"},
+ {0x041B, "Logical unit not ready, sanitize in progress"},
+ {0x041C, "Logical unit not ready, additional power use not yet "
+ "granted"},
+ {0x041D, "Logical unit not ready, configuration in progress"},
{0x0500, "Logical unit does not respond to selection"},
@@ -490,6 +577,7 @@
{0x0B06, "Warning - non-volatile cache now volatile"},
{0x0B07, "Warning - degraded power to non-volatile cache"},
{0x0B08, "Warning - power loss expected"},
+ {0x0B09, "Warning - device statistics notification active"},
{0x0C00, "Write error"},
{0x0C01, "Write error - recovered with auto reallocation"},
@@ -505,6 +593,7 @@
{0x0C0B, "Auxiliary memory write error"},
{0x0C0C, "Write error - unexpected unsolicited data"},
{0x0C0D, "Write error - not enough unsolicited data"},
+ {0x0C0E, "Multiple write errors"},
{0x0C0F, "Defects in error window"},
{0x0D00, "Error detected by third party temporary initiator"},
@@ -523,6 +612,8 @@
{0x1001, "Logical block guard check failed"},
{0x1002, "Logical block application tag check failed"},
{0x1003, "Logical block reference tag check failed"},
+ {0x1004, "Logical block protection error on recover buffered data"},
+ {0x1005, "Logical block protection method error"},
{0x1100, "Unrecovered read error"},
{0x1101, "Read retries exhausted"},
@@ -545,6 +636,7 @@
{0x1112, "Auxiliary memory read error"},
{0x1113, "Read error - failed retransmission request"},
{0x1114, "Read error - lba marked bad by application client"},
+ {0x1115, "Write after sanitize required"},
{0x1200, "Address mark not found for id field"},
@@ -622,6 +714,7 @@
{0x2009, "Access denied - invalid LU identifier"},
{0x200A, "Access denied - invalid proxy token"},
{0x200B, "Access denied - ACL LUN conflict"},
+ {0x200C, "Illegal command when not in append-only mode"},
{0x2100, "Logical block address out of range"},
{0x2101, "Invalid element address"},
@@ -630,6 +723,19 @@
{0x2200, "Illegal function (use 20 00, 24 00, or 26 00)"},
+ {0x2300, "Invalid token operation, cause not reportable"},
+ {0x2301, "Invalid token operation, unsupported token type"},
+ {0x2302, "Invalid token operation, remote token usage not supported"},
+ {0x2303, "Invalid token operation, remote rod token creation not "
+ "supported"},
+ {0x2304, "Invalid token operation, token unknown"},
+ {0x2305, "Invalid token operation, token corrupt"},
+ {0x2306, "Invalid token operation, token revoked"},
+ {0x2307, "Invalid token operation, token expired"},
+ {0x2308, "Invalid token operation, token cancelled"},
+ {0x2309, "Invalid token operation, token deleted"},
+ {0x230A, "Invalid token operation, invalid token length"},
+
{0x2400, "Invalid field in cdb"},
{0x2401, "CDB decryption error"},
{0x2402, "Obsolete"},
@@ -705,6 +811,7 @@
"event"},
{0x2A13, "Data encryption key instance counter has changed"},
{0x2A14, "SA creation capabilities data has changed"},
+ {0x2A15, "Medium removal prevention preempted"},
{0x2B00, "Copy cannot execute since host cannot disconnect"},
@@ -720,6 +827,7 @@
{0x2C09, "Previous reservation conflict status"},
{0x2C0A, "Partition or collection contains user objects"},
{0x2C0B, "Not reserved"},
+ {0x2C0C, "Orwrite generation does not match"},
{0x2D00, "Overwrite error on update in place"},
@@ -728,6 +836,7 @@
{0x2F00, "Commands cleared by another initiator"},
{0x2F01, "Commands cleared by power loss notification"},
{0x2F02, "Commands cleared by device server"},
+ {0x2F03, "Some commands cleared by queuing layer event"},
{0x3000, "Incompatible medium installed"},
{0x3001, "Cannot read medium - unknown format"},
@@ -745,10 +854,12 @@
{0x3010, "Medium not formatted"},
{0x3011, "Incompatible volume type"},
{0x3012, "Incompatible volume qualifier"},
+ {0x3013, "Cleaning volume expired"},
{0x3100, "Medium format corrupted"},
{0x3101, "Format command failed"},
{0x3102, "Zoned formatting failed due to spare linking"},
+ {0x3103, "Sanitize command failed"},
{0x3200, "No defect spare location available"},
{0x3201, "Defect list update failure"},
@@ -809,6 +920,8 @@
{0x3B19, "Element enabled"},
{0x3B1A, "Data transfer device removed"},
{0x3B1B, "Data transfer device inserted"},
+ {0x3B1C, "Too many logical objects on partition to support "
+ "operation"},
{0x3D00, "Invalid bits in identify message"},
@@ -839,6 +952,7 @@
{0x3F12, "iSCSI IP address added"},
{0x3F13, "iSCSI IP address removed"},
{0x3F14, "iSCSI IP address changed"},
+ {0x3F15, "Inspect referrals sense descriptors"},
/*
* {0x40NN, "Ram failure"},
* {0x40NN, "Diagnostic failure on component nn"},
@@ -848,6 +962,7 @@
{0x4300, "Message error"},
{0x4400, "Internal target failure"},
+ {0x4401, "Persistent reservation information lost"},
{0x4471, "ATA device failed set features"},
{0x4500, "Select or reselect failure"},
@@ -876,6 +991,21 @@
{0x4B04, "Nak received"},
{0x4B05, "Data offset error"},
{0x4B06, "Initiator response timeout"},
+ {0x4B07, "Connection lost"},
+ {0x4B08, "Data-in buffer overflow - data buffer size"},
+ {0x4B09, "Data-in buffer overflow - data buffer descriptor area"},
+ {0x4B0A, "Data-in buffer error"},
+ {0x4B0B, "Data-out buffer overflow - data buffer size"},
+ {0x4B0C, "Data-out buffer overflow - data buffer descriptor area"},
+ {0x4B0D, "Data-out buffer error"},
+ {0x4B0E, "PCIe fabric error"},
+ {0x4B0F, "PCIe completion timeout"},
+ {0x4B10, "PCIe completer abort"},
+ {0x4B11, "PCIe poisoned tlp received"},
+ {0x4B12, "PCIe eCRC check failed"},
+ {0x4B13, "PCIe unsupported request"},
+ {0x4B14, "PCIe acs violation"},
+ {0x4B15, "PCIe tlp prefix blocked"},
{0x4C00, "Logical unit failed self-configuration"},
/*
@@ -897,6 +1027,10 @@
{0x5302, "Medium removal prevented"},
{0x5303, "Medium removal prevented by data transfer element"},
{0x5304, "Medium thread or unthread failure"},
+ {0x5305, "Volume identifier invalid"},
+ {0x5306, "Volume identifier missing"},
+ {0x5307, "Duplicate volume identifier"},
+ {0x5308, "Element status unknown"},
{0x5400, "Scsi to host system interface failure"},
@@ -911,6 +1045,9 @@
{0x5508, "Maximum number of supplemental decryption keys exceeded"},
{0x5509, "Medium auxiliary memory not accessible"},
{0x550A, "Data currently unavailable"},
+ {0x550B, "Insufficient power for operation"},
+ {0x550C, "Insufficient resources to create rod"},
+ {0x550D, "Insufficient resources to create rod token"},
{0x5700, "Unable to recover table-of-contents"},
@@ -1069,6 +1206,7 @@
{0x670B, "ATA device feature not enabled"},
{0x6800, "Logical unit not configured"},
+ {0x6801, "Subsidiary logical unit not configured"},
{0x6900, "Data loss on logical unit"},
{0x6901, "Multiple logical unit failures"},
@@ -1185,10 +1323,13 @@
"Vendor Specific(9)",
"Copy Aborted", /* A: COPY or COMPARE was aborted */
"Aborted Command", /* B: The target aborted the command */
- "Equal", /* C: A SEARCH DATA command found data equal */
+ "Equal", /* C: A SEARCH DATA command found data equal,
+ reserved in SPC-4 rev 36 */
"Volume Overflow", /* D: Medium full with still data to be written */
"Miscompare", /* E: Source data and data on the medium
do not agree */
+ "Completed", /* F: command completed sense data reported,
+ may occur for successful command */
};
#endif
@@ -1306,7 +1447,7 @@
struct scsi_sense_hdr *sshdr)
{
int k, num, res;
-
+
res = scsi_normalize_sense(sense_buffer, sense_len, sshdr);
if (0 == res) {
/* this may be SCSI-1 sense data */
@@ -1459,5 +1600,3 @@
scsi_show_result(cmd->result);
}
EXPORT_SYMBOL(scsi_print_result);
-
-
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 4a05d04..07453bb 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -774,7 +774,6 @@
struct fcoe_port *port;
struct net_device *realdev;
int rc;
- struct netdev_fcoe_hbainfo fdmi;
port = lport_priv(lport);
fcoe = port->priv;
@@ -788,9 +787,13 @@
return;
if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) {
- memset(&fdmi, 0, sizeof(fdmi));
+ struct netdev_fcoe_hbainfo *fdmi;
+ fdmi = kzalloc(sizeof(*fdmi), GFP_KERNEL);
+ if (!fdmi)
+ return;
+
rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev,
- &fdmi);
+ fdmi);
if (rc) {
printk(KERN_INFO "fcoe: Failed to retrieve FDMI "
"information from netdev.\n");
@@ -800,38 +803,39 @@
snprintf(fc_host_serial_number(lport->host),
FC_SERIAL_NUMBER_SIZE,
"%s",
- fdmi.serial_number);
+ fdmi->serial_number);
snprintf(fc_host_manufacturer(lport->host),
FC_SERIAL_NUMBER_SIZE,
"%s",
- fdmi.manufacturer);
+ fdmi->manufacturer);
snprintf(fc_host_model(lport->host),
FC_SYMBOLIC_NAME_SIZE,
"%s",
- fdmi.model);
+ fdmi->model);
snprintf(fc_host_model_description(lport->host),
FC_SYMBOLIC_NAME_SIZE,
"%s",
- fdmi.model_description);
+ fdmi->model_description);
snprintf(fc_host_hardware_version(lport->host),
FC_VERSION_STRING_SIZE,
"%s",
- fdmi.hardware_version);
+ fdmi->hardware_version);
snprintf(fc_host_driver_version(lport->host),
FC_VERSION_STRING_SIZE,
"%s",
- fdmi.driver_version);
+ fdmi->driver_version);
snprintf(fc_host_optionrom_version(lport->host),
FC_VERSION_STRING_SIZE,
"%s",
- fdmi.optionrom_version);
+ fdmi->optionrom_version);
snprintf(fc_host_firmware_version(lport->host),
FC_VERSION_STRING_SIZE,
"%s",
- fdmi.firmware_version);
+ fdmi->firmware_version);
/* Enable FDMI lport states */
lport->fdmi_enabled = 1;
+ kfree(fdmi);
} else {
lport->fdmi_enabled = 0;
printk(KERN_INFO "fcoe: No FDMI support.\n");
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 795843d..203415e 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -2090,7 +2090,11 @@
*/
static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
{
+ struct fc_rport_priv *rdata;
+
mutex_lock(&lport->disc.disc_mutex);
+ list_for_each_entry_rcu(rdata, &lport->disc.rports, peers)
+ lport->tt.rport_logoff(rdata);
lport->disc.disc_callback = NULL;
mutex_unlock(&lport->disc.disc_mutex);
}
diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c
index 8c05ae01..c9382d6 100644
--- a/drivers/scsi/fcoe/fcoe_sysfs.c
+++ b/drivers/scsi/fcoe/fcoe_sysfs.c
@@ -507,7 +507,7 @@
NULL,
};
-struct bus_type fcoe_bus_type;
+static struct bus_type fcoe_bus_type;
static int fcoe_bus_match(struct device *dev,
struct device_driver *drv)
@@ -541,25 +541,25 @@
kfree(fcf);
}
-struct device_type fcoe_ctlr_device_type = {
+static struct device_type fcoe_ctlr_device_type = {
.name = "fcoe_ctlr",
.groups = fcoe_ctlr_attr_groups,
.release = fcoe_ctlr_device_release,
};
-struct device_type fcoe_fcf_device_type = {
+static struct device_type fcoe_fcf_device_type = {
.name = "fcoe_fcf",
.groups = fcoe_fcf_attr_groups,
.release = fcoe_fcf_device_release,
};
-struct bus_attribute fcoe_bus_attr_group[] = {
+static struct bus_attribute fcoe_bus_attr_group[] = {
__ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store),
__ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store),
__ATTR_NULL
};
-struct bus_type fcoe_bus_type = {
+static struct bus_type fcoe_bus_type = {
.name = "fcoe",
.match = &fcoe_bus_match,
.bus_attrs = fcoe_bus_attr_group,
@@ -569,7 +569,7 @@
* fcoe_ctlr_device_flush_work() - Flush a FIP ctlr's workqueue
* @ctlr: Pointer to the FIP ctlr whose workqueue is to be flushed
*/
-void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr)
+static void fcoe_ctlr_device_flush_work(struct fcoe_ctlr_device *ctlr)
{
if (!fcoe_ctlr_work_q(ctlr)) {
printk(KERN_ERR
@@ -590,8 +590,8 @@
* Return value:
* 1 on success / 0 already queued / < 0 for error
*/
-int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr,
- struct work_struct *work)
+static int fcoe_ctlr_device_queue_work(struct fcoe_ctlr_device *ctlr,
+ struct work_struct *work)
{
if (unlikely(!fcoe_ctlr_work_q(ctlr))) {
printk(KERN_ERR
@@ -609,7 +609,7 @@
* fcoe_ctlr_device_flush_devloss() - Flush a FIP ctlr's devloss workqueue
* @ctlr: Pointer to FIP ctlr whose workqueue is to be flushed
*/
-void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr)
+static void fcoe_ctlr_device_flush_devloss(struct fcoe_ctlr_device *ctlr)
{
if (!fcoe_ctlr_devloss_work_q(ctlr)) {
printk(KERN_ERR
@@ -631,9 +631,9 @@
* Return value:
* 1 on success / 0 already queued / < 0 for error
*/
-int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr,
- struct delayed_work *work,
- unsigned long delay)
+static int fcoe_ctlr_device_queue_devloss_work(struct fcoe_ctlr_device *ctlr,
+ struct delayed_work *work,
+ unsigned long delay)
{
if (unlikely(!fcoe_ctlr_devloss_work_q(ctlr))) {
printk(KERN_ERR
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 01adbe0..74277c2 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -180,24 +180,10 @@
{
struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev);
struct net_device *netdev = fcoe_get_netdev(fip->lp);
- struct fcoe_fc_els_lesb *fcoe_lesb;
- struct fc_els_lesb fc_lesb;
+ struct fc_els_lesb *fc_lesb;
- __fcoe_get_lesb(fip->lp, &fc_lesb, netdev);
- fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb);
-
- ctlr_dev->lesb.lesb_link_fail =
- ntohl(fcoe_lesb->lesb_link_fail);
- ctlr_dev->lesb.lesb_vlink_fail =
- ntohl(fcoe_lesb->lesb_vlink_fail);
- ctlr_dev->lesb.lesb_miss_fka =
- ntohl(fcoe_lesb->lesb_miss_fka);
- ctlr_dev->lesb.lesb_symb_err =
- ntohl(fcoe_lesb->lesb_symb_err);
- ctlr_dev->lesb.lesb_err_block =
- ntohl(fcoe_lesb->lesb_err_block);
- ctlr_dev->lesb.lesb_fcs_error =
- ntohl(fcoe_lesb->lesb_fcs_error);
+ fc_lesb = (struct fc_els_lesb *)(&ctlr_dev->lesb);
+ __fcoe_get_lesb(fip->lp, fc_lesb, netdev);
}
EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb);
@@ -721,7 +707,6 @@
{
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- struct fcoe_ctlr_device *ctlr_dev = NULL;
int rc = 0;
int err;
@@ -768,9 +753,8 @@
goto out_putdev;
}
- LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n",
- ft->name, (ctlr_dev) ? "succeeded" : "failed",
- netdev->name);
+ LIBFCOE_TRANSPORT_DBG("transport %s succeeded to create fcoe on %s.\n",
+ ft->name, netdev->name);
out_putdev:
dev_put(netdev);
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 8b928c6..5879929 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -337,7 +337,7 @@
* fc_exch_timer_cancel() - cancel exch timer
* @ep: The exchange whose timer to be canceled
*/
-static inline void fc_exch_timer_cancel(struct fc_exch *ep)
+static inline void fc_exch_timer_cancel(struct fc_exch *ep)
{
if (cancel_delayed_work(&ep->timeout_work)) {
FC_EXCH_DBG(ep, "Exchange timer canceled\n");
@@ -1567,7 +1567,7 @@
fc_exch_rctl_name(fh->fh_r_ctl));
if (cancel_delayed_work_sync(&ep->timeout_work)) {
- FC_EXCH_DBG(ep, "Exchange timer canceled\n");
+ FC_EXCH_DBG(ep, "Exchange timer canceled due to ABTS response\n");
fc_exch_release(ep); /* release from pending timer hold */
}
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 6bbb944..c710d90 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -926,6 +926,20 @@
kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
+static bool
+fc_rport_compatible_roles(struct fc_lport *lport, struct fc_rport_priv *rdata)
+{
+ if (rdata->ids.roles == FC_PORT_ROLE_UNKNOWN)
+ return true;
+ if ((rdata->ids.roles & FC_PORT_ROLE_FCP_TARGET) &&
+ (lport->service_params & FCP_SPPF_INIT_FCN))
+ return true;
+ if ((rdata->ids.roles & FC_PORT_ROLE_FCP_INITIATOR) &&
+ (lport->service_params & FCP_SPPF_TARG_FCN))
+ return true;
+ return false;
+}
+
/**
* fc_rport_enter_plogi() - Send Port Login (PLOGI) request
* @rdata: The remote port to send a PLOGI to
@@ -938,6 +952,12 @@
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
+ if (!fc_rport_compatible_roles(lport, rdata)) {
+ FC_RPORT_DBG(rdata, "PLOGI suppressed for incompatible role\n");
+ fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+ return;
+ }
+
FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",
fc_rport_state(rdata));
@@ -1646,6 +1666,13 @@
rjt_data.explan = ELS_EXPL_NONE;
goto reject;
}
+ if (!fc_rport_compatible_roles(lport, rdata)) {
+ FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n");
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
+ }
/*
* Get session payload size from incoming PLOGI.
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 6002d36..0177295 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -4958,10 +4958,12 @@
sense, sense_handle);
}
- for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) {
- dma_free_coherent(&instance->pdev->dev,
- kern_sge32[i].length,
- kbuff_arr[i], kern_sge32[i].phys_addr);
+ for (i = 0; i < ioc->sge_count; i++) {
+ if (kbuff_arr[i])
+ dma_free_coherent(&instance->pdev->dev,
+ kern_sge32[i].length,
+ kbuff_arr[i],
+ kern_sge32[i].phys_addr);
}
megasas_return_cmd(instance, cmd);
diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c
index 8056eac..4f401f7 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fp.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fp.c
@@ -585,7 +585,7 @@
case 1:
/* start with logical arm */
arm = get_arm_from_strip(instance, ld, stripe, map);
- if (arm != -1UL)
+ if (arm != -1U)
arm *= 2;
break;
}
@@ -637,7 +637,7 @@
if (raid->level == 6) {
logArm = get_arm_from_strip(instance, ld, stripRow, map);
- if (logArm == -1UL)
+ if (logArm == -1U)
return FALSE;
rowMod = mega_mod64(row, SPAN_ROW_SIZE(map, ld, span));
armQ = SPAN_ROW_SIZE(map, ld, span) - 1 - rowMod;
diff --git a/drivers/scsi/mpt3sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig
index 81471bf..d53e1b0 100644
--- a/drivers/scsi/mpt3sas/Kconfig
+++ b/drivers/scsi/mpt3sas/Kconfig
@@ -2,7 +2,7 @@
# Kernel configuration file for the MPT3SAS
#
# This code is based on drivers/scsi/mpt3sas/Kconfig
-# Copyright (C) 2012 LSI Corporation
+# Copyright (C) 2012-2013 LSI Corporation
# (mailto:DL-MPTFusionLinux@lsi.com)
# This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h
index 03317ff..20da8f9 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2012 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2.h
@@ -8,7 +8,7 @@
* scatter/gather formats.
* Creation Date: June 21, 2006
*
- * mpi2.h Version: 02.00.26
+ * mpi2.h Version: 02.00.29
*
* NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
* prefix are for use only on MPI v2.5 products, and must not be used
@@ -82,6 +82,10 @@
* 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT.
* Added Hard Reset delay timings.
* 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 07-26-12 02.00.27 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-27-12 02.00.28 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 12-20-12 02.00.29 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET.
* --------------------------------------------------------------------------
*/
@@ -115,7 +119,7 @@
#define MPI2_VERSION_02_05 (0x0205)
/*Unit and Dev versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT (0x1A)
+#define MPI2_HEADER_VERSION_UNIT (0x1D)
#define MPI2_HEADER_VERSION_DEV (0x00)
#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
@@ -274,6 +278,8 @@
#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF)
#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000)
#define MPI2_RPHI_MSIX_INDEX_SHIFT (24)
+#define MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET (0x0000030C) /*MPI v2.5 only*/
+
/*
*Defines for the HCBSize and address
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
index d8b2c3e..889aa70 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 2000-2011 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_cnfg.h
* Title: MPI Configuration messages and pages
* Creation Date: November 10, 2006
*
- * mpi2_cnfg.h Version: 02.00.22
+ * mpi2_cnfg.h Version: 02.00.24
*
* NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
* prefix are for use only on MPI v2.5 products, and must not be used
@@ -155,6 +155,11 @@
* Added UEFIVersion field to BIOS Page 1 and defined new
* BiosOptions bits.
* Incorporating additions for MPI v2.5.
+ * 11-27-12 02.00.23 Added MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER.
+ * Added MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID.
+ * 12-20-12 02.00.24 Marked MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION as
+ * obsolete for MPI v2.5 and later.
+ * Added some defines for 12G SAS speeds.
* --------------------------------------------------------------------------
*/
@@ -714,6 +719,7 @@
#define MPI2_MANUFACTURING7_PAGEVERSION (0x01)
/*defines for the Flags field */
+#define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002)
#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001)
@@ -1310,6 +1316,9 @@
#define MPI2_BIOSPAGE1_PAGEVERSION (0x05)
/*values for BIOS Page 1 BiosOptions field */
+#define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0)
+#define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000)
+
#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006)
#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000)
#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002)
@@ -1884,6 +1893,7 @@
#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80)
#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90)
#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0)
+#define MPI25_SAS_PRATE_MAX_RATE_12_0 (0xB0)
#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F)
#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08)
@@ -1897,6 +1907,7 @@
#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80)
#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90)
#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0)
+#define MPI25_SAS_HWRATE_MAX_RATE_12_0 (0xB0)
#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F)
#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08)
#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09)
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_init.h b/drivers/scsi/mpt3sas/mpi/mpi2_init.h
index a079e52..f7928bf 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_init.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_init.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2012 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_init.h
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
index 0de425d..e2bb821 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 2000-2012 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_ioc.h
* Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
* Creation Date: October 11, 2006
*
- * mpi2_ioc.h Version: 02.00.21
+ * mpi2_ioc.h Version: 02.00.22
*
* NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
* prefix are for use only on MPI v2.5 products, and must not be used
@@ -124,6 +124,9 @@
* Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete.
* 11-18-11 02.00.20 Incorporating additions for MPI v2.5.
* 03-29-12 02.00.21 Added a product specific range to event values.
+ * 07-26-12 02.00.22 Added MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE.
+ * Added ElapsedSeconds field to
+ * MPI2_EVENT_DATA_IR_OPERATION_STATUS.
* --------------------------------------------------------------------------
*/
@@ -283,6 +286,7 @@
#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0)
/*IOCExceptions */
+#define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200)
#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100)
#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0)
@@ -634,7 +638,7 @@
U8 RAIDOperation; /*0x04 */
U8 PercentComplete; /*0x05 */
U16 Reserved2; /*0x06 */
- U32 Resereved3; /*0x08 */
+ U32 ElapsedSeconds; /*0x08 */
} MPI2_EVENT_DATA_IR_OPERATION_STATUS,
*PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS,
Mpi2EventDataIrOperationStatus_t,
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_raid.h b/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
index d1d9866..7176523 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 2000-2012 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_raid.h
* Title: MPI Integrated RAID messages and structures
* Creation Date: April 26, 2007
*
- * mpi2_raid.h Version: 02.00.08
+ * mpi2_raid.h Version: 02.00.09
*
* Version History
* ---------------
@@ -28,6 +28,8 @@
* Added product-specific range to RAID Action values.
* 11-18-11 02.00.07 Incorporating additions for MPI v2.5.
* 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN.
+ * 07-26-12 02.00.09 Added ElapsedSeconds field to MPI2_RAID_VOL_INDICATOR.
+ * Added MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID define.
* --------------------------------------------------------------------------
*/
@@ -269,10 +271,12 @@
U64 TotalBlocks; /*0x00 */
U64 BlocksRemaining; /*0x08 */
U32 Flags; /*0x10 */
+ U32 ElapsedSeconds; /* 0x14 */
} MPI2_RAID_VOL_INDICATOR, *PTR_MPI2_RAID_VOL_INDICATOR,
Mpi2RaidVolIndicator_t, *pMpi2RaidVolIndicator_t;
/*defines for RAID Volume Indicator Flags field */
+#define MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID (0x80000000)
#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F)
#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000)
#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
@@ -312,7 +316,7 @@
/*RAID Action Reply ActionData union */
typedef union _MPI2_RAID_ACTION_REPLY_DATA {
- U32 Word[5];
+ U32 Word[6];
MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator;
U16 VolDevHandle;
U8 VolumeState;
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_sas.h b/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
index b4e7084a..cba046f 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2012 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_sas.h
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
index 71453d1..34e9a7b 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
@@ -1,12 +1,12 @@
/*
- * Copyright (c) 2000-2012 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_tool.h
* Title: MPI diagnostic tool structures and definitions
* Creation Date: March 26, 2007
*
- * mpi2_tool.h Version: 02.00.09
+ * mpi2_tool.h Version: 02.00.10
*
* Version History
* ---------------
@@ -30,6 +30,8 @@
* 11-18-11 02.00.08 Incorporating additions for MPI v2.5.
* 07-10-12 02.00.09 Add MPI v2.5 Toolbox Diagnostic CLI Tool Request
* message.
+ * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that
+ * it uses MPI Chain SGE as well as MPI Simple SGE.
* --------------------------------------------------------------------------
*/
@@ -279,7 +281,7 @@
U16 Reserved6; /*0x0E */
U32 DataLength; /*0x10 */
U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */
- MPI2_SGE_SIMPLE_UNION SGL; /*0x70 */
+ MPI2_MPI_SGE_IO_UNION SGL; /*0x70 */
} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
*PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
Mpi2ToolboxDiagnosticCliRequest_t,
@@ -302,7 +304,7 @@
U32 Reserved5; /*0x0C */
U32 DataLength; /*0x10 */
U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */
- MPI25_SGE_IO_UNION SGL; /*0x70 */
+ MPI25_SGE_IO_UNION SGL; /* 0x70 */
} MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
*PTR_MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
Mpi25ToolboxDiagnosticCliRequest_t,
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_type.h b/drivers/scsi/mpt3sas/mpi/mpi2_type.h
index 516f959..ba1fed5 100644
--- a/drivers/scsi/mpt3sas/mpi/mpi2_type.h
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_type.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2007 LSI Corporation.
+ * Copyright (c) 2000-2013 LSI Corporation.
*
*
* Name: mpi2_type.h
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 1836003..5dc280c 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -3,7 +3,7 @@
* for access to MPT (Message Passing Technology) firmware.
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
@@ -4090,11 +4090,15 @@
writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER,
&ioc->chip->HostDiagnostic);
- /* don't access any registers for 50 milliseconds */
- msleep(50);
+ /*This delay allows the chip PCIe hardware time to finish reset tasks*/
+ if (sleep_flag == CAN_SLEEP)
+ msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000);
+ else
+ mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000);
- /* 300 second max wait */
- for (count = 0; count < 3000000 ; count++) {
+ /* Approximately 300 second max wait */
+ for (count = 0; count < (300000000 /
+ MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) {
host_diagnostic = readl(&ioc->chip->HostDiagnostic);
@@ -4103,11 +4107,13 @@
if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER))
break;
- /* wait 1 msec */
+ /* Wait to pass the second read delay window */
if (sleep_flag == CAN_SLEEP)
- usleep_range(1000, 1500);
+ msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC
+ / 1000);
else
- mdelay(1);
+ mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC
+ / 1000);
}
if (host_diagnostic & MPI2_DIAG_HCB_MODE) {
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index 994656c..0ebf5d9 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -3,7 +3,7 @@
* for access to MPT (Message Passing Technology) firmware.
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
@@ -70,10 +70,10 @@
#define MPT3SAS_DRIVER_NAME "mpt3sas"
#define MPT3SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver"
-#define MPT3SAS_DRIVER_VERSION "01.100.01.00"
-#define MPT3SAS_MAJOR_VERSION 1
+#define MPT3SAS_DRIVER_VERSION "02.100.00.00"
+#define MPT3SAS_MAJOR_VERSION 2
#define MPT3SAS_MINOR_VERSION 100
-#define MPT3SAS_BUILD_VERSION 1
+#define MPT3SAS_BUILD_VERSION 0
#define MPT3SAS_RELEASE_VERSION 00
/*
diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c
index 4db0c7a..936ec03 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_config.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_config.c
@@ -2,7 +2,7 @@
* This module provides common API for accessing firmware configuration pages
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
index 0b402b6..9b89de1 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
@@ -3,7 +3,7 @@
* controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.h b/drivers/scsi/mpt3sas/mpt3sas_ctl.h
index bd89f4f..53b0c48 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_ctl.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.h
@@ -3,7 +3,7 @@
* controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.h
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpt3sas_debug.h b/drivers/scsi/mpt3sas/mpt3sas_debug.h
index 35405e7..545b22d 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_debug.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_debug.h
@@ -2,7 +2,7 @@
* Logging Support for MPT (Message Passing Technology) based controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_debug.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index dcbf7c8..8cbe8fd 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -2,7 +2,7 @@
* Scsi Host Layer for MPT (Message Passing Technology) based controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
@@ -675,11 +675,12 @@
* devices while scanning is turned on due to an oops in
* scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start()
*/
- if (!ioc->is_driver_loading)
+ if (!ioc->is_driver_loading) {
mpt3sas_transport_port_remove(ioc,
sas_device->sas_address,
sas_device->sas_address_parent);
- _scsih_sas_device_remove(ioc, sas_device);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
}
}
@@ -1273,6 +1274,7 @@
struct MPT3SAS_DEVICE *sas_device_priv_data;
struct scsi_target *starget;
struct _raid_device *raid_device;
+ struct _sas_device *sas_device;
unsigned long flags;
sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL);
@@ -1301,6 +1303,19 @@
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
}
+ if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_target_priv_data->sas_address);
+ if (sas_device && (sas_device->starget == NULL)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s : sas_device->starget set to starget @ %d\n",
+ __func__, __LINE__);
+ sas_device->starget = starget;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+
return 0;
}
@@ -6392,7 +6407,7 @@
handle))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
break;
handle = le16_to_cpu(sas_device_pg0.DevHandle);
device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
@@ -6494,7 +6509,7 @@
&volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
break;
handle = le16_to_cpu(volume_pg1.DevHandle);
@@ -6518,7 +6533,7 @@
phys_disk_num))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
break;
phys_disk_num = pd_pg0.PhysDiskNum;
handle = le16_to_cpu(pd_pg0.DevHandle);
@@ -6597,7 +6612,7 @@
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
break;
handle = le16_to_cpu(expander_pg0.DevHandle);
@@ -6742,8 +6757,6 @@
MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
pr_info(MPT3SAS_FMT "\tbreak from expander scan: " \
"ioc_status(0x%04x), loginfo(0x%08x)\n",
@@ -6787,8 +6800,6 @@
phys_disk_num))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
pr_info(MPT3SAS_FMT "\tbreak from phys disk scan: "\
"ioc_status(0x%04x), loginfo(0x%08x)\n",
@@ -6854,8 +6865,6 @@
&volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \
"ioc_status(0x%04x), loginfo(0x%08x)\n",
@@ -6914,8 +6923,6 @@
handle))) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
pr_info(MPT3SAS_FMT "\tbreak from end device scan:"\
" ioc_status(0x%04x), loginfo(0x%08x)\n",
@@ -7525,10 +7532,12 @@
sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
} else if (!sas_device->starget) {
- if (!ioc->is_driver_loading)
- mpt3sas_transport_port_remove(ioc, sas_address,
+ if (!ioc->is_driver_loading) {
+ mpt3sas_transport_port_remove(ioc,
+ sas_address,
sas_address_parent);
- _scsih_sas_device_remove(ioc, sas_device);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
}
}
}
@@ -7584,13 +7593,14 @@
* oops in scsi_sysfs_add_sdev()->add_device()->
* sysfs_addrm_start()
*/
- if (!ioc->is_driver_loading)
+ if (!ioc->is_driver_loading) {
mpt3sas_transport_port_remove(ioc,
sas_device->sas_address,
sas_device->sas_address_parent);
- list_del(&sas_device->list);
- kfree(sas_device);
- continue;
+ list_del(&sas_device->list);
+ kfree(sas_device);
+ continue;
+ }
}
spin_lock_irqsave(&ioc->sas_device_lock, flags);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c
index 87ca2b7..dcadd56 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_transport.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c
@@ -2,7 +2,7 @@
* SAS Transport Layer for MPT (Message Passing Technology) based controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
index 6f8d621..f6533ab 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
@@ -3,7 +3,7 @@
* (Message Passing Technology) based controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
index a10c309..bb69392 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
@@ -4,7 +4,7 @@
* controllers
*
* This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h
- * Copyright (C) 2012 LSI Corporation
+ * Copyright (C) 2012-2013 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index e4b9bc7..3861aa1f 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -912,14 +912,13 @@
{
struct sas_ha_struct *sha = pci_get_drvdata(pdev);
struct pm8001_hba_info *pm8001_ha;
- int i , pos;
+ int i;
u32 device_state;
pm8001_ha = sha->lldd_ha;
flush_workqueue(pm8001_wq);
scsi_block_requests(pm8001_ha->shost);
- pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pos == 0) {
- printk(KERN_ERR " PCI PM not supported\n");
+ if (!pdev->pm_cap) {
+ dev_err(&pdev->dev, " PCI PM not supported\n");
return -ENODEV;
}
PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF);
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index bf60c63..d7a99ae 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1691,6 +1691,9 @@
if (unlikely(pci_channel_offline(ha->pdev)))
goto done;
+ if (qla2x00_reset_active(vha))
+ goto done;
+
stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma);
if (stats == NULL) {
ql_log(ql_log_warn, vha, 0x707d,
@@ -1703,7 +1706,7 @@
if (IS_FWI2_CAPABLE(ha)) {
rval = qla24xx_get_isp_stats(base_vha, stats, stats_dma);
} else if (atomic_read(&base_vha->loop_state) == LOOP_READY &&
- !qla2x00_reset_active(vha) && !ha->dpc_active) {
+ !ha->dpc_active) {
/* Must be in a 'READY' state for statistics retrieval. */
rval = qla2x00_get_link_status(base_vha, base_vha->loop_id,
stats, stats_dma);
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 39719f8..417eaad 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -269,6 +269,12 @@
type = "FC_BSG_HST_ELS_NOLOGIN";
}
+ if (!vha->flags.online) {
+ ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n");
+ rval = -EIO;
+ goto done;
+ }
+
/* pass through is supported only for ISP 4Gb or higher */
if (!IS_FWI2_CAPABLE(ha)) {
ql_dbg(ql_dbg_user, vha, 0x7001,
@@ -326,12 +332,6 @@
NPH_FABRIC_CONTROLLER : NPH_F_PORT;
}
- if (!vha->flags.online) {
- ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n");
- rval = -EIO;
- goto done;
- }
-
req_sg_cnt =
dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
@@ -399,7 +399,7 @@
goto done_free_fcport;
done_free_fcport:
- if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN)
+ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS)
kfree(fcport);
done:
return rval;
@@ -1084,14 +1084,6 @@
return -EINVAL;
}
- ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request +
- sizeof(struct fc_bsg_request));
- if (!ql84_mgmt) {
- ql_log(ql_log_warn, vha, 0x703b,
- "MGMT header not provided, exiting.\n");
- return -EINVAL;
- }
-
mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
if (!mn) {
ql_log(ql_log_warn, vha, 0x703c,
@@ -1102,7 +1094,7 @@
memset(mn, 0, sizeof(struct access_chip_84xx));
mn->entry_type = ACCESS_CHIP_IOCB_TYPE;
mn->entry_count = 1;
-
+ ql84_mgmt = (void *)bsg_job->request + sizeof(struct fc_bsg_request);
switch (ql84_mgmt->mgmt.cmd) {
case QLA84_MGMT_READ_MEM:
case QLA84_MGMT_GET_INFO:
@@ -1282,14 +1274,7 @@
return -EINVAL;
}
- port_param = (struct qla_port_param *)((char *)bsg_job->request +
- sizeof(struct fc_bsg_request));
- if (!port_param) {
- ql_log(ql_log_warn, vha, 0x7047,
- "port_param header not provided.\n");
- return -EINVAL;
- }
-
+ port_param = (void *)bsg_job->request + sizeof(struct fc_bsg_request);
if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) {
ql_log(ql_log_warn, vha, 0x7048,
"Invalid destination type.\n");
@@ -2153,6 +2138,7 @@
(sp->type == SRB_ELS_CMD_HST) ||
(sp->type == SRB_FXIOCB_BCMD))
&& (sp->u.bsg_job == bsg_job)) {
+ req->outstanding_cmds[cnt] = NULL;
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (ha->isp_ops->abort_command(sp)) {
ql_log(ql_log_warn, vha, 0x7089,
@@ -2180,8 +2166,6 @@
done:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- if (bsg_job->request->msgcode == FC_BSG_HST_CT)
- kfree(sp->fcport);
- qla2x00_rel_sp(vha, sp);
+ sp->free(vha, sp);
return 0;
}
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index cfa2a20..df132fe 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -12,9 +12,10 @@
* | Level | Last Value Used | Holes |
* ----------------------------------------------------------------------
* | Module Init and Probe | 0x014f | 0x4b,0xba,0xfa |
- * | Mailbox commands | 0x1179 | 0x111a-0x111b |
+ * | Mailbox commands | 0x117a | 0x111a-0x111b |
* | | | 0x1155-0x1158 |
* | Device Discovery | 0x2095 | 0x2020-0x2022, |
+ * | | | 0x2011-0x2012, |
* | | | 0x2016 |
* | Queue Command and IO tracing | 0x3058 | 0x3006-0x300b |
* | | | 0x3027-0x3028 |
@@ -35,7 +36,8 @@
* | | | 0x70a5,0x70a6, |
* | | | 0x70a8,0x70ab, |
* | | | 0x70ad-0x70ae, |
- * | | | 0x70d1-0x70da |
+ * | | | 0x70d1-0x70da, |
+ * | | | 0x7047,0x703b |
* | Task Management | 0x803c | 0x8025-0x8026 |
* | | | 0x800b,0x8039 |
* | AER/EEH | 0x9011 | |
@@ -1468,7 +1470,7 @@
nxt = qla2xxx_copy_queues(ha, nxt);
- nxt = qla24xx_copy_eft(ha, nxt);
+ qla24xx_copy_eft(ha, nxt);
/* Chain entries -- started with MQ. */
nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
@@ -1787,7 +1789,7 @@
nxt = qla2xxx_copy_queues(ha, nxt);
- nxt = qla24xx_copy_eft(ha, nxt);
+ qla24xx_copy_eft(ha, nxt);
/* Chain entries -- started with MQ. */
nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
@@ -2289,7 +2291,7 @@
copy_queue:
nxt = qla2xxx_copy_queues(ha, nxt);
- nxt = qla24xx_copy_eft(ha, nxt);
+ qla24xx_copy_eft(ha, nxt);
/* Chain entries -- started with MQ. */
nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index c32efc7..95ca32a 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -323,7 +323,7 @@
uint32_t lun;
uint32_t data;
struct completion comp;
- uint32_t comp_status;
+ __le16 comp_status;
} tmf;
struct {
#define SRB_FXDISC_REQ_DMA_VALID BIT_0
@@ -338,21 +338,21 @@
void *rsp_addr;
dma_addr_t req_dma_handle;
dma_addr_t rsp_dma_handle;
- uint32_t adapter_id;
- uint32_t adapter_id_hi;
- uint32_t req_func_type;
- uint32_t req_data;
- uint32_t req_data_extra;
- uint32_t result;
- uint32_t seq_number;
- uint32_t fw_flags;
+ __le32 adapter_id;
+ __le32 adapter_id_hi;
+ __le16 req_func_type;
+ __le32 req_data;
+ __le32 req_data_extra;
+ __le32 result;
+ __le32 seq_number;
+ __le16 fw_flags;
struct completion fxiocb_comp;
- uint32_t reserved_0;
+ __le32 reserved_0;
uint8_t reserved_1;
} fxiocb;
struct {
uint32_t cmd_hndl;
- uint32_t comp_status;
+ __le16 comp_status;
struct completion comp;
} abt;
} u;
@@ -1196,14 +1196,14 @@
struct init_cb_fx {
uint16_t version;
uint16_t reserved_1[13];
- uint16_t request_q_outpointer;
- uint16_t response_q_inpointer;
+ __le16 request_q_outpointer;
+ __le16 response_q_inpointer;
uint16_t reserved_2[2];
- uint16_t response_q_length;
- uint16_t request_q_length;
+ __le16 response_q_length;
+ __le16 request_q_length;
uint16_t reserved_3[2];
- uint32_t request_q_address[2];
- uint32_t response_q_address[2];
+ __le32 request_q_address[2];
+ __le32 response_q_address[2];
uint16_t reserved_4[4];
uint8_t response_q_msivec;
uint8_t reserved_5[19];
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 026bfde..2d98232 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -587,7 +587,7 @@
extern int qlafx00_fw_ready(scsi_qla_host_t *);
extern int qlafx00_configure_devices(scsi_qla_host_t *);
extern int qlafx00_reset_initialize(scsi_qla_host_t *);
-extern int qlafx00_fx_disc(scsi_qla_host_t *, fc_port_t *, uint8_t);
+extern int qlafx00_fx_disc(scsi_qla_host_t *, fc_port_t *, uint16_t);
extern int qlafx00_process_aen(struct scsi_qla_host *, struct qla_work_evt *);
extern int qlafx00_post_aenfx_work(struct scsi_qla_host *, uint32_t,
uint32_t *, int);
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index d0ea8b9..0926451 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -99,17 +99,17 @@
* Returns a pointer to the intitialized @ct_req.
*/
static inline struct ct_sns_req *
-qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size)
+qla2x00_prep_ct_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size)
{
- memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+ memset(p, 0, sizeof(struct ct_sns_pkt));
- ct_req->header.revision = 0x01;
- ct_req->header.gs_type = 0xFC;
- ct_req->header.gs_subtype = 0x02;
- ct_req->command = cpu_to_be16(cmd);
- ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+ p->p.req.header.revision = 0x01;
+ p->p.req.header.gs_type = 0xFC;
+ p->p.req.header.gs_subtype = 0x02;
+ p->p.req.command = cpu_to_be16(cmd);
+ p->p.req.max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
- return (ct_req);
+ return &p->p.req;
}
static int
@@ -188,7 +188,7 @@
GA_NXT_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GA_NXT_CMD,
GA_NXT_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -284,8 +284,7 @@
gid_pt_rsp_size);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD,
- gid_pt_rsp_size);
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GID_PT_CMD, gid_pt_rsp_size);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_type */
@@ -359,7 +358,7 @@
GPN_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GPN_ID_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GPN_ID_CMD,
GPN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -421,7 +420,7 @@
GNN_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GNN_ID_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GNN_ID_CMD,
GNN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -495,7 +494,7 @@
RFT_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD,
RFT_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -551,7 +550,7 @@
RFF_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD,
RFF_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -606,8 +605,7 @@
RNN_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RNN_ID_CMD,
- RNN_ID_RSP_SIZE);
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, node_name */
@@ -676,7 +674,7 @@
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD,
RSNN_NN_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -1262,18 +1260,18 @@
* Returns a pointer to the intitialized @ct_req.
*/
static inline struct ct_sns_req *
-qla2x00_prep_ct_fdmi_req(struct ct_sns_req *ct_req, uint16_t cmd,
+qla2x00_prep_ct_fdmi_req(struct ct_sns_pkt *p, uint16_t cmd,
uint16_t rsp_size)
{
- memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+ memset(p, 0, sizeof(struct ct_sns_pkt));
- ct_req->header.revision = 0x01;
- ct_req->header.gs_type = 0xFA;
- ct_req->header.gs_subtype = 0x10;
- ct_req->command = cpu_to_be16(cmd);
- ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+ p->p.req.header.revision = 0x01;
+ p->p.req.header.gs_type = 0xFA;
+ p->p.req.header.gs_subtype = 0x10;
+ p->p.req.command = cpu_to_be16(cmd);
+ p->p.req.max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
- return ct_req;
+ return &p->p.req;
}
/**
@@ -1301,8 +1299,7 @@
ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RHBA_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD,
- RHBA_RSP_SIZE);
+ ct_req = qla2x00_prep_ct_fdmi_req(ha->ct_sns, RHBA_CMD, RHBA_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- attribute block, attributes. */
@@ -1370,8 +1367,7 @@
/* Model description. */
eiter = (struct ct_fdmi_hba_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL_DESCRIPTION);
- if (ha->model_desc)
- strncpy(eiter->a.model_desc, ha->model_desc, 80);
+ strncpy(eiter->a.model_desc, ha->model_desc, 80);
alen = strlen(eiter->a.model_desc);
alen += (alen & 3) ? (4 - (alen & 3)) : 4;
eiter->len = cpu_to_be16(4 + alen);
@@ -1491,8 +1487,7 @@
DHBA_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, DHBA_CMD,
- DHBA_RSP_SIZE);
+ ct_req = qla2x00_prep_ct_fdmi_req(ha->ct_sns, DHBA_CMD, DHBA_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- portname. */
@@ -1548,8 +1543,7 @@
ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RPA_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD,
- RPA_RSP_SIZE);
+ ct_req = qla2x00_prep_ct_fdmi_req(ha->ct_sns, RPA_CMD, RPA_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- attribute block, attributes. */
@@ -1776,7 +1770,7 @@
GFPN_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFPN_ID_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFPN_ID_CMD,
GFPN_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
@@ -1843,18 +1837,18 @@
static inline struct ct_sns_req *
-qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd,
+qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd,
uint16_t rsp_size)
{
- memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+ memset(p, 0, sizeof(struct ct_sns_pkt));
- ct_req->header.revision = 0x01;
- ct_req->header.gs_type = 0xFA;
- ct_req->header.gs_subtype = 0x01;
- ct_req->command = cpu_to_be16(cmd);
- ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+ p->p.req.header.revision = 0x01;
+ p->p.req.header.gs_type = 0xFA;
+ p->p.req.header.gs_subtype = 0x01;
+ p->p.req.command = cpu_to_be16(cmd);
+ p->p.req.max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
- return ct_req;
+ return &p->p.req;
}
/**
@@ -1890,8 +1884,8 @@
GPSC_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla24xx_prep_ct_fm_req(&ha->ct_sns->p.req,
- GPSC_CMD, GPSC_RSP_SIZE);
+ ct_req = qla24xx_prep_ct_fm_req(ha->ct_sns, GPSC_CMD,
+ GPSC_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_name */
@@ -2001,7 +1995,7 @@
GFF_ID_RSP_SIZE);
/* Prepare CT request */
- ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFF_ID_CMD,
+ ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFF_ID_CMD,
GFF_ID_RSP_SIZE);
ct_rsp = &ha->ct_sns->p.rsp;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 3565dfd..f2216ed 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -2309,14 +2309,6 @@
"Topology - %s, Host Loop address 0x%x.\n",
connect_type, vha->loop_id);
- if (rval) {
- ql_log(ql_log_warn, vha, 0x2011,
- "%s FAILED\n", __func__);
- } else {
- ql_dbg(ql_dbg_disc, vha, 0x2012,
- "%s success\n", __func__);
- }
-
return(rval);
}
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 0a5c895..28c38b4 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -83,7 +83,7 @@
host_to_adap(uint8_t *src, uint8_t *dst, uint32_t bsize)
{
uint32_t *isrc = (uint32_t *) src;
- uint32_t *odest = (uint32_t *) dst;
+ __le32 *odest = (__le32 *) dst;
uint32_t iter = bsize >> 2;
for (; iter ; iter--)
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 15e4080..42ef481 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -1189,7 +1189,6 @@
uint32_t *cur_dsd, *fcp_dl;
scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
- struct scatterlist *cur_seg;
int sgc;
uint32_t total_bytes = 0;
uint32_t data_bytes;
@@ -1396,7 +1395,6 @@
if (bundling && tot_prot_dsds) {
/* Walks dif segments */
- cur_seg = scsi_prot_sglist(cmd);
cmd_pkt->control_flags |=
__constant_cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE);
cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address;
@@ -1863,8 +1861,8 @@
pkt = req->ring_ptr;
memset(pkt, 0, REQUEST_ENTRY_SIZE);
if (IS_QLAFX00(ha)) {
- WRT_REG_BYTE(&pkt->entry_count, req_cnt);
- WRT_REG_WORD(&pkt->handle, handle);
+ WRT_REG_BYTE((void __iomem *)&pkt->entry_count, req_cnt);
+ WRT_REG_WORD((void __iomem *)&pkt->handle, handle);
} else {
pkt->entry_count = req_cnt;
pkt->handle = handle;
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index d2a4c75..2d8e7b8 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -2485,6 +2485,7 @@
if (rval == QLA_SUCCESS)
goto next_test;
+ rval = QLA_SUCCESS;
WRT_REG_DWORD(®->iobase_window, 0x0003);
for (cnt = 100; (RD_REG_DWORD(®->iobase_window) & BIT_0) == 0 &&
rval == QLA_SUCCESS; cnt--) {
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3587ec2..7257c3c 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -177,8 +177,14 @@
WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- wait_for_completion_timeout(&ha->mbx_intr_comp, mcp->tov * HZ);
-
+ if (!wait_for_completion_timeout(&ha->mbx_intr_comp,
+ mcp->tov * HZ)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x117a,
+ "cmd=%x Timeout.\n", command);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
} else {
ql_dbg(ql_dbg_mbx, vha, 0x1011,
"Cmd=%x Polling Mode.\n", command);
@@ -275,9 +281,11 @@
/*
* Attempt to capture a firmware dump for further analysis
- * of the current firmware state
+ * of the current firmware state. We do not need to do this
+ * if we are intentionally generating a dump.
*/
- ha->isp_ops->fw_dump(vha, 0);
+ if (mcp->mb[0] != MBC_GEN_SYSTEM_ERROR)
+ ha->isp_ops->fw_dump(vha, 0);
rval = QLA_FUNCTION_TIMEOUT;
}
diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c
index a6df558..d799379 100644
--- a/drivers/scsi/qla2xxx/qla_mr.c
+++ b/drivers/scsi/qla2xxx/qla_mr.c
@@ -707,7 +707,7 @@
srb_t *sp = (srb_t *)data;
struct srb_iocb *tmf = &sp->u.iocb_cmd;
- tmf->u.tmf.comp_status = CS_TIMEOUT;
+ tmf->u.tmf.comp_status = cpu_to_le16((uint16_t)CS_TIMEOUT);
complete(&tmf->u.tmf.comp);
}
@@ -1418,7 +1418,8 @@
pkt = rsp->ring_ptr;
for (cnt = 0; cnt < rsp->length; cnt++) {
pkt->signature = RESPONSE_PROCESSED;
- WRT_REG_DWORD(&pkt->signature, RESPONSE_PROCESSED);
+ WRT_REG_DWORD((void __iomem *)&pkt->signature,
+ RESPONSE_PROCESSED);
pkt++;
}
}
@@ -1733,7 +1734,7 @@
}
int
-qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t fx_type)
+qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type)
{
srb_t *sp;
struct srb_iocb *fdisc;
@@ -1759,13 +1760,13 @@
fdisc->u.fxiocb.flags =
SRB_FXDISC_RESP_DMA_VALID | SRB_FXDISC_REQ_DWRD_VALID;
fdisc->u.fxiocb.rsp_len = QLAFX00_PORT_DATA_INFO;
- fdisc->u.fxiocb.req_data = fcport->port_id;
+ fdisc->u.fxiocb.req_data = cpu_to_le32(fcport->port_id);
break;
case FXDISC_GET_TGT_NODE_INFO:
fdisc->u.fxiocb.flags =
SRB_FXDISC_RESP_DMA_VALID | SRB_FXDISC_REQ_DWRD_VALID;
fdisc->u.fxiocb.rsp_len = QLAFX00_TGT_NODE_INFO;
- fdisc->u.fxiocb.req_data = fcport->tgt_id;
+ fdisc->u.fxiocb.req_data = cpu_to_le32(fcport->tgt_id);
break;
case FXDISC_GET_TGT_NODE_LIST:
fdisc->u.fxiocb.flags =
@@ -1851,7 +1852,7 @@
sp->name = "fxdisc";
qla2x00_init_timer(sp, FXDISC_TIMEOUT);
fdisc->timeout = qla2x00_fxdisc_iocb_timeout;
- fdisc->u.fxiocb.req_func_type = fx_type;
+ fdisc->u.fxiocb.req_func_type = cpu_to_le16(fx_type);
sp->done = qla2x00_fxdisc_sp_done;
rval = qla2x00_start_sp(sp);
@@ -1904,7 +1905,7 @@
(uint8_t *)pinfo, 16);
memcpy(vha->hw->gid_list, pinfo, QLAFX00_TGT_NODE_LIST_SIZE);
}
- rval = fdisc->u.fxiocb.result;
+ rval = le32_to_cpu(fdisc->u.fxiocb.result);
done_unmap_dma:
if (fdisc->u.fxiocb.rsp_addr)
@@ -1927,7 +1928,7 @@
srb_t *sp = (srb_t *)data;
struct srb_iocb *abt = &sp->u.iocb_cmd;
- abt->u.abt.comp_status = CS_TIMEOUT;
+ abt->u.abt.comp_status = cpu_to_le16((uint16_t)CS_TIMEOUT);
complete(&abt->u.abt.comp);
}
@@ -2169,14 +2170,14 @@
static void
qlafx00_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
struct tsk_mgmt_entry_fx00 *pkt, srb_t *sp,
- uint16_t sstatus, uint16_t cpstatus)
+ __le16 sstatus, __le16 cpstatus)
{
struct srb_iocb *tmf;
tmf = &sp->u.iocb_cmd;
- if (cpstatus != CS_COMPLETE ||
- (sstatus & SS_RESPONSE_INFO_LEN_VALID))
- cpstatus = CS_INCOMPLETE;
+ if (cpstatus != cpu_to_le16((uint16_t)CS_COMPLETE) ||
+ (sstatus & cpu_to_le16((uint16_t)SS_RESPONSE_INFO_LEN_VALID)))
+ cpstatus = cpu_to_le16((uint16_t)CS_INCOMPLETE);
tmf->u.tmf.comp_status = cpstatus;
sp->done(vha, sp, 0);
}
@@ -2194,7 +2195,7 @@
return;
abt = &sp->u.iocb_cmd;
- abt->u.abt.comp_status = le32_to_cpu(pkt->tgt_id_sts);
+ abt->u.abt.comp_status = pkt->tgt_id_sts;
sp->done(vha, sp, 0);
}
@@ -2216,12 +2217,12 @@
if (sp->type == SRB_FXIOCB_DCMD) {
iocb_job = &sp->u.iocb_cmd;
- iocb_job->u.fxiocb.seq_number = le32_to_cpu(pkt->seq_no);
- iocb_job->u.fxiocb.fw_flags = le32_to_cpu(pkt->fw_iotcl_flags);
- iocb_job->u.fxiocb.result = le32_to_cpu(pkt->status);
+ iocb_job->u.fxiocb.seq_number = pkt->seq_no;
+ iocb_job->u.fxiocb.fw_flags = pkt->fw_iotcl_flags;
+ iocb_job->u.fxiocb.result = pkt->status;
if (iocb_job->u.fxiocb.flags & SRB_FXDISC_RSP_DWRD_VALID)
iocb_job->u.fxiocb.req_data =
- le32_to_cpu(pkt->dataword_r);
+ pkt->dataword_r;
} else {
bsg_job = sp->u.bsg_job;
@@ -2275,10 +2276,10 @@
fc_port_t *fcport;
struct scsi_cmnd *cp;
struct sts_entry_fx00 *sts;
- uint16_t comp_status;
- uint16_t scsi_status;
+ __le16 comp_status;
+ __le16 scsi_status;
uint16_t ox_id;
- uint8_t lscsi_status;
+ __le16 lscsi_status;
int32_t resid;
uint32_t sense_len, par_sense_len, rsp_info_len, resid_len,
fw_resid_len;
@@ -2292,8 +2293,8 @@
sts = (struct sts_entry_fx00 *) pkt;
- comp_status = le16_to_cpu(sts->comp_status);
- scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
+ comp_status = sts->comp_status;
+ scsi_status = sts->scsi_status & cpu_to_le16((uint16_t)SS_MASK);
hindex = sts->handle;
handle = LSW(hindex);
@@ -2339,38 +2340,40 @@
return;
}
- lscsi_status = scsi_status & STATUS_MASK;
+ lscsi_status = scsi_status & cpu_to_le16((uint16_t)STATUS_MASK);
fcport = sp->fcport;
ox_id = 0;
sense_len = par_sense_len = rsp_info_len = resid_len =
fw_resid_len = 0;
- if (scsi_status & SS_SENSE_LEN_VALID)
- sense_len = le32_to_cpu(sts->sense_len);
- if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
+ if (scsi_status & cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID))
+ sense_len = sts->sense_len;
+ if (scsi_status & cpu_to_le16(((uint16_t)SS_RESIDUAL_UNDER
+ | (uint16_t)SS_RESIDUAL_OVER)))
resid_len = le32_to_cpu(sts->residual_len);
- if (comp_status == CS_DATA_UNDERRUN)
+ if (comp_status == cpu_to_le16((uint16_t)CS_DATA_UNDERRUN))
fw_resid_len = le32_to_cpu(sts->residual_len);
rsp_info = sense_data = sts->data;
par_sense_len = sizeof(sts->data);
/* Check for overrun. */
if (comp_status == CS_COMPLETE &&
- scsi_status & SS_RESIDUAL_OVER)
- comp_status = CS_DATA_OVERRUN;
+ scsi_status & cpu_to_le16((uint16_t)SS_RESIDUAL_OVER))
+ comp_status = cpu_to_le16((uint16_t)CS_DATA_OVERRUN);
/*
* Based on Host and scsi status generate status code for Linux
*/
- switch (comp_status) {
+ switch (le16_to_cpu(comp_status)) {
case CS_COMPLETE:
case CS_QUEUE_FULL:
if (scsi_status == 0) {
res = DID_OK << 16;
break;
}
- if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) {
+ if (scsi_status & cpu_to_le16(((uint16_t)SS_RESIDUAL_UNDER
+ | (uint16_t)SS_RESIDUAL_OVER))) {
resid = resid_len;
scsi_set_resid(cp, resid);
@@ -2386,19 +2389,20 @@
break;
}
}
- res = DID_OK << 16 | lscsi_status;
+ res = DID_OK << 16 | le16_to_cpu(lscsi_status);
- if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
+ if (lscsi_status ==
+ cpu_to_le16((uint16_t)SAM_STAT_TASK_SET_FULL)) {
ql_dbg(ql_dbg_io, fcport->vha, 0x3051,
"QUEUE FULL detected.\n");
break;
}
logit = 0;
- if (lscsi_status != SS_CHECK_CONDITION)
+ if (lscsi_status != cpu_to_le16((uint16_t)SS_CHECK_CONDITION))
break;
memset(cp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
- if (!(scsi_status & SS_SENSE_LEN_VALID))
+ if (!(scsi_status & cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID)))
break;
qlafx00_handle_sense(sp, sense_data, par_sense_len, sense_len,
@@ -2412,7 +2416,7 @@
else
resid = resid_len;
scsi_set_resid(cp, resid);
- if (scsi_status & SS_RESIDUAL_UNDER) {
+ if (scsi_status & cpu_to_le16((uint16_t)SS_RESIDUAL_UNDER)) {
if ((IS_FWI2_CAPABLE(ha) || IS_QLAFX00(ha))
&& fw_resid_len != resid_len) {
ql_dbg(ql_dbg_io, fcport->vha, 0x3052,
@@ -2420,7 +2424,8 @@
"(0x%x of 0x%x bytes).\n",
resid, scsi_bufflen(cp));
- res = DID_ERROR << 16 | lscsi_status;
+ res = DID_ERROR << 16 |
+ le16_to_cpu(lscsi_status);
goto check_scsi_status;
}
@@ -2436,8 +2441,9 @@
res = DID_ERROR << 16;
break;
}
- } else if (lscsi_status != SAM_STAT_TASK_SET_FULL &&
- lscsi_status != SAM_STAT_BUSY) {
+ } else if (lscsi_status !=
+ cpu_to_le16((uint16_t)SAM_STAT_TASK_SET_FULL) &&
+ lscsi_status != cpu_to_le16((uint16_t)SAM_STAT_BUSY)) {
/*
* scsi status of task set and busy are considered
* to be task not completed.
@@ -2448,7 +2454,7 @@
"of 0x%x bytes).\n", resid,
scsi_bufflen(cp));
- res = DID_ERROR << 16 | lscsi_status;
+ res = DID_ERROR << 16 | le16_to_cpu(lscsi_status);
goto check_scsi_status;
} else {
ql_dbg(ql_dbg_io, fcport->vha, 0x3055,
@@ -2456,7 +2462,7 @@
scsi_status, lscsi_status);
}
- res = DID_OK << 16 | lscsi_status;
+ res = DID_OK << 16 | le16_to_cpu(lscsi_status);
logit = 0;
check_scsi_status:
@@ -2465,17 +2471,20 @@
* Status.
*/
if (lscsi_status != 0) {
- if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
+ if (lscsi_status ==
+ cpu_to_le16((uint16_t)SAM_STAT_TASK_SET_FULL)) {
ql_dbg(ql_dbg_io, fcport->vha, 0x3056,
"QUEUE FULL detected.\n");
logit = 1;
break;
}
- if (lscsi_status != SS_CHECK_CONDITION)
+ if (lscsi_status !=
+ cpu_to_le16((uint16_t)SS_CHECK_CONDITION))
break;
memset(cp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
- if (!(scsi_status & SS_SENSE_LEN_VALID))
+ if (!(scsi_status &
+ cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID)))
break;
qlafx00_handle_sense(sp, sense_data, par_sense_len,
@@ -2629,7 +2638,7 @@
uint32_t handle, hindex, handle_count, i;
uint16_t que;
struct req_que *req;
- uint32_t *handle_ptr;
+ __le32 *handle_ptr;
stsmfx = (struct multi_sts_entry_fx00 *) pkt;
@@ -2643,7 +2652,7 @@
return;
}
- handle_ptr = (uint32_t *) &stsmfx->handles[0];
+ handle_ptr = &stsmfx->handles[0];
for (i = 0; i < handle_count; i++) {
hindex = le32_to_cpu(*handle_ptr);
@@ -2714,10 +2723,11 @@
if (!vha->flags.online)
return;
- while (RD_REG_DWORD(&(rsp->ring_ptr->signature)) !=
+ while (RD_REG_DWORD((void __iomem *)&(rsp->ring_ptr->signature)) !=
RESPONSE_PROCESSED) {
lptr = rsp->ring_ptr;
- memcpy_fromio(rsp->rsp_pkt, lptr, sizeof(rsp->rsp_pkt));
+ memcpy_fromio(rsp->rsp_pkt, (void __iomem *)lptr,
+ sizeof(rsp->rsp_pkt));
pkt = (struct sts_entry_fx00 *)rsp->rsp_pkt;
rsp->ring_index++;
@@ -2768,7 +2778,8 @@
break;
}
next_iter:
- WRT_REG_DWORD(&lptr->signature, RESPONSE_PROCESSED);
+ WRT_REG_DWORD((void __iomem *)&lptr->signature,
+ RESPONSE_PROCESSED);
wmb();
}
@@ -2958,8 +2969,7 @@
cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
/* Load packet defaults. */
- *((uint32_t *)(&lcont_pkt->entry_type)) =
- __constant_cpu_to_le32(CONTINUE_A64_TYPE_FX00);
+ lcont_pkt->entry_type = CONTINUE_A64_TYPE_FX00;
return cont_pkt;
}
@@ -2969,7 +2979,7 @@
uint16_t tot_dsds, struct cmd_type_7_fx00 *lcmd_pkt)
{
uint16_t avail_dsds;
- uint32_t *cur_dsd;
+ __le32 *cur_dsd;
scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
@@ -2986,8 +2996,7 @@
cont_pkt = NULL;
/* Update entry type to indicate Command Type 3 IOCB */
- *((uint32_t *)(&lcmd_pkt->entry_type)) =
- __constant_cpu_to_le32(FX00_COMMAND_TYPE_7);
+ lcmd_pkt->entry_type = FX00_COMMAND_TYPE_7;
/* No data transfer */
if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
@@ -3006,7 +3015,7 @@
/* One DSD is available in the Command Type 3 IOCB */
avail_dsds = 1;
- cur_dsd = (uint32_t *)&lcmd_pkt->dseg_0_address;
+ cur_dsd = (__le32 *)&lcmd_pkt->dseg_0_address;
/* Load data segments */
scsi_for_each_sg(cmd, sg, tot_dsds, i) {
@@ -3021,7 +3030,7 @@
memset(&lcont_pkt, 0, REQUEST_ENTRY_SIZE);
cont_pkt =
qlafx00_prep_cont_type1_iocb(req, &lcont_pkt);
- cur_dsd = (uint32_t *)lcont_pkt.dseg_0_address;
+ cur_dsd = (__le32 *)lcont_pkt.dseg_0_address;
avail_dsds = 5;
cont = 1;
}
@@ -3224,13 +3233,13 @@
tm_iocb.timeout = cpu_to_le16(qla2x00_get_async_timeout(vha) + 2);
tm_iocb.tgt_id = cpu_to_le16(sp->fcport->tgt_id);
tm_iocb.control_flags = cpu_to_le32(fxio->u.tmf.flags);
- if (tm_iocb.control_flags == TCF_LUN_RESET) {
+ if (tm_iocb.control_flags == cpu_to_le32((uint32_t)TCF_LUN_RESET)) {
int_to_scsilun(fxio->u.tmf.lun, &llun);
host_to_adap((uint8_t *)&llun, (uint8_t *)&tm_iocb.lun,
sizeof(struct scsi_lun));
}
- memcpy((void __iomem *)ptm_iocb, &tm_iocb,
+ memcpy((void *)ptm_iocb, &tm_iocb,
sizeof(struct tsk_mgmt_entry_fx00));
wmb();
}
@@ -3252,7 +3261,7 @@
abt_iocb.tgt_id_sts = cpu_to_le16(sp->fcport->tgt_id);
abt_iocb.req_que_no = cpu_to_le16(req->id);
- memcpy((void __iomem *)pabt_iocb, &abt_iocb,
+ memcpy((void *)pabt_iocb, &abt_iocb,
sizeof(struct abort_iocb_entry_fx00));
wmb();
}
@@ -3273,13 +3282,12 @@
if (sp->type == SRB_FXIOCB_DCMD) {
fx_iocb.func_num =
- cpu_to_le16(sp->u.iocb_cmd.u.fxiocb.req_func_type);
- fx_iocb.adapid = cpu_to_le32(fxio->u.fxiocb.adapter_id);
- fx_iocb.adapid_hi = cpu_to_le32(fxio->u.fxiocb.adapter_id_hi);
- fx_iocb.reserved_0 = cpu_to_le32(fxio->u.fxiocb.reserved_0);
- fx_iocb.reserved_1 = cpu_to_le32(fxio->u.fxiocb.reserved_1);
- fx_iocb.dataword_extra =
- cpu_to_le32(fxio->u.fxiocb.req_data_extra);
+ sp->u.iocb_cmd.u.fxiocb.req_func_type;
+ fx_iocb.adapid = fxio->u.fxiocb.adapter_id;
+ fx_iocb.adapid_hi = fxio->u.fxiocb.adapter_id_hi;
+ fx_iocb.reserved_0 = fxio->u.fxiocb.reserved_0;
+ fx_iocb.reserved_1 = fxio->u.fxiocb.reserved_1;
+ fx_iocb.dataword_extra = fxio->u.fxiocb.req_data_extra;
if (fxio->u.fxiocb.flags & SRB_FXDISC_REQ_DMA_VALID) {
fx_iocb.req_dsdcnt = cpu_to_le16(1);
@@ -3306,8 +3314,7 @@
}
if (fxio->u.fxiocb.flags & SRB_FXDISC_REQ_DWRD_VALID) {
- fx_iocb.dataword =
- cpu_to_le32(fxio->u.fxiocb.req_data);
+ fx_iocb.dataword = fxio->u.fxiocb.req_data;
}
fx_iocb.flags = fxio->u.fxiocb.flags;
} else {
@@ -3323,21 +3330,21 @@
fx_iocb.reserved_1 = piocb_rqst->reserved_1;
fx_iocb.dataword_extra = piocb_rqst->dataword_extra;
fx_iocb.dataword = piocb_rqst->dataword;
- fx_iocb.req_xfrcnt = cpu_to_le16(piocb_rqst->req_len);
- fx_iocb.rsp_xfrcnt = cpu_to_le16(piocb_rqst->rsp_len);
+ fx_iocb.req_xfrcnt = piocb_rqst->req_len;
+ fx_iocb.rsp_xfrcnt = piocb_rqst->rsp_len;
if (piocb_rqst->flags & SRB_FXDISC_REQ_DMA_VALID) {
int avail_dsds, tot_dsds;
cont_a64_entry_t lcont_pkt;
cont_a64_entry_t *cont_pkt = NULL;
- uint32_t *cur_dsd;
+ __le32 *cur_dsd;
int index = 0, cont = 0;
fx_iocb.req_dsdcnt =
cpu_to_le16(bsg_job->request_payload.sg_cnt);
tot_dsds =
- cpu_to_le32(bsg_job->request_payload.sg_cnt);
- cur_dsd = (uint32_t *)&fx_iocb.dseg_rq_address[0];
+ bsg_job->request_payload.sg_cnt;
+ cur_dsd = (__le32 *)&fx_iocb.dseg_rq_address[0];
avail_dsds = 1;
for_each_sg(bsg_job->request_payload.sg_list, sg,
tot_dsds, index) {
@@ -3355,7 +3362,7 @@
qlafx00_prep_cont_type1_iocb(
sp->fcport->vha->req,
&lcont_pkt);
- cur_dsd = (uint32_t *)
+ cur_dsd = (__le32 *)
lcont_pkt.dseg_0_address;
avail_dsds = 5;
cont = 1;
@@ -3393,13 +3400,13 @@
int avail_dsds, tot_dsds;
cont_a64_entry_t lcont_pkt;
cont_a64_entry_t *cont_pkt = NULL;
- uint32_t *cur_dsd;
+ __le32 *cur_dsd;
int index = 0, cont = 0;
fx_iocb.rsp_dsdcnt =
cpu_to_le16(bsg_job->reply_payload.sg_cnt);
- tot_dsds = cpu_to_le32(bsg_job->reply_payload.sg_cnt);
- cur_dsd = (uint32_t *)&fx_iocb.dseg_rsp_address[0];
+ tot_dsds = bsg_job->reply_payload.sg_cnt;
+ cur_dsd = (__le32 *)&fx_iocb.dseg_rsp_address[0];
avail_dsds = 1;
for_each_sg(bsg_job->reply_payload.sg_list, sg,
@@ -3418,7 +3425,7 @@
qlafx00_prep_cont_type1_iocb(
sp->fcport->vha->req,
&lcont_pkt);
- cur_dsd = (uint32_t *)
+ cur_dsd = (__le32 *)
lcont_pkt.dseg_0_address;
avail_dsds = 5;
cont = 1;
@@ -3453,7 +3460,7 @@
}
if (piocb_rqst->flags & SRB_FXDISC_REQ_DWRD_VALID)
- fx_iocb.dataword = cpu_to_le32(piocb_rqst->dataword);
+ fx_iocb.dataword = piocb_rqst->dataword;
fx_iocb.flags = piocb_rqst->flags;
fx_iocb.entry_count = entry_cnt;
}
@@ -3462,7 +3469,7 @@
sp->fcport->vha, 0x3047,
(uint8_t *)&fx_iocb, sizeof(struct fxdisc_entry_fx00));
- memcpy((void __iomem *)pfxiocb, &fx_iocb,
+ memcpy((void *)pfxiocb, &fx_iocb,
sizeof(struct fxdisc_entry_fx00));
wmb();
}
diff --git a/drivers/scsi/qla2xxx/qla_mr.h b/drivers/scsi/qla2xxx/qla_mr.h
index cc327dc..1a092af 100644
--- a/drivers/scsi/qla2xxx/qla_mr.h
+++ b/drivers/scsi/qla2xxx/qla_mr.h
@@ -24,10 +24,10 @@
uint32_t handle; /* System handle. */
uint32_t handle_hi;
- uint16_t tgt_idx; /* Target Idx. */
+ __le16 tgt_idx; /* Target Idx. */
uint16_t timeout; /* Command timeout. */
- uint16_t dseg_count; /* Data segment count. */
+ __le16 dseg_count; /* Data segment count. */
uint16_t scsi_rsp_dsd_len;
struct scsi_lun lun; /* LUN (LE). */
@@ -41,7 +41,7 @@
uint8_t crn;
uint8_t fcp_cdb[MAX_CMDSZ]; /* SCSI command words. */
- uint32_t byte_count; /* Total byte count. */
+ __le32 byte_count; /* Total byte count. */
uint32_t dseg_0_address[2]; /* Data segment 0 address. */
uint32_t dseg_0_len; /* Data segment 0 length. */
@@ -81,16 +81,16 @@
uint32_t handle; /* System handle. */
uint32_t handle_hi; /* System handle. */
- uint16_t comp_status; /* Completion status. */
+ __le16 comp_status; /* Completion status. */
uint16_t reserved_0; /* OX_ID used by the firmware. */
- uint32_t residual_len; /* FW calc residual transfer length. */
+ __le32 residual_len; /* FW calc residual transfer length. */
uint16_t reserved_1;
uint16_t state_flags; /* State flags. */
uint16_t reserved_2;
- uint16_t scsi_status; /* SCSI status. */
+ __le16 scsi_status; /* SCSI status. */
uint32_t sense_len; /* FCP SENSE length. */
uint8_t data[32]; /* FCP response/sense information. */
@@ -106,7 +106,7 @@
uint8_t handle_count;
uint8_t entry_status;
- uint32_t handles[MAX_HANDLE_COUNT];
+ __le32 handles[MAX_HANDLE_COUNT];
};
#define TSK_MGMT_IOCB_TYPE_FX00 0x05
@@ -116,21 +116,21 @@
uint8_t sys_define;
uint8_t entry_status; /* Entry Status. */
- uint32_t handle; /* System handle. */
+ __le32 handle; /* System handle. */
uint32_t handle_hi; /* System handle. */
- uint16_t tgt_id; /* Target Idx. */
+ __le16 tgt_id; /* Target Idx. */
uint16_t reserved_1;
uint16_t delay; /* Activity delay in seconds. */
- uint16_t timeout; /* Command timeout. */
+ __le16 timeout; /* Command timeout. */
struct scsi_lun lun; /* LUN (LE). */
- uint32_t control_flags; /* Control Flags. */
+ __le32 control_flags; /* Control Flags. */
uint8_t reserved_2[32];
};
@@ -143,16 +143,16 @@
uint8_t sys_define; /* System defined. */
uint8_t entry_status; /* Entry Status. */
- uint32_t handle; /* System handle. */
- uint32_t handle_hi; /* System handle. */
+ __le32 handle; /* System handle. */
+ __le32 handle_hi; /* System handle. */
- uint16_t tgt_id_sts; /* Completion status. */
- uint16_t options;
+ __le16 tgt_id_sts; /* Completion status. */
+ __le16 options;
- uint32_t abort_handle; /* System handle. */
- uint32_t abort_handle_hi; /* System handle. */
+ __le32 abort_handle; /* System handle. */
+ __le32 abort_handle_hi; /* System handle. */
- uint16_t req_que_no;
+ __le16 req_que_no;
uint8_t reserved_1[38];
};
@@ -167,17 +167,17 @@
uint32_t reserved_0; /* System handle. */
uint16_t comp_func_num;
- uint16_t fw_iotcl_flags;
+ __le16 fw_iotcl_flags;
- uint32_t dataword_r; /* Data word returned */
+ __le32 dataword_r; /* Data word returned */
uint32_t adapid; /* Adapter ID */
uint32_t adapid_hi; /* Adapter ID high */
uint32_t reserved_1;
- uint32_t seq_no;
+ __le32 seq_no;
uint8_t reserved_2[20];
uint32_t residuallen;
- uint32_t status;
+ __le32 status;
};
#define STATUS_CONT_TYPE_FX00 0x04
@@ -189,26 +189,26 @@
uint8_t sys_define; /* System Defined. */
uint8_t entry_status; /* Entry Status. */
- uint32_t handle; /* System handle. */
- uint32_t reserved_0; /* System handle. */
+ __le32 handle; /* System handle. */
+ __le32 reserved_0; /* System handle. */
- uint16_t func_num;
- uint16_t req_xfrcnt;
- uint16_t req_dsdcnt;
- uint16_t rsp_xfrcnt;
- uint16_t rsp_dsdcnt;
+ __le16 func_num;
+ __le16 req_xfrcnt;
+ __le16 req_dsdcnt;
+ __le16 rsp_xfrcnt;
+ __le16 rsp_dsdcnt;
uint8_t flags;
uint8_t reserved_1;
- uint32_t dseg_rq_address[2]; /* Data segment 0 address. */
- uint32_t dseg_rq_len; /* Data segment 0 length. */
- uint32_t dseg_rsp_address[2]; /* Data segment 1 address. */
- uint32_t dseg_rsp_len; /* Data segment 1 length. */
+ __le32 dseg_rq_address[2]; /* Data segment 0 address. */
+ __le32 dseg_rq_len; /* Data segment 0 length. */
+ __le32 dseg_rsp_address[2]; /* Data segment 1 address. */
+ __le32 dseg_rsp_len; /* Data segment 1 length. */
- uint32_t dataword;
- uint32_t adapid;
- uint32_t adapid_hi;
- uint32_t dataword_extra;
+ __le32 dataword;
+ __le32 adapid;
+ __le32 adapid_hi;
+ __le32 dataword_extra;
};
struct qlafx00_tgt_node_info {
@@ -421,43 +421,43 @@
WRT_REG_DWORD((ha)->cregbase + off, val)
struct qla_mt_iocb_rqst_fx00 {
- uint32_t reserved_0;
+ __le32 reserved_0;
- uint16_t func_type;
+ __le16 func_type;
uint8_t flags;
uint8_t reserved_1;
- uint32_t dataword;
+ __le32 dataword;
- uint32_t adapid;
- uint32_t adapid_hi;
+ __le32 adapid;
+ __le32 adapid_hi;
- uint32_t dataword_extra;
+ __le32 dataword_extra;
- uint32_t req_len;
+ __le32 req_len;
- uint32_t rsp_len;
+ __le32 rsp_len;
};
struct qla_mt_iocb_rsp_fx00 {
uint32_t reserved_1;
uint16_t func_type;
- uint16_t ioctl_flags;
+ __le16 ioctl_flags;
- uint32_t ioctl_data;
+ __le32 ioctl_data;
uint32_t adapid;
uint32_t adapid_hi;
uint32_t reserved_2;
- uint32_t seq_number;
+ __le32 seq_number;
uint8_t reserved_3[20];
int32_t res_count;
- uint32_t status;
+ __le32 status;
};
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index ad72c1d..3e21e9f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -104,8 +104,6 @@
"Set to control shifting of command type processing "
"based on total number of SG elements.");
-static void qla2x00_free_device(scsi_qla_host_t *);
-
int ql2xfdmienable=1;
module_param(ql2xfdmienable, int, S_IRUGO);
MODULE_PARM_DESC(ql2xfdmienable,
@@ -237,6 +235,7 @@
static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
static int qla2x00_change_queue_type(struct scsi_device *, int);
+static void qla2x00_free_device(scsi_qla_host_t *);
struct scsi_host_template qla2xxx_driver_template = {
.module = THIS_MODULE,
@@ -2840,8 +2839,7 @@
qla2x00_dfs_setup(base_vha);
ql_log(ql_log_info, base_vha, 0x00fb,
- "QLogic %s - %s.\n",
- ha->model_number, ha->model_desc ? ha->model_desc : "");
+ "QLogic %s - %s.\n", ha->model_number, ha->model_desc);
ql_log(ql_log_info, base_vha, 0x00fc,
"ISP%04X: %s @ %s hdma%c host#=%ld fw=%s.\n",
pdev->device, ha->isp_ops->pci_info_str(base_vha, pci_info),
@@ -2981,14 +2979,12 @@
set_bit(UNLOADING, &base_vha->dpc_flags);
mutex_lock(&ha->vport_lock);
while (ha->cur_vport_count) {
- struct Scsi_Host *scsi_host;
-
spin_lock_irqsave(&ha->vport_slock, flags);
BUG_ON(base_vha->list.next == &ha->vp_list);
/* This assumes first entry in ha->vp_list is always base vha */
vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list);
- scsi_host = scsi_host_get(vha->host);
+ scsi_host_get(vha->host);
spin_unlock_irqrestore(&ha->vport_slock, flags);
mutex_unlock(&ha->vport_lock);
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index fcdc223..83a8f7a 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -544,102 +544,6 @@
return res;
}
-static bool qlt_check_fcport_exist(struct scsi_qla_host *vha,
- struct qla_tgt_sess *sess)
-{
- struct qla_hw_data *ha = vha->hw;
- struct qla_port_24xx_data *pmap24;
- bool res, found = false;
- int rc, i;
- uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */
- uint16_t entries;
- void *pmap;
- int pmap_len;
- fc_port_t *fcport;
- int global_resets;
- unsigned long flags;
-
-retry:
- global_resets = atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count);
-
- rc = qla2x00_get_node_name_list(vha, &pmap, &pmap_len);
- if (rc != QLA_SUCCESS) {
- res = false;
- goto out;
- }
-
- pmap24 = pmap;
- entries = pmap_len/sizeof(*pmap24);
-
- for (i = 0; i < entries; ++i) {
- if (!memcmp(sess->port_name, pmap24[i].port_name, WWN_SIZE)) {
- loop_id = le16_to_cpu(pmap24[i].loop_id);
- found = true;
- break;
- }
- }
-
- kfree(pmap);
-
- if (!found) {
- res = false;
- goto out;
- }
-
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf046,
- "qlt_check_fcport_exist(): loop_id %d", loop_id);
-
- fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
- if (fcport == NULL) {
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf047,
- "qla_target(%d): Allocation of tmp FC port failed",
- vha->vp_idx);
- res = false;
- goto out;
- }
-
- fcport->loop_id = loop_id;
-
- rc = qla2x00_get_port_database(vha, fcport, 0);
- if (rc != QLA_SUCCESS) {
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf048,
- "qla_target(%d): Failed to retrieve fcport "
- "information -- get_port_database() returned %x "
- "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
- res = false;
- goto out_free_fcport;
- }
-
- if (global_resets !=
- atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count)) {
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf002,
- "qla_target(%d): global reset during session discovery"
- " (counter was %d, new %d), retrying",
- vha->vp_idx, global_resets,
- atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count));
- goto retry;
- }
-
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf003,
- "Updating sess %p s_id %x:%x:%x, loop_id %d) to d_id %x:%x:%x, "
- "loop_id %d", sess, sess->s_id.b.domain, sess->s_id.b.al_pa,
- sess->s_id.b.area, sess->loop_id, fcport->d_id.b.domain,
- fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->loop_id);
-
- spin_lock_irqsave(&ha->hardware_lock, flags);
- ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, fcport->loop_id,
- (fcport->flags & FCF_CONF_COMP_SUPPORTED));
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
- res = true;
-
-out_free_fcport:
- kfree(fcport);
-
-out:
- return res;
-}
-
/* ha->hardware_lock supposed to be held on entry */
static void qlt_undelete_sess(struct qla_tgt_sess *sess)
{
@@ -663,43 +567,13 @@
sess = list_entry(tgt->del_sess_list.next, typeof(*sess),
del_list_entry);
if (time_after_eq(jiffies, sess->expires)) {
- bool cancel;
-
qlt_undelete_sess(sess);
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
- cancel = qlt_check_fcport_exist(vha, sess);
-
- if (cancel) {
- if (sess->deleted) {
- /*
- * sess was again deleted while we were
- * discovering it
- */
- spin_lock_irqsave(&ha->hardware_lock,
- flags);
- continue;
- }
-
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf049,
- "qla_target(%d): cancel deletion of "
- "session for port %02x:%02x:%02x:%02x:%02x:"
- "%02x:%02x:%02x (loop ID %d), because "
- " it isn't deleted by firmware",
- vha->vp_idx, sess->port_name[0],
- sess->port_name[1], sess->port_name[2],
- sess->port_name[3], sess->port_name[4],
- sess->port_name[5], sess->port_name[6],
- sess->port_name[7], sess->loop_id);
- } else {
- ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
- "Timeout: sess %p about to be deleted\n",
- sess);
- ha->tgt.tgt_ops->shutdown_sess(sess);
- ha->tgt.tgt_ops->put_sess(sess);
- }
-
- spin_lock_irqsave(&ha->hardware_lock, flags);
+ ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
+ "Timeout: sess %p about to be deleted\n",
+ sess);
+ ha->tgt.tgt_ops->shutdown_sess(sess);
+ ha->tgt.tgt_ops->put_sess(sess);
} else {
schedule_delayed_work(&tgt->sess_del_work,
jiffies - sess->expires);
@@ -884,9 +758,8 @@
sess->loop_id);
sess->local = 0;
}
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
@@ -2706,7 +2579,9 @@
/*
* Drop extra session reference from qla_tgt_handle_cmd_for_atio*(
*/
+ spin_lock_irqsave(&ha->hardware_lock, flags);
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return;
out_term:
@@ -2718,9 +2593,9 @@
spin_lock_irqsave(&ha->hardware_lock, flags);
qlt_send_term_exchange(vha, NULL, &cmd->atio, 1);
kmem_cache_free(qla_tgt_cmd_cachep, cmd);
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (sess)
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
/* ha->hardware_lock supposed to be held on entry */
@@ -4169,16 +4044,16 @@
rc = __qlt_24xx_handle_abts(vha, &prm->abts, sess);
if (rc != 0)
goto out_term;
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return;
out_term:
qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false);
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (sess)
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
static void qlt_tmr_work(struct qla_tgt *tgt,
@@ -4226,16 +4101,16 @@
rc = qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
if (rc != 0)
goto out_term;
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return;
out_term:
qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1);
- spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (sess)
ha->tgt.tgt_ops->put_sess(sess);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
static void qlt_sess_work_fn(struct work_struct *work)
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 66b0b26..a318092 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -703,7 +703,7 @@
return qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status);
}
-static int tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd)
+static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd)
{
struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd,
@@ -735,8 +735,6 @@
* CTIO response packet.
*/
qlt_xmit_tm_rsp(mcmd);
-
- return 0;
}
/* Local pointer to allocated TCM configfs fabric module */
@@ -799,12 +797,14 @@
static void tcm_qla2xxx_put_sess(struct qla_tgt_sess *sess)
{
- tcm_qla2xxx_put_session(sess->se_sess);
+ assert_spin_locked(&sess->vha->hw->hardware_lock);
+ kref_put(&sess->se_sess->sess_kref, tcm_qla2xxx_release_session);
}
static void tcm_qla2xxx_shutdown_sess(struct qla_tgt_sess *sess)
{
- tcm_qla2xxx_shutdown_session(sess->se_sess);
+ assert_spin_locked(&sess->vha->hw->hardware_lock);
+ target_sess_cmd_list_set_waiting(sess->se_sess);
}
static struct se_node_acl *tcm_qla2xxx_make_nodeacl(
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index d055450..cb4fefa 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -258,7 +258,7 @@
static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
static unsigned char * fake_storep; /* ramdisk storage */
-static unsigned char *dif_storep; /* protection info */
+static struct sd_dif_tuple *dif_storep; /* protection info */
static void *map_storep; /* provisioning map */
static unsigned long map_size;
@@ -277,11 +277,6 @@
static struct bus_type pseudo_lld_bus;
-static inline sector_t dif_offset(sector_t sector)
-{
- return sector << 3;
-}
-
static struct device_driver sdebug_driverfs_driver = {
.name = sdebug_proc_name,
.bus = &pseudo_lld_bus,
@@ -1736,6 +1731,50 @@
return ret;
}
+static u16 dif_compute_csum(const void *buf, int len)
+{
+ u16 csum;
+
+ switch (scsi_debug_guard) {
+ case 1:
+ csum = ip_compute_csum(buf, len);
+ break;
+ case 0:
+ csum = cpu_to_be16(crc_t10dif(buf, len));
+ break;
+ }
+ return csum;
+}
+
+static int dif_verify(struct sd_dif_tuple *sdt, const void *data,
+ sector_t sector, u32 ei_lba)
+{
+ u16 csum = dif_compute_csum(data, scsi_debug_sector_size);
+
+ if (sdt->guard_tag != csum) {
+ pr_err("%s: GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n",
+ __func__,
+ (unsigned long)sector,
+ be16_to_cpu(sdt->guard_tag),
+ be16_to_cpu(csum));
+ return 0x01;
+ }
+ if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
+ be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
+ pr_err("%s: REF check failed on sector %lu\n",
+ __func__, (unsigned long)sector);
+ return 0x03;
+ }
+ if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
+ be32_to_cpu(sdt->ref_tag) != ei_lba) {
+ pr_err("%s: REF check failed on sector %lu\n",
+ __func__, (unsigned long)sector);
+ dif_errors++;
+ return 0x03;
+ }
+ return 0;
+}
+
static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
unsigned int sectors, u32 ei_lba)
{
@@ -1748,71 +1787,38 @@
start_sec = do_div(tmp_sec, sdebug_store_sectors);
- sdt = (struct sd_dif_tuple *)(dif_storep + dif_offset(start_sec));
+ sdt = dif_storep + start_sec;
for (i = 0 ; i < sectors ; i++) {
- u16 csum;
+ int ret;
if (sdt[i].app_tag == 0xffff)
continue;
sector = start_sec + i;
- switch (scsi_debug_guard) {
- case 1:
- csum = ip_compute_csum(fake_storep +
- sector * scsi_debug_sector_size,
- scsi_debug_sector_size);
- break;
- case 0:
- csum = crc_t10dif(fake_storep +
- sector * scsi_debug_sector_size,
- scsi_debug_sector_size);
- csum = cpu_to_be16(csum);
- break;
- default:
- BUG();
- }
-
- if (sdt[i].guard_tag != csum) {
- printk(KERN_ERR "%s: GUARD check failed on sector %lu" \
- " rcvd 0x%04x, data 0x%04x\n", __func__,
- (unsigned long)sector,
- be16_to_cpu(sdt[i].guard_tag),
- be16_to_cpu(csum));
+ ret = dif_verify(&sdt[i],
+ fake_storep + sector * scsi_debug_sector_size,
+ sector, ei_lba);
+ if (ret) {
dif_errors++;
- return 0x01;
- }
-
- if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
- be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) {
- printk(KERN_ERR "%s: REF check failed on sector %lu\n",
- __func__, (unsigned long)sector);
- dif_errors++;
- return 0x03;
- }
-
- if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
- be32_to_cpu(sdt[i].ref_tag) != ei_lba) {
- printk(KERN_ERR "%s: REF check failed on sector %lu\n",
- __func__, (unsigned long)sector);
- dif_errors++;
- return 0x03;
+ return ret;
}
ei_lba++;
}
- resid = sectors * 8; /* Bytes of protection data to copy into sgl */
+ /* Bytes of protection data to copy into sgl */
+ resid = sectors * sizeof(*dif_storep);
sector = start_sec;
scsi_for_each_prot_sg(SCpnt, psgl, scsi_prot_sg_count(SCpnt), i) {
int len = min(psgl->length, resid);
paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
- memcpy(paddr, dif_storep + dif_offset(sector), len);
+ memcpy(paddr, dif_storep + sector, len);
- sector += len >> 3;
+ sector += len / sizeof(*dif_storep);
if (sector >= sdebug_store_sectors) {
/* Force wrap */
tmp_sec = sector;
@@ -1910,22 +1916,21 @@
sector_t tmp_sec = start_sec;
sector_t sector;
int ppage_offset;
- unsigned short csum;
sector = do_div(tmp_sec, sdebug_store_sectors);
BUG_ON(scsi_sg_count(SCpnt) == 0);
BUG_ON(scsi_prot_sg_count(SCpnt) == 0);
- paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
ppage_offset = 0;
/* For each data page */
scsi_for_each_sg(SCpnt, dsgl, scsi_sg_count(SCpnt), i) {
daddr = kmap_atomic(sg_page(dsgl)) + dsgl->offset;
+ paddr = kmap_atomic(sg_page(psgl)) + psgl->offset;
/* For each sector-sized chunk in data page */
- for (j = 0 ; j < dsgl->length ; j += scsi_debug_sector_size) {
+ for (j = 0; j < dsgl->length; j += scsi_debug_sector_size) {
/* If we're at the end of the current
* protection page advance to the next one
@@ -1941,51 +1946,9 @@
sdt = paddr + ppage_offset;
- switch (scsi_debug_guard) {
- case 1:
- csum = ip_compute_csum(daddr,
- scsi_debug_sector_size);
- break;
- case 0:
- csum = cpu_to_be16(crc_t10dif(daddr,
- scsi_debug_sector_size));
- break;
- default:
- BUG();
- ret = 0;
- goto out;
- }
-
- if (sdt->guard_tag != csum) {
- printk(KERN_ERR
- "%s: GUARD check failed on sector %lu " \
- "rcvd 0x%04x, calculated 0x%04x\n",
- __func__, (unsigned long)sector,
- be16_to_cpu(sdt->guard_tag),
- be16_to_cpu(csum));
- ret = 0x01;
- dump_sector(daddr, scsi_debug_sector_size);
- goto out;
- }
-
- if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
- be32_to_cpu(sdt->ref_tag)
- != (start_sec & 0xffffffff)) {
- printk(KERN_ERR
- "%s: REF check failed on sector %lu\n",
- __func__, (unsigned long)sector);
- ret = 0x03;
- dump_sector(daddr, scsi_debug_sector_size);
- goto out;
- }
-
- if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
- be32_to_cpu(sdt->ref_tag) != ei_lba) {
- printk(KERN_ERR
- "%s: REF check failed on sector %lu\n",
- __func__, (unsigned long)sector);
- ret = 0x03;
- dump_sector(daddr, scsi_debug_sector_size);
+ ret = dif_verify(sdt, daddr + j, start_sec, ei_lba);
+ if (ret) {
+ dump_sector(daddr + j, scsi_debug_sector_size);
goto out;
}
@@ -1994,7 +1957,7 @@
* correctness we need to verify each sector
* before writing it to "stable" storage
*/
- memcpy(dif_storep + dif_offset(sector), sdt, 8);
+ memcpy(dif_storep + sector, sdt, sizeof(*sdt));
sector++;
@@ -2003,23 +1966,21 @@
start_sec++;
ei_lba++;
- daddr += scsi_debug_sector_size;
ppage_offset += sizeof(struct sd_dif_tuple);
}
+ kunmap_atomic(paddr);
kunmap_atomic(daddr);
}
- kunmap_atomic(paddr);
-
dix_writes++;
return 0;
out:
dif_errors++;
- kunmap_atomic(daddr);
kunmap_atomic(paddr);
+ kunmap_atomic(daddr);
return ret;
}
@@ -2092,6 +2053,11 @@
scsi_debug_sector_size *
scsi_debug_unmap_granularity);
}
+ if (dif_storep) {
+ memset(dif_storep + lba, 0xff,
+ sizeof(*dif_storep) *
+ scsi_debug_unmap_granularity);
+ }
}
lba = map_index_to_lba(index + 1);
}
@@ -3400,7 +3366,7 @@
if (scsi_debug_num_parts > 0)
sdebug_build_parts(fake_storep, sz);
- if (scsi_debug_dif) {
+ if (scsi_debug_dix) {
int dif_size;
dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple);
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 86d5220..124392f 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -434,6 +434,8 @@
list_splice_init(&shost->starved_list, &starved_list);
while (!list_empty(&starved_list)) {
+ struct request_queue *slq;
+
/*
* As long as shost is accepting commands and we have
* starved queues, call blk_run_queue. scsi_request_fn
@@ -456,11 +458,25 @@
continue;
}
- spin_unlock(shost->host_lock);
- spin_lock(sdev->request_queue->queue_lock);
- __blk_run_queue(sdev->request_queue);
- spin_unlock(sdev->request_queue->queue_lock);
- spin_lock(shost->host_lock);
+ /*
+ * Once we drop the host lock, a racing scsi_remove_device()
+ * call may remove the sdev from the starved list and destroy
+ * it and the queue. Mitigate by taking a reference to the
+ * queue and never touching the sdev again after we drop the
+ * host lock. Note: if __scsi_remove_device() invokes
+ * blk_cleanup_queue() before the queue is run from this
+ * function then blk_run_queue() will return immediately since
+ * blk_cleanup_queue() marks the queue with QUEUE_FLAG_DYING.
+ */
+ slq = sdev->request_queue;
+ if (!blk_get_queue(slq))
+ continue;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ blk_run_queue(slq);
+ blk_put_queue(slq);
+
+ spin_lock_irqsave(shost->host_lock, flags);
}
/* put any unprocessed entries back */
list_splice(&starved_list, &shost->starved_list);
@@ -2177,6 +2193,7 @@
case SDEV_OFFLINE:
case SDEV_TRANSPORT_OFFLINE:
case SDEV_CANCEL:
+ case SDEV_CREATED_BLOCK:
break;
default:
goto illegal;
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 9451989..83ec1aa 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -326,7 +326,9 @@
*/
static int storvsc_timeout = 180;
-#define STORVSC_MAX_IO_REQUESTS 128
+#define STORVSC_MAX_IO_REQUESTS 200
+
+static void storvsc_on_channel_callback(void *context);
/*
* In Hyper-V, each port/path/target maps to 1 scsi host adapter. In
@@ -364,6 +366,7 @@
bool destroy;
bool drain_notify;
+ bool open_sub_channel;
atomic_t num_outstanding_req;
struct Scsi_Host *host;
@@ -755,12 +758,104 @@
return total_copied;
}
+static void handle_sc_creation(struct vmbus_channel *new_sc)
+{
+ struct hv_device *device = new_sc->primary_channel->device_obj;
+ struct storvsc_device *stor_device;
+ struct vmstorage_channel_properties props;
+
+ stor_device = get_out_stor_device(device);
+ if (!stor_device)
+ return;
+
+ if (stor_device->open_sub_channel == false)
+ return;
+
+ memset(&props, 0, sizeof(struct vmstorage_channel_properties));
+
+ vmbus_open(new_sc,
+ storvsc_ringbuffer_size,
+ storvsc_ringbuffer_size,
+ (void *)&props,
+ sizeof(struct vmstorage_channel_properties),
+ storvsc_on_channel_callback, new_sc);
+}
+
+static void handle_multichannel_storage(struct hv_device *device, int max_chns)
+{
+ struct storvsc_device *stor_device;
+ int num_cpus = num_online_cpus();
+ int num_sc;
+ struct storvsc_cmd_request *request;
+ struct vstor_packet *vstor_packet;
+ int ret, t;
+
+ num_sc = ((max_chns > num_cpus) ? num_cpus : max_chns);
+ stor_device = get_out_stor_device(device);
+ if (!stor_device)
+ return;
+
+ request = &stor_device->init_request;
+ vstor_packet = &request->vstor_packet;
+
+ stor_device->open_sub_channel = true;
+ /*
+ * Establish a handler for dealing with subchannels.
+ */
+ vmbus_set_sc_create_callback(device->channel, handle_sc_creation);
+
+ /*
+ * Check to see if sub-channels have already been created. This
+ * can happen when this driver is re-loaded after unloading.
+ */
+
+ if (vmbus_are_subchannels_present(device->channel))
+ return;
+
+ stor_device->open_sub_channel = false;
+ /*
+ * Request the host to create sub-channels.
+ */
+ memset(request, 0, sizeof(struct storvsc_cmd_request));
+ init_completion(&request->wait_event);
+ vstor_packet->operation = VSTOR_OPERATION_CREATE_SUB_CHANNELS;
+ vstor_packet->flags = REQUEST_COMPLETION_FLAG;
+ vstor_packet->sub_channel_count = num_sc;
+
+ ret = vmbus_sendpacket(device->channel, vstor_packet,
+ (sizeof(struct vstor_packet) -
+ vmscsi_size_delta),
+ (unsigned long)request,
+ VM_PKT_DATA_INBAND,
+ VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+ if (ret != 0)
+ return;
+
+ t = wait_for_completion_timeout(&request->wait_event, 10*HZ);
+ if (t == 0)
+ return;
+
+ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
+ vstor_packet->status != 0)
+ return;
+
+ /*
+ * Now that we created the sub-channels, invoke the check; this
+ * may trigger the callback.
+ */
+ stor_device->open_sub_channel = true;
+ vmbus_are_subchannels_present(device->channel);
+}
+
static int storvsc_channel_init(struct hv_device *device)
{
struct storvsc_device *stor_device;
struct storvsc_cmd_request *request;
struct vstor_packet *vstor_packet;
int ret, t;
+ int max_chns;
+ bool process_sub_channels = false;
stor_device = get_out_stor_device(device);
if (!stor_device)
@@ -855,6 +950,19 @@
vstor_packet->status != 0)
goto cleanup;
+ /*
+ * Check to see if multi-channel support is there.
+ * Hosts that implement protocol version of 5.1 and above
+ * support multi-channel.
+ */
+ max_chns = vstor_packet->storage_channel_properties.max_channel_cnt;
+ if ((vmbus_proto_version != VERSION_WIN7) &&
+ (vmbus_proto_version != VERSION_WS2008)) {
+ if (vstor_packet->storage_channel_properties.flags &
+ STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL)
+ process_sub_channels = true;
+ }
+
memset(vstor_packet, 0, sizeof(struct vstor_packet));
vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
vstor_packet->flags = REQUEST_COMPLETION_FLAG;
@@ -879,6 +987,9 @@
vstor_packet->status != 0)
goto cleanup;
+ if (process_sub_channels)
+ handle_multichannel_storage(device, max_chns);
+
cleanup:
return ret;
@@ -1100,7 +1211,8 @@
static void storvsc_on_channel_callback(void *context)
{
- struct hv_device *device = (struct hv_device *)context;
+ struct vmbus_channel *channel = (struct vmbus_channel *)context;
+ struct hv_device *device;
struct storvsc_device *stor_device;
u32 bytes_recvd;
u64 request_id;
@@ -1108,13 +1220,17 @@
struct storvsc_cmd_request *request;
int ret;
+ if (channel->primary_channel != NULL)
+ device = channel->primary_channel->device_obj;
+ else
+ device = channel->device_obj;
stor_device = get_in_stor_device(device);
if (!stor_device)
return;
do {
- ret = vmbus_recvpacket(device->channel, packet,
+ ret = vmbus_recvpacket(channel, packet,
ALIGN((sizeof(struct vstor_packet) -
vmscsi_size_delta), 8),
&bytes_recvd, &request_id);
@@ -1155,7 +1271,7 @@
ring_size,
(void *)&props,
sizeof(struct vmstorage_channel_properties),
- storvsc_on_channel_callback, device);
+ storvsc_on_channel_callback, device->channel);
if (ret != 0)
return ret;
@@ -1207,6 +1323,7 @@
{
struct storvsc_device *stor_device;
struct vstor_packet *vstor_packet;
+ struct vmbus_channel *outgoing_channel;
int ret = 0;
vstor_packet = &request->vstor_packet;
@@ -1217,6 +1334,11 @@
request->device = device;
+ /*
+ * Select an an appropriate channel to send the request out.
+ */
+
+ outgoing_channel = vmbus_get_outgoing_channel(device->channel);
vstor_packet->flags |= REQUEST_COMPLETION_FLAG;
@@ -1234,7 +1356,7 @@
vstor_packet->operation = VSTOR_OPERATION_EXECUTE_SRB;
if (request->data_buffer.len) {
- ret = vmbus_sendpacket_multipagebuffer(device->channel,
+ ret = vmbus_sendpacket_multipagebuffer(outgoing_channel,
&request->data_buffer,
vstor_packet,
(sizeof(struct vstor_packet) -
@@ -1580,6 +1702,7 @@
enum {
SCSI_GUID,
IDE_GUID,
+ SFC_GUID,
};
static const struct hv_vmbus_device_id id_table[] = {
@@ -1591,6 +1714,11 @@
{ HV_IDE_GUID,
.driver_data = IDE_GUID
},
+ /* Fibre Channel GUID */
+ {
+ HV_SYNTHFC_GUID,
+ .driver_data = SFC_GUID
+ },
{ },
};
@@ -1643,6 +1771,7 @@
}
stor_device->destroy = false;
+ stor_device->open_sub_channel = false;
init_waitqueue_head(&stor_device->waiting_to_drain);
stor_device->device = device;
stor_device->host = host;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 10f99f4..89cbbab 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -266,7 +266,7 @@
config SPI_OCTEON
tristate "Cavium OCTEON SPI controller"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
help
SPI host driver for the hardware found on some Cavium OCTEON
SOCs.
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 5ff3a4f..36171fd 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -144,7 +144,7 @@
# Assumption: We are on embedded, if we compile the MIPS core.
config SSB_EMBEDDED
bool
- depends on SSB_DRIVER_MIPS
+ depends on SSB_DRIVER_MIPS && SSB_PCICORE_HOSTMODE
default y
config SSB_DRIVER_EXTIF
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index f64b662..3227ebe 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -120,8 +120,6 @@
source "drivers/staging/csr/Kconfig"
-source "drivers/staging/ti-soc-thermal/Kconfig"
-
source "drivers/staging/silicom/Kconfig"
source "drivers/staging/ced1401/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 1fb58a1..4d79ebe 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -53,7 +53,6 @@
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
obj-$(CONFIG_CSR_WIFI) += csr/
-obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
obj-$(CONFIG_CED1401) += ced1401/
obj-$(CONFIG_DRM_IMX) += imx-drm/
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
index 05673ed..766a071 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
@@ -1751,10 +1751,10 @@
*/
void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe)
{
- /* cleanup entity */
- media_entity_cleanup(&vpfe_ipipe->subdev.entity);
/* unregister subdev */
v4l2_device_unregister_subdev(&vpfe_ipipe->subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&vpfe_ipipe->subdev.entity);
}
/*
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
index b2f4ef8..59540cd 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
@@ -947,10 +947,10 @@
/* unregister video device */
vpfe_video_unregister(&ipipeif->video_in);
- /* cleanup entity */
- media_entity_cleanup(&ipipeif->subdev.entity);
/* unregister subdev */
v4l2_device_unregister_subdev(&ipipeif->subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&ipipeif->subdev.entity);
}
int
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c
index 5829360f..ff48fce 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_isif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c
@@ -1750,10 +1750,10 @@
void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif)
{
vpfe_video_unregister(&isif->video_out);
- /* cleanup entity */
- media_entity_cleanup(&isif->subdev.entity);
/* unregister subdev */
v4l2_device_unregister_subdev(&isif->subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&isif->subdev.entity);
}
static void isif_restore_defaults(struct vpfe_isif_device *isif)
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
index 126f84c..8e13bd4 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -1777,14 +1777,14 @@
vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out);
vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out);
- /* cleanup entity */
- media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity);
- media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity);
- media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity);
/* unregister subdev */
v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev);
v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev);
v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity);
+ media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity);
+ media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity);
}
/*
@@ -1865,12 +1865,12 @@
vpfe_video_unregister(&resizer->resizer_b.video_out);
out_video_out2_register:
vpfe_video_unregister(&resizer->resizer_a.video_out);
- media_entity_cleanup(&resizer->crop_resizer.subdev.entity);
- media_entity_cleanup(&resizer->resizer_a.subdev.entity);
- media_entity_cleanup(&resizer->resizer_b.subdev.entity);
v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev);
v4l2_device_unregister_subdev(&resizer->resizer_a.subdev);
v4l2_device_unregister_subdev(&resizer->resizer_b.subdev);
+ media_entity_cleanup(&resizer->crop_resizer.subdev.entity);
+ media_entity_cleanup(&resizer->resizer_a.subdev.entity);
+ media_entity_cleanup(&resizer->resizer_b.subdev.entity);
return ret;
}
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index ba913f1..24d98a6 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -39,7 +39,7 @@
struct vpfe_device *vpfe_dev = video->vpfe_dev;
struct media_pad *remote;
- remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]);
+ remote = media_entity_remote_pad(&vpfe_dev->vpfe_isif.pads[0]);
if (remote == NULL) {
pr_err("Invalid media connection to isif/ccdc\n");
return NULL;
@@ -56,7 +56,7 @@
struct media_pad *remote;
int i;
- remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]);
+ remote = media_entity_remote_pad(&vpfe_dev->vpfe_isif.pads[0]);
if (remote == NULL) {
pr_err("Invalid media connection to isif/ccdc\n");
return -EINVAL;
@@ -89,7 +89,7 @@
static struct v4l2_subdev *
vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad)
{
- struct media_pad *remote = media_entity_remote_source(&video->pad);
+ struct media_pad *remote = media_entity_remote_pad(&video->pad);
if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
return NULL;
@@ -114,7 +114,7 @@
return -EINVAL;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- remote = media_entity_remote_source(&video->pad);
+ remote = media_entity_remote_pad(&video->pad);
fmt.pad = remote->index;
ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
@@ -245,7 +245,7 @@
return -EPIPE;
/* Retrieve the source format */
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -667,7 +667,7 @@
return -EINVAL;
}
/* get the remote pad */
- remote = media_entity_remote_source(&video->pad);
+ remote = media_entity_remote_pad(&video->pad);
if (remote == NULL) {
v4l2_err(&vpfe_dev->v4l2_dev,
"invalid remote pad for video node\n");
@@ -1614,7 +1614,7 @@
void vpfe_video_unregister(struct vpfe_video_device *video)
{
if (video_is_registered(&video->video_dev)) {
- media_entity_cleanup(&video->video_dev.entity);
video_unregister_device(&video->video_dev);
+ media_entity_cleanup(&video->video_dev.entity);
}
}
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index c32e0ac..90d6ac4 100644
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -829,7 +829,6 @@
.minor = -1,
.release = video_device_release,
.tvnorms = DT3155_CURRENT_NORM,
- .current_norm = DT3155_CURRENT_NORM,
};
/* same as in drivers/base/dma-coherent.c */
diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c
index 50066e0..46ed832 100644
--- a/drivers/staging/media/go7007/go7007-usb.c
+++ b/drivers/staging/media/go7007/go7007-usb.c
@@ -1124,7 +1124,7 @@
case GO7007_BOARDID_LIFEVIEW_LR192:
printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra "
"is not supported. Sorry!\n");
- return 0;
+ return -ENODEV;
name = "Lifeview TV Walker Ultra";
board = &board_lifeview_lr192;
break;
@@ -1140,7 +1140,7 @@
default:
printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
(unsigned int)id->driver_info);
- return 0;
+ return -ENODEV;
}
go = go7007_alloc(&board->main_info, &intf->dev);
diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c
index 0a2c45d..4afa7da 100644
--- a/drivers/staging/media/lirc/lirc_imon.c
+++ b/drivers/staging/media/lirc/lirc_imon.c
@@ -911,8 +911,8 @@
if (retval) {
dev_err(dev, "%s: usb_submit_urb failed for intf0 (%d)\n",
__func__, retval);
- mutex_unlock(&context->ctx_lock);
- goto exit;
+ alloc_status = 8;
+ goto unlock;
}
usb_set_intfdata(interface, context);
@@ -937,6 +937,8 @@
alloc_status_switch:
switch (alloc_status) {
+ case 8:
+ lirc_unregister_driver(driver->minor);
case 7:
usb_free_urb(tx_urb);
case 6:
@@ -959,7 +961,6 @@
retval = 0;
}
-exit:
mutex_unlock(&driver_lock);
return retval;
diff --git a/drivers/staging/media/solo6x10/solo6x10-tw28.c b/drivers/staging/media/solo6x10/solo6x10-tw28.c
index ad00e2b..af65ea6 100644
--- a/drivers/staging/media/solo6x10/solo6x10-tw28.c
+++ b/drivers/staging/media/solo6x10/solo6x10-tw28.c
@@ -513,62 +513,82 @@
#define FIRST_ACTIVE_LINE 0x0008
#define LAST_ACTIVE_LINE 0x0102
-static void saa7128_setup(struct solo_dev *solo_dev)
+static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals,
+ int start, int n)
{
- int i;
- unsigned char regs[128] = {
- 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+ for (;start < n; start++, vals++) {
+ /* Skip read-only registers */
+ switch (start) {
+ /* case 0x00 ... 0x25: */
+ case 0x2e ... 0x37:
+ case 0x60:
+ case 0x7d:
+ continue;
+ }
+ solo_i2c_writebyte(dev, SOLO_I2C_SAA, 0x46, start, *vals);
+ }
+}
+
+#define SAA712x_reg7c (0x80 | ((LAST_ACTIVE_LINE & 0x100) >> 2) \
+ | ((FIRST_ACTIVE_LINE & 0x100) >> 4))
+
+static void saa712x_setup(struct solo_dev *dev)
+{
+ const int reg_start = 0x26;
+ const uint8_t saa7128_regs_ntsc[] = {
+ /* :0x26 */
+ 0x0d, 0x00,
+ /* :0x28 */
+ 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
+ /* :0x2e XXX: read-only */
+ 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00,
- 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00,
- 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
+ /* :0x38 */
0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* :0x40 */
0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
+ /* :0x50 */
0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e,
+ /* :0x60 */
0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77,
- 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00,
+ 0x41, 0x88, 0x41, 0x52, 0xed, 0x10, 0x10, 0x00,
+ /* :0x70 */
0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff,
+ 0x00, 0x00, FIRST_ACTIVE_LINE, LAST_ACTIVE_LINE & 0xff,
+ SAA712x_reg7c, 0x00, 0xff, 0xff,
+ }, saa7128_regs_pal[] = {
+ /* :0x26 */
+ 0x0d, 0x00,
+ /* :0x28 */
+ 0xe1, 0x1d, 0x75, 0x3f, 0x06, 0x3f,
+ /* :0x2e XXX: read-only */
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* :0x38 */
+ 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* :0x40 */
+ 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18,
+ 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f,
+ /* :0x50 */
+ 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06,
+ 0x02, 0x80, 0x0f, 0x77, 0xa7, 0x67, 0x66, 0x2e,
+ /* :0x60 */
+ 0x7b, 0x02, 0x35, 0xcb, 0x8a, 0x09, 0x2a, 0x77,
+ 0x41, 0x88, 0x41, 0x52, 0xf1, 0x10, 0x20, 0x00,
+ /* :0x70 */
+ 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x12, 0x30,
+ SAA712x_reg7c | 0x40, 0x00, 0xff, 0xff,
};
- regs[0x7A] = FIRST_ACTIVE_LINE & 0xff;
- regs[0x7B] = LAST_ACTIVE_LINE & 0xff;
- regs[0x7C] = ((1 << 7) |
- (((LAST_ACTIVE_LINE >> 8) & 1) << 6) |
- (((FIRST_ACTIVE_LINE >> 8) & 1) << 4));
-
- /* PAL: XXX: We could do a second set of regs to avoid this */
- if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) {
- regs[0x28] = 0xE1;
-
- regs[0x5A] = 0x0F;
- regs[0x61] = 0x02;
- regs[0x62] = 0x35;
- regs[0x63] = 0xCB;
- regs[0x64] = 0x8A;
- regs[0x65] = 0x09;
- regs[0x66] = 0x2A;
-
- regs[0x6C] = 0xf1;
- regs[0x6E] = 0x20;
-
- regs[0x7A] = 0x06 + 12;
- regs[0x7b] = 0x24 + 12;
- regs[0x7c] |= 1 << 6;
- }
-
- /* First 0x25 bytes are read-only? */
- for (i = 0x26; i < 128; i++) {
- if (i == 0x60 || i == 0x7D)
- continue;
- solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]);
- }
-
- return;
+ if (dev->video_type == SOLO_VO_FMT_TYPE_PAL)
+ saa712x_write_regs(dev, saa7128_regs_pal, reg_start,
+ sizeof(saa7128_regs_pal));
+ else
+ saa712x_write_regs(dev, saa7128_regs_ntsc, reg_start,
+ sizeof(saa7128_regs_ntsc));
}
int solo_tw28_init(struct solo_dev *solo_dev)
@@ -609,7 +629,7 @@
return -EINVAL;
}
- saa7128_setup(solo_dev);
+ saa712x_setup(solo_dev);
for (i = 0; i < solo_dev->tw28_cnt; i++) {
if ((solo_dev->tw2865 & (1 << i)))
diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
index 98e2902..a4c5896 100644
--- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
+++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c
@@ -996,12 +996,11 @@
struct v4l2_streamparm *sp)
{
struct solo_enc_dev *solo_enc = video_drvdata(file);
- struct solo_dev *solo_dev = solo_enc->solo_dev;
struct v4l2_captureparm *cp = &sp->parm.capture;
cp->capability = V4L2_CAP_TIMEPERFRAME;
cp->timeperframe.numerator = solo_enc->interval;
- cp->timeperframe.denominator = solo_dev->fps;
+ cp->timeperframe.denominator = solo_enc->solo_dev->fps;
cp->capturemode = 0;
/* XXX: Shouldn't we be able to get/set this from videobuf? */
cp->readbuffers = 2;
@@ -1009,36 +1008,29 @@
return 0;
}
+static inline int calc_interval(u8 fps, u32 n, u32 d)
+{
+ if (!n || !d)
+ return 1;
+ if (d == fps)
+ return n;
+ n *= fps;
+ return min(15U, n / d + (n % d >= (fps >> 1)));
+}
+
static int solo_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *sp)
{
struct solo_enc_dev *solo_enc = video_drvdata(file);
- struct solo_dev *solo_dev = solo_enc->solo_dev;
- struct v4l2_captureparm *cp = &sp->parm.capture;
+ struct v4l2_fract *t = &sp->parm.capture.timeperframe;
+ u8 fps = solo_enc->solo_dev->fps;
if (vb2_is_streaming(&solo_enc->vidq))
return -EBUSY;
- if ((cp->timeperframe.numerator == 0) ||
- (cp->timeperframe.denominator == 0)) {
- /* reset framerate */
- cp->timeperframe.numerator = 1;
- cp->timeperframe.denominator = solo_dev->fps;
- }
-
- if (cp->timeperframe.denominator != solo_dev->fps)
- cp->timeperframe.denominator = solo_dev->fps;
-
- if (cp->timeperframe.numerator > 15)
- cp->timeperframe.numerator = 15;
-
- solo_enc->interval = cp->timeperframe.numerator;
-
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->readbuffers = 2;
-
+ solo_enc->interval = calc_interval(fps, t->numerator, t->denominator);
solo_update_mode(solo_enc);
- return 0;
+ return solo_g_parm(file, priv, sp);
}
static long solo_enc_default(struct file *file, void *fh,
diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig
index 9493128..6e1d5f8 100644
--- a/drivers/staging/octeon/Kconfig
+++ b/drivers/staging/octeon/Kconfig
@@ -1,6 +1,6 @@
config OCTEON_ETHERNET
tristate "Cavium Networks Octeon Ethernet support"
- depends on CPU_CAVIUM_OCTEON && NETDEVICES
+ depends on CAVIUM_OCTEON_SOC && NETDEVICES
select PHYLIB
select MDIO_OCTEON
help
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index d7705e5..f73da43 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -628,25 +628,18 @@
}
static int iscsit_add_reject(
+ struct iscsi_conn *conn,
u8 reason,
- int fail_conn,
- unsigned char *buf,
- struct iscsi_conn *conn)
+ unsigned char *buf)
{
struct iscsi_cmd *cmd;
- struct iscsi_reject *hdr;
- int ret;
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
if (!cmd)
return -1;
cmd->iscsi_opcode = ISCSI_OP_REJECT;
- if (fail_conn)
- cmd->cmd_flags |= ICF_REJECT_FAIL_CONN;
-
- hdr = (struct iscsi_reject *) cmd->pdu;
- hdr->reason = reason;
+ cmd->reject_reason = reason;
cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
if (!cmd->buf_ptr) {
@@ -662,23 +655,16 @@
cmd->i_state = ISTATE_SEND_REJECT;
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
- ret = wait_for_completion_interruptible(&cmd->reject_comp);
- if (ret != 0)
- return -1;
-
- return (!fail_conn) ? 0 : -1;
+ return -1;
}
-int iscsit_add_reject_from_cmd(
+static int iscsit_add_reject_from_cmd(
+ struct iscsi_cmd *cmd,
u8 reason,
- int fail_conn,
- int add_to_conn,
- unsigned char *buf,
- struct iscsi_cmd *cmd)
+ bool add_to_conn,
+ unsigned char *buf)
{
struct iscsi_conn *conn;
- struct iscsi_reject *hdr;
- int ret;
if (!cmd->conn) {
pr_err("cmd->conn is NULL for ITT: 0x%08x\n",
@@ -688,11 +674,7 @@
conn = cmd->conn;
cmd->iscsi_opcode = ISCSI_OP_REJECT;
- if (fail_conn)
- cmd->cmd_flags |= ICF_REJECT_FAIL_CONN;
-
- hdr = (struct iscsi_reject *) cmd->pdu;
- hdr->reason = reason;
+ cmd->reject_reason = reason;
cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
if (!cmd->buf_ptr) {
@@ -709,8 +691,6 @@
cmd->i_state = ISTATE_SEND_REJECT;
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
-
- ret = wait_for_completion_interruptible(&cmd->reject_comp);
/*
* Perform the kref_put now if se_cmd has already been setup by
* scsit_setup_scsi_cmd()
@@ -719,12 +699,19 @@
pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n");
target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
}
- if (ret != 0)
- return -1;
-
- return (!fail_conn) ? 0 : -1;
+ return -1;
}
-EXPORT_SYMBOL(iscsit_add_reject_from_cmd);
+
+static int iscsit_add_reject_cmd(struct iscsi_cmd *cmd, u8 reason,
+ unsigned char *buf)
+{
+ return iscsit_add_reject_from_cmd(cmd, reason, true, buf);
+}
+
+int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8 reason, unsigned char *buf)
+{
+ return iscsit_add_reject_from_cmd(cmd, reason, false, buf);
+}
/*
* Map some portion of the allocated scatterlist to an iovec, suitable for
@@ -844,8 +831,8 @@
!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL"
" not set. Bad iSCSI Initiator.\n");
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (((hdr->flags & ISCSI_FLAG_CMD_READ) ||
@@ -865,8 +852,8 @@
pr_err("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE"
" set when Expected Data Transfer Length is 0 for"
" CDB: 0x%02x. Bad iSCSI Initiator.\n", hdr->cdb[0]);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
done:
@@ -875,62 +862,62 @@
pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE"
" MUST be set if Expected Data Transfer Length is not 0."
" Bad iSCSI Initiator\n");
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&
(hdr->flags & ISCSI_FLAG_CMD_WRITE)) {
pr_err("Bidirectional operations not supported!\n");
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
pr_err("Illegally set Immediate Bit in iSCSI Initiator"
" Scsi Command PDU.\n");
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (payload_length && !conn->sess->sess_ops->ImmediateData) {
pr_err("ImmediateData=No but DataSegmentLength=%u,"
" protocol error.\n", payload_length);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
}
- if ((be32_to_cpu(hdr->data_length )== payload_length) &&
+ if ((be32_to_cpu(hdr->data_length) == payload_length) &&
(!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) {
pr_err("Expected Data Transfer Length and Length of"
" Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL"
" bit is not set protocol error\n");
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if (payload_length > be32_to_cpu(hdr->data_length)) {
pr_err("DataSegmentLength: %u is greater than"
" EDTL: %u, protocol error.\n", payload_length,
hdr->data_length);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
pr_err("DataSegmentLength: %u is greater than"
" MaxXmitDataSegmentLength: %u, protocol error.\n",
payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if (payload_length > conn->sess->sess_ops->FirstBurstLength) {
pr_err("DataSegmentLength: %u is greater than"
" FirstBurstLength: %u, protocol error.\n",
payload_length, conn->sess->sess_ops->FirstBurstLength);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :
@@ -985,9 +972,8 @@
dr = iscsit_allocate_datain_req();
if (!dr)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
iscsit_attach_datain_req(cmd, dr);
}
@@ -1015,18 +1001,16 @@
cmd->sense_reason = target_setup_cmd_from_cdb(&cmd->se_cmd, hdr->cdb);
if (cmd->sense_reason) {
if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) {
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
goto attach_cmd;
}
if (iscsit_build_pdu_and_seq_lists(cmd, payload_length) < 0) {
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
attach_cmd:
@@ -1068,17 +1052,13 @@
* be acknowledged. (See below)
*/
if (!cmd->immediate_data) {
- cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
- if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
- if (!cmd->sense_reason)
- return 0;
-
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+ else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
return 0;
- } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, (unsigned char *)hdr, cmd);
}
}
@@ -1103,6 +1083,9 @@
* iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below.
*/
if (cmd->sense_reason) {
+ if (cmd->reject_reason)
+ return 0;
+
target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
return 1;
}
@@ -1111,10 +1094,8 @@
* the backend memory allocation.
*/
cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd);
- if (cmd->sense_reason) {
- target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ if (cmd->sense_reason)
return 1;
- }
return 0;
}
@@ -1124,6 +1105,7 @@
iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr,
bool dump_payload)
{
+ struct iscsi_conn *conn = cmd->conn;
int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION;
/*
* Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
@@ -1140,20 +1122,25 @@
* DataCRC, check against ExpCmdSN/MaxCmdSN if
* Immediate Bit is not set.
*/
- cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd, hdr->cmdsn);
+ cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
+ return -1;
+ } else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ return 0;
+ }
if (cmd->sense_reason) {
- if (iscsit_dump_data_payload(cmd->conn,
- cmd->first_burst_len, 1) < 0)
- return -1;
+ int rc;
+
+ rc = iscsit_dump_data_payload(cmd->conn,
+ cmd->first_burst_len, 1);
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ return rc;
} else if (cmd->unsolicited_data)
iscsit_set_unsoliticed_dataout(cmd);
- if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, (unsigned char *)hdr, cmd);
-
} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
/*
* Immediate Data failed DataCRC and ERL>=1,
@@ -1184,15 +1171,14 @@
rc = iscsit_setup_scsi_cmd(conn, cmd, buf);
if (rc < 0)
- return rc;
+ return 0;
/*
* Allocation iovecs needed for struct socket operations for
* traditional iSCSI block I/O.
*/
if (iscsit_allocate_iovecs(cmd) < 0) {
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 0, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
immed_data = cmd->immediate_data;
@@ -1277,14 +1263,13 @@
struct iscsi_data *hdr = (struct iscsi_data *)buf;
struct iscsi_cmd *cmd = NULL;
struct se_cmd *se_cmd;
- unsigned long flags;
u32 payload_length = ntoh24(hdr->dlength);
int rc;
if (!payload_length) {
pr_err("DataOUT payload is ZERO, protocol error.\n");
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
}
/* iSCSI write */
@@ -1301,8 +1286,8 @@
pr_err("DataSegmentLength: %u is greater than"
" MaxXmitDataSegmentLength: %u\n", payload_length,
conn->conn_ops->MaxXmitDataSegmentLength);
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
}
cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt,
@@ -1325,8 +1310,7 @@
if (cmd->data_direction != DMA_TO_DEVICE) {
pr_err("Command ITT: 0x%08x received DataOUT for a"
" NON-WRITE command.\n", cmd->init_task_tag);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
}
se_cmd = &cmd->se_cmd;
iscsit_mod_dataout_timer(cmd);
@@ -1335,8 +1319,7 @@
pr_err("DataOut Offset: %u, Length %u greater than"
" iSCSI Command EDTL %u, protocol error.\n",
hdr->offset, payload_length, cmd->se_cmd.data_length);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, buf);
}
if (cmd->unsolicited_data) {
@@ -1356,14 +1339,9 @@
*/
/* Something's amiss if we're not in WRITE_PENDING state... */
- spin_lock_irqsave(&se_cmd->t_state_lock, flags);
WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING);
- spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
-
- spin_lock_irqsave(&se_cmd->t_state_lock, flags);
if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE))
dump_unsolicited_data = 1;
- spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
if (dump_unsolicited_data) {
/*
@@ -1528,7 +1506,7 @@
rc = iscsit_check_dataout_hdr(conn, buf, &cmd);
if (rc < 0)
- return rc;
+ return 0;
else if (!cmd)
return 0;
@@ -1541,24 +1519,16 @@
return iscsit_check_dataout_payload(cmd, hdr, data_crc_failed);
}
-int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
- unsigned char *buf)
+int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_nopout *hdr)
{
- unsigned char *ping_data = NULL;
- int cmdsn_ret, niov = 0, ret = 0, rx_got, rx_size;
- u32 checksum, data_crc, padding = 0, payload_length;
- struct iscsi_cmd *cmd_p = NULL;
- struct kvec *iov = NULL;
- struct iscsi_nopout *hdr;
-
- hdr = (struct iscsi_nopout *) buf;
- payload_length = ntoh24(hdr->dlength);
+ u32 payload_length = ntoh24(hdr->dlength);
if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
" not set, protocol error.\n");
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
}
if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
@@ -1566,8 +1536,8 @@
" greater than MaxXmitDataSegmentLength: %u, protocol"
" error.\n", payload_length,
conn->conn_ops->MaxXmitDataSegmentLength);
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
}
pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x,"
@@ -1583,11 +1553,6 @@
* can contain ping data.
*/
if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
- if (!cmd)
- return iscsit_add_reject(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, buf, conn);
-
cmd->iscsi_opcode = ISCSI_OP_NOOP_OUT;
cmd->i_state = ISTATE_SEND_NOPIN;
cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ?
@@ -1599,8 +1564,85 @@
cmd->data_direction = DMA_NONE;
}
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_setup_nop_out);
+
+int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_nopout *hdr)
+{
+ struct iscsi_cmd *cmd_p = NULL;
+ int cmdsn_ret = 0;
+ /*
+ * Initiator is expecting a NopIN ping reply..
+ */
+ if (hdr->itt != RESERVED_ITT) {
+ BUG_ON(!cmd);
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
+ iscsit_add_cmd_to_response_queue(cmd, conn,
+ cmd->i_state);
+ return 0;
+ }
+
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
+ return 0;
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+
+ return 0;
+ }
+ /*
+ * This was a response to a unsolicited NOPIN ping.
+ */
+ if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
+ cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt));
+ if (!cmd_p)
+ return -EINVAL;
+
+ iscsit_stop_nopin_response_timer(conn);
+
+ cmd_p->i_state = ISTATE_REMOVE;
+ iscsit_add_cmd_to_immediate_queue(cmd_p, conn, cmd_p->i_state);
+
+ iscsit_start_nopin_timer(conn);
+ return 0;
+ }
+ /*
+ * Otherwise, initiator is not expecting a NOPIN is response.
+ * Just ignore for now.
+ */
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_process_nop_out);
+
+static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ unsigned char *ping_data = NULL;
+ struct iscsi_nopout *hdr = (struct iscsi_nopout *)buf;
+ struct kvec *iov = NULL;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int ret;
+
+ ret = iscsit_setup_nop_out(conn, cmd, hdr);
+ if (ret < 0)
+ return 0;
+ /*
+ * Handle NOP-OUT payload for traditional iSCSI sockets
+ */
if (payload_length && hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
- rx_size = payload_length;
+ u32 checksum, data_crc, padding = 0;
+ int niov = 0, rx_got, rx_size = payload_length;
+
ping_data = kzalloc(payload_length + 1, GFP_KERNEL);
if (!ping_data) {
pr_err("Unable to allocate memory for"
@@ -1679,76 +1721,14 @@
pr_debug("Ping Data: \"%s\"\n", ping_data);
}
- if (hdr->itt != RESERVED_ITT) {
- if (!cmd) {
- pr_err("Checking CmdSN for NOPOUT,"
- " but cmd is NULL!\n");
- return -1;
- }
- /*
- * Initiator is expecting a NopIN ping reply,
- */
- spin_lock_bh(&conn->cmd_lock);
- list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
- spin_unlock_bh(&conn->cmd_lock);
-
- iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
-
- if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
- iscsit_add_cmd_to_response_queue(cmd, conn,
- cmd->i_state);
- return 0;
- }
-
- cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
- if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
- ret = 0;
- goto ping_out;
- }
- if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
-
- return 0;
- }
-
- if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
- /*
- * This was a response to a unsolicited NOPIN ping.
- */
- cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt));
- if (!cmd_p)
- return -1;
-
- iscsit_stop_nopin_response_timer(conn);
-
- cmd_p->i_state = ISTATE_REMOVE;
- iscsit_add_cmd_to_immediate_queue(cmd_p, conn, cmd_p->i_state);
- iscsit_start_nopin_timer(conn);
- } else {
- /*
- * Initiator is not expecting a NOPIN is response.
- * Just ignore for now.
- *
- * iSCSI v19-91 10.18
- * "A NOP-OUT may also be used to confirm a changed
- * ExpStatSN if another PDU will not be available
- * for a long time."
- */
- ret = 0;
- goto out;
- }
-
- return 0;
+ return iscsit_process_nop_out(conn, cmd, hdr);
out:
if (cmd)
iscsit_free_cmd(cmd, false);
-ping_out:
+
kfree(ping_data);
return ret;
}
-EXPORT_SYMBOL(iscsit_handle_nop_out);
int
iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
@@ -1757,8 +1737,8 @@
struct se_tmr_req *se_tmr;
struct iscsi_tmr_req *tmr_req;
struct iscsi_tm *hdr;
- int out_of_order_cmdsn = 0;
- int ret;
+ int out_of_order_cmdsn = 0, ret;
+ bool sess_ref = false;
u8 function;
hdr = (struct iscsi_tm *) buf;
@@ -1782,8 +1762,8 @@
pr_err("Task Management Request TASK_REASSIGN not"
" issued as immediate command, bad iSCSI Initiator"
"implementation\n");
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG)
@@ -1795,9 +1775,9 @@
if (!cmd->tmr_req) {
pr_err("Unable to allocate memory for"
" Task Management command!\n");
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+ buf);
}
/*
@@ -1814,6 +1794,9 @@
conn->sess->se_sess, 0, DMA_NONE,
MSG_SIMPLE_TAG, cmd->sense_buffer + 2);
+ target_get_sess_cmd(conn->sess->se_sess, &cmd->se_cmd, true);
+ sess_ref = true;
+
switch (function) {
case ISCSI_TM_FUNC_ABORT_TASK:
tcm_function = TMR_ABORT_TASK;
@@ -1839,17 +1822,15 @@
default:
pr_err("Unknown iSCSI TMR Function:"
" 0x%02x\n", function);
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req,
tcm_function, GFP_KERNEL);
if (ret < 0)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 1, buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req;
}
@@ -1908,9 +1889,8 @@
break;
if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_INVALID, 1, 1,
- buf, cmd);
+ return iscsit_add_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
break;
default:
pr_err("Unknown TMR function: 0x%02x, protocol"
@@ -1928,15 +1908,13 @@
spin_unlock_bh(&conn->cmd_lock);
if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
- int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
+ int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP)
out_of_order_cmdsn = 1;
else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
return 0;
else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
+ return -1;
}
iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
@@ -1956,51 +1934,135 @@
* For connection recovery, this is also the default action for
* TMR TASK_REASSIGN.
*/
+ if (sess_ref) {
+ pr_debug("Handle TMR, using sess_ref=true check\n");
+ target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd);
+ }
+
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
return 0;
}
EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
/* #warning FIXME: Support Text Command parameters besides SendTargets */
-static int iscsit_handle_text_cmd(
- struct iscsi_conn *conn,
- unsigned char *buf)
+int
+iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_text *hdr)
{
- char *text_ptr, *text_in;
- int cmdsn_ret, niov = 0, rx_got, rx_size;
- u32 checksum = 0, data_crc = 0, payload_length;
- u32 padding = 0, pad_bytes = 0, text_length = 0;
- struct iscsi_cmd *cmd;
- struct kvec iov[3];
- struct iscsi_text *hdr;
-
- hdr = (struct iscsi_text *) buf;
- payload_length = ntoh24(hdr->dlength);
+ u32 payload_length = ntoh24(hdr->dlength);
if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
pr_err("Unable to accept text parameter length: %u"
"greater than MaxXmitDataSegmentLength %u.\n",
payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
}
pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x,"
" ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn,
hdr->exp_statsn, payload_length);
- rx_size = text_length = payload_length;
- if (text_length) {
- text_in = kzalloc(text_length, GFP_KERNEL);
+ cmd->iscsi_opcode = ISCSI_OP_TEXT;
+ cmd->i_state = ISTATE_SEND_TEXTRSP;
+ cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
+ conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
+ cmd->targ_xfer_tag = 0xFFFFFFFF;
+ cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
+ cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
+ cmd->data_direction = DMA_NONE;
+
+ return 0;
+}
+EXPORT_SYMBOL(iscsit_setup_text_cmd);
+
+int
+iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ struct iscsi_text *hdr)
+{
+ unsigned char *text_in = cmd->text_in_ptr, *text_ptr;
+ int cmdsn_ret;
+
+ if (!text_in) {
+ pr_err("Unable to locate text_in buffer for sendtargets"
+ " discovery\n");
+ goto reject;
+ }
+ if (strncmp("SendTargets", text_in, 11) != 0) {
+ pr_err("Received Text Data that is not"
+ " SendTargets, cannot continue.\n");
+ goto reject;
+ }
+ text_ptr = strchr(text_in, '=');
+ if (!text_ptr) {
+ pr_err("No \"=\" separator found in Text Data,"
+ " cannot continue.\n");
+ goto reject;
+ }
+ if (!strncmp("=All", text_ptr, 4)) {
+ cmd->cmd_flags |= IFC_SENDTARGETS_ALL;
+ } else if (!strncmp("=iqn.", text_ptr, 5) ||
+ !strncmp("=eui.", text_ptr, 5)) {
+ cmd->cmd_flags |= IFC_SENDTARGETS_SINGLE;
+ } else {
+ pr_err("Unable to locate valid SendTargets=%s value\n", text_ptr);
+ goto reject;
+ }
+
+ spin_lock_bh(&conn->cmd_lock);
+ list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
+ spin_unlock_bh(&conn->cmd_lock);
+
+ iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
+
+ if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
+ (unsigned char *)hdr, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
+
+ return 0;
+ }
+
+ return iscsit_execute_cmd(cmd, 0);
+
+reject:
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
+}
+EXPORT_SYMBOL(iscsit_process_text_cmd);
+
+static int
+iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf)
+{
+ struct iscsi_text *hdr = (struct iscsi_text *)buf;
+ char *text_in = NULL;
+ u32 payload_length = ntoh24(hdr->dlength);
+ int rx_size, rc;
+
+ rc = iscsit_setup_text_cmd(conn, cmd, hdr);
+ if (rc < 0)
+ return 0;
+
+ rx_size = payload_length;
+ if (payload_length) {
+ u32 checksum = 0, data_crc = 0;
+ u32 padding = 0, pad_bytes = 0;
+ int niov = 0, rx_got;
+ struct kvec iov[3];
+
+ text_in = kzalloc(payload_length, GFP_KERNEL);
if (!text_in) {
pr_err("Unable to allocate memory for"
" incoming text parameters\n");
- return -1;
+ goto reject;
}
+ cmd->text_in_ptr = text_in;
memset(iov, 0, 3 * sizeof(struct kvec));
iov[niov].iov_base = text_in;
- iov[niov++].iov_len = text_length;
+ iov[niov++].iov_len = payload_length;
padding = ((-payload_length) & 3);
if (padding != 0) {
@@ -2017,14 +2079,12 @@
}
rx_got = rx_data(conn, &iov[0], niov, rx_size);
- if (rx_got != rx_size) {
- kfree(text_in);
- return -1;
- }
+ if (rx_got != rx_size)
+ goto reject;
if (conn->conn_ops->DataDigest) {
iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
- text_in, text_length,
+ text_in, payload_length,
padding, (u8 *)&pad_bytes,
(u8 *)&data_crc);
@@ -2036,8 +2096,7 @@
pr_err("Unable to recover from"
" Text Data digest failure while in"
" ERL=0.\n");
- kfree(text_in);
- return -1;
+ goto reject;
} else {
/*
* Silently drop this PDU and let the
@@ -2052,68 +2111,22 @@
} else {
pr_debug("Got CRC32C DataDigest"
" 0x%08x for %u bytes of text data.\n",
- checksum, text_length);
+ checksum, payload_length);
}
}
- text_in[text_length - 1] = '\0';
+ text_in[payload_length - 1] = '\0';
pr_debug("Successfully read %d bytes of text"
- " data.\n", text_length);
-
- if (strncmp("SendTargets", text_in, 11) != 0) {
- pr_err("Received Text Data that is not"
- " SendTargets, cannot continue.\n");
- kfree(text_in);
- return -1;
- }
- text_ptr = strchr(text_in, '=');
- if (!text_ptr) {
- pr_err("No \"=\" separator found in Text Data,"
- " cannot continue.\n");
- kfree(text_in);
- return -1;
- }
- if (strncmp("=All", text_ptr, 4) != 0) {
- pr_err("Unable to locate All value for"
- " SendTargets key, cannot continue.\n");
- kfree(text_in);
- return -1;
- }
-/*#warning Support SendTargets=(iSCSI Target Name/Nothing) values. */
- kfree(text_in);
+ " data.\n", payload_length);
}
- cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
- if (!cmd)
- return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, buf, conn);
+ return iscsit_process_text_cmd(conn, cmd, hdr);
- cmd->iscsi_opcode = ISCSI_OP_TEXT;
- cmd->i_state = ISTATE_SEND_TEXTRSP;
- cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
- conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
- cmd->targ_xfer_tag = 0xFFFFFFFF;
- cmd->cmd_sn = be32_to_cpu(hdr->cmdsn);
- cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn);
- cmd->data_direction = DMA_NONE;
-
- spin_lock_bh(&conn->cmd_lock);
- list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
- spin_unlock_bh(&conn->cmd_lock);
-
- iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
-
- if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
- cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
- if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
-
- return 0;
- }
-
- return iscsit_execute_cmd(cmd, 0);
+reject:
+ kfree(cmd->text_in_ptr);
+ cmd->text_in_ptr = NULL;
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
}
+EXPORT_SYMBOL(iscsit_handle_text_cmd);
int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
{
@@ -2292,14 +2305,11 @@
if (ret < 0)
return ret;
} else {
- cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn);
- if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
+ cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
+ if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
logout_remove = 0;
- } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
- }
+ else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
+ return -1;
}
return logout_remove;
@@ -2323,8 +2333,8 @@
if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
pr_err("Initiator sent SNACK request while in"
" ErrorRecoveryLevel=0.\n");
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
}
/*
* SNACK_DATA and SNACK_R2T are both 0, so check which function to
@@ -2348,13 +2358,13 @@
case ISCSI_FLAG_SNACK_TYPE_RDATA:
/* FIXME: Support R-Data SNACK */
pr_err("R-Data SNACK Not Supported.\n");
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
default:
pr_err("Unknown SNACK type 0x%02x, protocol"
" error.\n", hdr->flags & 0x0f);
- return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buf, conn);
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buf);
}
return 0;
@@ -2426,14 +2436,14 @@
pr_err("Unable to recover from"
" Immediate Data digest failure while"
" in ERL=0.\n");
- iscsit_add_reject_from_cmd(
+ iscsit_reject_cmd(cmd,
ISCSI_REASON_DATA_DIGEST_ERROR,
- 1, 0, (unsigned char *)hdr, cmd);
+ (unsigned char *)hdr);
return IMMEDIATE_DATA_CANNOT_RECOVER;
} else {
- iscsit_add_reject_from_cmd(
+ iscsit_reject_cmd(cmd,
ISCSI_REASON_DATA_DIGEST_ERROR,
- 0, 0, (unsigned char *)hdr, cmd);
+ (unsigned char *)hdr);
return IMMEDIATE_DATA_ERL1_CRC_FAILURE;
}
} else {
@@ -3276,8 +3286,6 @@
return ISCSI_TMF_RSP_NO_LUN;
case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
return ISCSI_TMF_RSP_NOT_SUPPORTED;
- case TMR_FUNCTION_AUTHORIZATION_FAILED:
- return ISCSI_TMF_RSP_AUTH_FAILED;
case TMR_FUNCTION_REJECTED:
default:
return ISCSI_TMF_RSP_REJECTED;
@@ -3372,6 +3380,7 @@
struct iscsi_tpg_np *tpg_np;
int buffer_len, end_of_buf = 0, len = 0, payload_len = 0;
unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */
+ unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL;
buffer_len = max(conn->conn_ops->MaxRecvDataSegmentLength,
SENDTARGETS_BUF_LIMIT);
@@ -3382,9 +3391,31 @@
" response.\n");
return -ENOMEM;
}
+ /*
+ * Locate pointer to iqn./eui. string for IFC_SENDTARGETS_SINGLE
+ * explicit case..
+ */
+ if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) {
+ text_ptr = strchr(text_in, '=');
+ if (!text_ptr) {
+ pr_err("Unable to locate '=' string in text_in:"
+ " %s\n", text_in);
+ kfree(payload);
+ return -EINVAL;
+ }
+ /*
+ * Skip over '=' character..
+ */
+ text_ptr += 1;
+ }
spin_lock(&tiqn_lock);
list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
+ if ((cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) &&
+ strcmp(tiqn->tiqn, text_ptr)) {
+ continue;
+ }
+
len = sprintf(buf, "TargetName=%s", tiqn->tiqn);
len += 1;
@@ -3438,6 +3469,9 @@
eob:
if (end_of_buf)
break;
+
+ if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE)
+ break;
}
spin_unlock(&tiqn_lock);
@@ -3446,6 +3480,37 @@
return payload_len;
}
+int
+iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
+ struct iscsi_text_rsp *hdr)
+{
+ int text_length, padding;
+
+ text_length = iscsit_build_sendtargets_response(cmd);
+ if (text_length < 0)
+ return text_length;
+
+ hdr->opcode = ISCSI_OP_TEXT_RSP;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ padding = ((-text_length) & 3);
+ hton24(hdr->dlength, text_length);
+ hdr->itt = cmd->init_task_tag;
+ hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
+ cmd->stat_sn = conn->stat_sn++;
+ hdr->statsn = cpu_to_be32(cmd->stat_sn);
+
+ iscsit_increment_maxcmdsn(cmd, conn->sess);
+ hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
+ hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+
+ pr_debug("Built Text Response: ITT: 0x%08x, StatSN: 0x%08x,"
+ " Length: %u, CID: %hu\n", cmd->init_task_tag, cmd->stat_sn,
+ text_length, conn->cid);
+
+ return text_length + padding;
+}
+EXPORT_SYMBOL(iscsit_build_text_rsp);
+
/*
* FIXME: Add support for F_BIT and C_BIT when the length is longer than
* MaxRecvDataSegmentLength.
@@ -3454,44 +3519,23 @@
struct iscsi_cmd *cmd,
struct iscsi_conn *conn)
{
- struct iscsi_text_rsp *hdr;
+ struct iscsi_text_rsp *hdr = (struct iscsi_text_rsp *)cmd->pdu;
struct kvec *iov;
- u32 padding = 0, tx_size = 0;
- int text_length, iov_count = 0;
+ u32 tx_size = 0;
+ int text_length, iov_count = 0, rc;
- text_length = iscsit_build_sendtargets_response(cmd);
- if (text_length < 0)
- return text_length;
+ rc = iscsit_build_text_rsp(cmd, conn, hdr);
+ if (rc < 0)
+ return rc;
- padding = ((-text_length) & 3);
- if (padding != 0) {
- memset(cmd->buf_ptr + text_length, 0, padding);
- pr_debug("Attaching %u additional bytes for"
- " padding.\n", padding);
- }
-
- hdr = (struct iscsi_text_rsp *) cmd->pdu;
- memset(hdr, 0, ISCSI_HDR_LEN);
- hdr->opcode = ISCSI_OP_TEXT_RSP;
- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- hton24(hdr->dlength, text_length);
- hdr->itt = cmd->init_task_tag;
- hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
- cmd->stat_sn = conn->stat_sn++;
- hdr->statsn = cpu_to_be32(cmd->stat_sn);
-
- iscsit_increment_maxcmdsn(cmd, conn->sess);
- hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
- hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
-
+ text_length = rc;
iov = &cmd->iov_misc[0];
-
iov[iov_count].iov_base = cmd->pdu;
iov[iov_count++].iov_len = ISCSI_HDR_LEN;
iov[iov_count].iov_base = cmd->buf_ptr;
- iov[iov_count++].iov_len = text_length + padding;
+ iov[iov_count++].iov_len = text_length;
- tx_size += (ISCSI_HDR_LEN + text_length + padding);
+ tx_size += (ISCSI_HDR_LEN + text_length);
if (conn->conn_ops->HeaderDigest) {
u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
@@ -3507,7 +3551,7 @@
if (conn->conn_ops->DataDigest) {
iscsit_do_crypto_hash_buf(&conn->conn_tx_hash,
- cmd->buf_ptr, (text_length + padding),
+ cmd->buf_ptr, text_length,
0, NULL, (u8 *)&cmd->data_crc);
iov[iov_count].iov_base = &cmd->data_crc;
@@ -3515,16 +3559,13 @@
tx_size += ISCSI_CRC_LEN;
pr_debug("Attaching DataDigest for %u bytes of text"
- " data, CRC 0x%08x\n", (text_length + padding),
+ " data, CRC 0x%08x\n", text_length,
cmd->data_crc);
}
cmd->iov_misc_count = iov_count;
cmd->tx_size = tx_size;
- pr_debug("Built Text Response: ITT: 0x%08x, StatSN: 0x%08x,"
- " Length: %u, CID: %hu\n", cmd->init_task_tag, cmd->stat_sn,
- text_length, conn->cid);
return 0;
}
@@ -3533,6 +3574,7 @@
struct iscsi_reject *hdr)
{
hdr->opcode = ISCSI_OP_REJECT;
+ hdr->reason = cmd->reject_reason;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
hton24(hdr->dlength, ISCSI_HDR_LEN);
hdr->ffffffff = cpu_to_be32(0xffffffff);
@@ -3806,18 +3848,11 @@
case ISTATE_SEND_STATUS_RECOVERY:
case ISTATE_SEND_TEXTRSP:
case ISTATE_SEND_TASKMGTRSP:
+ case ISTATE_SEND_REJECT:
spin_lock_bh(&cmd->istate_lock);
cmd->i_state = ISTATE_SENT_STATUS;
spin_unlock_bh(&cmd->istate_lock);
break;
- case ISTATE_SEND_REJECT:
- if (cmd->cmd_flags & ICF_REJECT_FAIL_CONN) {
- cmd->cmd_flags &= ~ICF_REJECT_FAIL_CONN;
- complete(&cmd->reject_comp);
- goto err;
- }
- complete(&cmd->reject_comp);
- break;
default:
pr_err("Unknown Opcode: 0x%02x ITT:"
" 0x%08x, i_state: %d on CID: %hu\n",
@@ -3922,8 +3957,7 @@
case ISCSI_OP_SCSI_CMD:
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
if (!cmd)
- return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, buf, conn);
+ goto reject;
ret = iscsit_handle_scsi_cmd(conn, cmd, buf);
break;
@@ -3935,27 +3969,28 @@
if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
if (!cmd)
- return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, buf, conn);
+ goto reject;
}
ret = iscsit_handle_nop_out(conn, cmd, buf);
break;
case ISCSI_OP_SCSI_TMFUNC:
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
if (!cmd)
- return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, buf, conn);
+ goto reject;
ret = iscsit_handle_task_mgt_cmd(conn, cmd, buf);
break;
case ISCSI_OP_TEXT:
- ret = iscsit_handle_text_cmd(conn, buf);
+ cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
+ if (!cmd)
+ goto reject;
+
+ ret = iscsit_handle_text_cmd(conn, cmd, buf);
break;
case ISCSI_OP_LOGOUT:
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
if (!cmd)
- return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, buf, conn);
+ goto reject;
ret = iscsit_handle_logout_cmd(conn, cmd, buf);
if (ret > 0)
@@ -3987,6 +4022,8 @@
}
return ret;
+reject:
+ return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
int iscsi_target_rx_thread(void *arg)
@@ -4039,11 +4076,6 @@
goto transport_err;
}
- /*
- * Set conn->bad_hdr for use with REJECT PDUs.
- */
- memcpy(&conn->bad_hdr, &buffer, ISCSI_HDR_LEN);
-
if (conn->conn_ops->HeaderDigest) {
iov.iov_base = &digest;
iov.iov_len = ISCSI_CRC_LEN;
@@ -4086,8 +4118,8 @@
(!(opcode & ISCSI_OP_LOGOUT)))) {
pr_err("Received illegal iSCSI Opcode: 0x%02x"
" while in Discovery Session, rejecting.\n", opcode);
- iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1,
- buffer, conn);
+ iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ buffer);
goto transport_err;
}
diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h
index a0050b2..2c437cb 100644
--- a/drivers/target/iscsi/iscsi_target.h
+++ b/drivers/target/iscsi/iscsi_target.h
@@ -15,7 +15,7 @@
extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
struct iscsi_portal_group *);
extern int iscsit_del_np(struct iscsi_np *);
-extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *);
+extern int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8, unsigned char *);
extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);
extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *);
extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *);
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index 8d8b3ff..bbfd288 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -20,6 +20,7 @@
****************************************************************************/
#include <linux/configfs.h>
+#include <linux/ctype.h>
#include <linux/export.h>
#include <linux/inet.h>
#include <target/target_core_base.h>
@@ -78,11 +79,12 @@
struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
struct iscsi_tpg_np, se_tpg_np);
struct iscsi_tpg_np *tpg_np_sctp = NULL;
- char *endptr;
u32 op;
int ret;
- op = simple_strtoul(page, &endptr, 0);
+ ret = kstrtou32(page, 0, &op);
+ if (ret)
+ return ret;
if ((op != 1) && (op != 0)) {
pr_err("Illegal value for tpg_enable: %u\n", op);
return -EINVAL;
@@ -382,11 +384,12 @@
{ \
struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \
se_node_acl); \
- char *endptr; \
u32 val; \
int ret; \
\
- val = simple_strtoul(page, &endptr, 0); \
+ ret = kstrtou32(page, 0, &val); \
+ if (ret) \
+ return ret; \
ret = iscsit_na_##name(nacl, val); \
if (ret < 0) \
return ret; \
@@ -474,7 +477,7 @@
if (!capable(CAP_SYS_ADMIN)) \
return -EPERM; \
\
- snprintf(auth->name, PAGE_SIZE, "%s", page); \
+ snprintf(auth->name, sizeof(auth->name), "%s", page); \
if (!strncmp("NULL", auth->name, 4)) \
auth->naf_flags &= ~flags; \
else \
@@ -789,11 +792,12 @@
struct iscsi_portal_group *tpg = container_of(se_tpg,
struct iscsi_portal_group, tpg_se_tpg);
struct config_item *acl_ci, *tpg_ci, *wwn_ci;
- char *endptr;
u32 cmdsn_depth = 0;
int ret;
- cmdsn_depth = simple_strtoul(page, &endptr, 0);
+ ret = kstrtou32(page, 0, &cmdsn_depth);
+ if (ret)
+ return ret;
if (cmdsn_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) {
pr_err("Passed cmdsn_depth: %u exceeds"
" TA_DEFAULT_CMDSN_DEPTH_MAX: %u\n", cmdsn_depth,
@@ -977,14 +981,15 @@
{ \
struct iscsi_portal_group *tpg = container_of(se_tpg, \
struct iscsi_portal_group, tpg_se_tpg); \
- char *endptr; \
u32 val; \
int ret; \
\
if (iscsit_get_tpg(tpg) < 0) \
return -EINVAL; \
\
- val = simple_strtoul(page, &endptr, 0); \
+ ret = kstrtou32(page, 0, &val); \
+ if (ret) \
+ goto out; \
ret = iscsit_ta_##name(tpg, val); \
if (ret < 0) \
goto out; \
@@ -1053,6 +1058,131 @@
/* End items for lio_target_tpg_attrib_cit */
+/* Start items for lio_target_tpg_auth_cit */
+
+#define __DEF_TPG_AUTH_STR(prefix, name, flags) \
+static ssize_t __iscsi_##prefix##_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ return snprintf(page, PAGE_SIZE, "%s\n", auth->name); \
+} \
+ \
+static ssize_t __iscsi_##prefix##_store_##name( \
+ struct se_portal_group *se_tpg, \
+ const char *page, \
+ size_t count) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ snprintf(auth->name, sizeof(auth->name), "%s", page); \
+ if (!(strncmp("NULL", auth->name, 4))) \
+ auth->naf_flags &= ~flags; \
+ else \
+ auth->naf_flags |= flags; \
+ \
+ if ((auth->naf_flags & NAF_USERID_IN_SET) && \
+ (auth->naf_flags & NAF_PASSWORD_IN_SET)) \
+ auth->authenticate_target = 1; \
+ else \
+ auth->authenticate_target = 0; \
+ \
+ return count; \
+}
+
+#define __DEF_TPG_AUTH_INT(prefix, name) \
+static ssize_t __iscsi_##prefix##_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ struct iscsi_portal_group *tpg = container_of(se_tpg, \
+ struct iscsi_portal_group, tpg_se_tpg); \
+ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \
+ \
+ if (!capable(CAP_SYS_ADMIN)) \
+ return -EPERM; \
+ \
+ return snprintf(page, PAGE_SIZE, "%d\n", auth->name); \
+}
+
+#define DEF_TPG_AUTH_STR(name, flags) \
+ __DEF_TPG_AUTH_STR(tpg_auth, name, flags) \
+static ssize_t iscsi_tpg_auth_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ return __iscsi_tpg_auth_show_##name(se_tpg, page); \
+} \
+ \
+static ssize_t iscsi_tpg_auth_store_##name( \
+ struct se_portal_group *se_tpg, \
+ const char *page, \
+ size_t count) \
+{ \
+ return __iscsi_tpg_auth_store_##name(se_tpg, page, count); \
+}
+
+#define DEF_TPG_AUTH_INT(name) \
+ __DEF_TPG_AUTH_INT(tpg_auth, name) \
+static ssize_t iscsi_tpg_auth_show_##name( \
+ struct se_portal_group *se_tpg, \
+ char *page) \
+{ \
+ return __iscsi_tpg_auth_show_##name(se_tpg, page); \
+}
+
+#define TPG_AUTH_ATTR(_name, _mode) TF_TPG_AUTH_ATTR(iscsi, _name, _mode);
+#define TPG_AUTH_ATTR_RO(_name) TF_TPG_AUTH_ATTR_RO(iscsi, _name);
+
+/*
+ * * One-way authentication userid
+ * */
+DEF_TPG_AUTH_STR(userid, NAF_USERID_SET);
+TPG_AUTH_ATTR(userid, S_IRUGO | S_IWUSR);
+/*
+ * * One-way authentication password
+ * */
+DEF_TPG_AUTH_STR(password, NAF_PASSWORD_SET);
+TPG_AUTH_ATTR(password, S_IRUGO | S_IWUSR);
+/*
+ * * Enforce mutual authentication
+ * */
+DEF_TPG_AUTH_INT(authenticate_target);
+TPG_AUTH_ATTR_RO(authenticate_target);
+/*
+ * * Mutual authentication userid
+ * */
+DEF_TPG_AUTH_STR(userid_mutual, NAF_USERID_IN_SET);
+TPG_AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR);
+/*
+ * * Mutual authentication password
+ * */
+DEF_TPG_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET);
+TPG_AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *lio_target_tpg_auth_attrs[] = {
+ &iscsi_tpg_auth_userid.attr,
+ &iscsi_tpg_auth_password.attr,
+ &iscsi_tpg_auth_authenticate_target.attr,
+ &iscsi_tpg_auth_userid_mutual.attr,
+ &iscsi_tpg_auth_password_mutual.attr,
+ NULL,
+};
+
+/* End items for lio_target_tpg_auth_cit */
+
/* Start items for lio_target_tpg_param_cit */
#define DEF_TPG_PARAM(name) \
@@ -1087,13 +1217,14 @@
struct iscsi_portal_group *tpg = container_of(se_tpg, \
struct iscsi_portal_group, tpg_se_tpg); \
char *buf; \
- int ret; \
+ int ret, len; \
\
buf = kzalloc(PAGE_SIZE, GFP_KERNEL); \
if (!buf) \
return -ENOMEM; \
- snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page); \
- buf[strlen(buf)-1] = '\0'; /* Kill newline */ \
+ len = snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page); \
+ if (isspace(buf[len-1])) \
+ buf[len-1] = '\0'; /* Kill newline */ \
\
if (iscsit_get_tpg(tpg) < 0) { \
kfree(buf); \
@@ -1230,11 +1361,12 @@
{
struct iscsi_portal_group *tpg = container_of(se_tpg,
struct iscsi_portal_group, tpg_se_tpg);
- char *endptr;
u32 op;
- int ret = 0;
+ int ret;
- op = simple_strtoul(page, &endptr, 0);
+ ret = kstrtou32(page, 0, &op);
+ if (ret)
+ return ret;
if ((op != 1) && (op != 0)) {
pr_err("Illegal value for tpg_enable: %u\n", op);
return -EINVAL;
@@ -1282,15 +1414,15 @@
{
struct iscsi_portal_group *tpg;
struct iscsi_tiqn *tiqn;
- char *tpgt_str, *end_ptr;
- int ret = 0;
- unsigned short int tpgt;
+ char *tpgt_str;
+ int ret;
+ u16 tpgt;
tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn);
/*
* Only tpgt_# directory groups can be created below
* target/iscsi/iqn.superturodiskarry/
- */
+ */
tpgt_str = strstr(name, "tpgt_");
if (!tpgt_str) {
pr_err("Unable to locate \"tpgt_#\" directory"
@@ -1298,7 +1430,9 @@
return NULL;
}
tpgt_str += 5; /* Skip ahead of "tpgt_" */
- tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0);
+ ret = kstrtou16(tpgt_str, 0, &tpgt);
+ if (ret)
+ return NULL;
tpg = iscsit_alloc_portal_group(tiqn, tpgt);
if (!tpg)
@@ -1506,10 +1640,12 @@
{
struct iscsi_param *param;
struct iscsi_portal_group *discovery_tpg = iscsit_global->discovery_tpg;
- char *endptr;
u32 op;
+ int err;
- op = simple_strtoul(page, &endptr, 0);
+ err = kstrtou32(page, 0, &op);
+ if (err)
+ return -EINVAL;
if ((op != 1) && (op != 0)) {
pr_err("Illegal value for enforce_discovery_auth:"
" %u\n", op);
@@ -1655,13 +1791,12 @@
return 0;
}
-static int lio_queue_tm_rsp(struct se_cmd *se_cmd)
+static void lio_queue_tm_rsp(struct se_cmd *se_cmd)
{
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
cmd->i_state = ISTATE_SEND_TASKMGTRSP;
iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
- return 0;
}
static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg)
@@ -1866,6 +2001,7 @@
TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs;
TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs;
TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_auth_cit.ct_attrs = lio_target_tpg_auth_attrs;
TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs;
TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs;
TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs;
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
index 60ec4b9..4f77a78 100644
--- a/drivers/target/iscsi/iscsi_target_core.h
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -132,7 +132,8 @@
ICF_CONTIG_MEMORY = 0x00000020,
ICF_ATTACHED_TO_RQUEUE = 0x00000040,
ICF_OOO_CMDSN = 0x00000080,
- ICF_REJECT_FAIL_CONN = 0x00000100,
+ IFC_SENDTARGETS_ALL = 0x00000100,
+ IFC_SENDTARGETS_SINGLE = 0x00000200,
};
/* struct iscsi_cmd->i_state */
@@ -366,6 +367,8 @@
u8 maxcmdsn_inc;
/* Immediate Unsolicited Dataout */
u8 unsolicited_data;
+ /* Reject reason code */
+ u8 reject_reason;
/* CID contained in logout PDU when opcode == ISCSI_INIT_LOGOUT_CMND */
u16 logout_cid;
/* Command flags */
@@ -427,6 +430,8 @@
u32 tx_size;
/* Buffer used for various purposes */
void *buf_ptr;
+ /* Used by SendTargets=[iqn.,eui.] discovery */
+ void *text_in_ptr;
/* See include/linux/dma-mapping.h */
enum dma_data_direction data_direction;
/* iSCSI PDU Header + CRC */
@@ -446,7 +451,6 @@
struct list_head datain_list;
/* R2T List */
struct list_head cmd_r2t_list;
- struct completion reject_comp;
/* Timer for DataOUT */
struct timer_list dataout_timer;
/* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */
@@ -528,8 +532,6 @@
u32 of_marker;
/* Used for calculating OFMarker offset to next PDU */
u32 of_marker_offset;
- /* Complete Bad PDU for sending reject */
- unsigned char bad_hdr[ISCSI_HDR_LEN];
#define IPV6_ADDRESS_SPACE 48
unsigned char login_ip[IPV6_ADDRESS_SPACE];
unsigned char local_ip[IPV6_ADDRESS_SPACE];
@@ -809,6 +811,7 @@
struct mutex tpg_access_lock;
struct mutex np_login_lock;
struct iscsi_tpg_attrib tpg_attrib;
+ struct iscsi_node_auth tpg_demo_auth;
/* Pointer to default list of iSCSI parameters for TPG */
struct iscsi_param_list *param_list;
struct iscsi_tiqn *tpg_tiqn;
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index dcb199d..08bd878 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -746,13 +746,12 @@
if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
pr_err("Unable to recover from DataOUT CRC"
" failure while ERL=0, closing session.\n");
- iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR,
- 1, 0, buf, cmd);
+ iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR,
+ buf);
return DATAOUT_CANNOT_RECOVER;
}
- iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR,
- 0, 0, buf, cmd);
+ iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf);
return iscsit_dataout_post_crc_failed(cmd, buf);
}
}
@@ -909,6 +908,7 @@
wait_for_completion(&conn->conn_wait_comp);
complete(&conn->conn_post_wait_comp);
}
+EXPORT_SYMBOL(iscsit_cause_connection_reinstatement);
void iscsit_fall_back_to_erl0(struct iscsi_session *sess)
{
diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c
index 40d9dbc..586c268 100644
--- a/drivers/target/iscsi/iscsi_target_erl1.c
+++ b/drivers/target/iscsi/iscsi_target_erl1.c
@@ -162,9 +162,8 @@
" protocol error.\n", cmd->init_task_tag, begrun,
(begrun + runlength), cmd->acked_data_sn);
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd,
+ ISCSI_REASON_PROTOCOL_ERROR, buf);
}
if (runlength) {
@@ -173,8 +172,8 @@
" with BegRun: 0x%08x, RunLength: 0x%08x, exceeds"
" current R2TSN: 0x%08x, protocol error.\n",
cmd->init_task_tag, begrun, runlength, cmd->r2t_sn);
- return iscsit_add_reject_from_cmd(
- ISCSI_REASON_BOOKMARK_INVALID, 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd,
+ ISCSI_REASON_BOOKMARK_INVALID, buf);
}
last_r2tsn = (begrun + runlength);
} else
@@ -433,8 +432,7 @@
" protocol error.\n", cmd->init_task_tag, begrun,
(begrun + runlength), cmd->acked_data_sn);
- return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR,
- 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
}
/*
@@ -445,14 +443,14 @@
pr_err("Initiator requesting BegRun: 0x%08x, RunLength"
": 0x%08x greater than maximum DataSN: 0x%08x.\n",
begrun, runlength, (cmd->data_sn - 1));
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID,
- 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID,
+ buf);
}
dr = iscsit_allocate_datain_req();
if (!dr)
- return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_NO_RESOURCES,
- 1, 0, buf, cmd);
+ return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES,
+ buf);
dr->data_sn = dr->begrun = begrun;
dr->runlength = runlength;
@@ -1090,7 +1088,7 @@
ooo_cmdsn = iscsit_allocate_ooo_cmdsn();
if (!ooo_cmdsn)
- return CMDSN_ERROR_CANNOT_RECOVER;
+ return -ENOMEM;
ooo_cmdsn->cmd = cmd;
ooo_cmdsn->batch_count = (batch) ?
@@ -1101,10 +1099,10 @@
if (iscsit_attach_ooo_cmdsn(sess, ooo_cmdsn) < 0) {
kmem_cache_free(lio_ooo_cache, ooo_cmdsn);
- return CMDSN_ERROR_CANNOT_RECOVER;
+ return -ENOMEM;
}
- return CMDSN_HIGHER_THAN_EXP;
+ return 0;
}
static int iscsit_set_dataout_timeout_values(
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index cd5018f..c4675b4 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -112,6 +112,7 @@
struct iscsi_session *sess = conn->sess;
struct iscsi_node_auth *auth;
struct iscsi_node_acl *iscsi_nacl;
+ struct iscsi_portal_group *iscsi_tpg;
struct se_node_acl *se_nacl;
if (!sess->sess_ops->SessionType) {
@@ -132,7 +133,17 @@
return -1;
}
- auth = ISCSI_NODE_AUTH(iscsi_nacl);
+ if (se_nacl->dynamic_node_acl) {
+ iscsi_tpg = container_of(se_nacl->se_tpg,
+ struct iscsi_portal_group, tpg_se_tpg);
+
+ auth = &iscsi_tpg->tpg_demo_auth;
+ } else {
+ iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
+ se_node_acl);
+
+ auth = ISCSI_NODE_AUTH(iscsi_nacl);
+ }
} else {
/*
* For SessionType=Discovery
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index e382221..35fd643 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -1799,9 +1799,6 @@
* this key is not sent over the wire.
*/
if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
- if (param_list->iser == true)
- continue;
-
ops->MaxXmitDataSegmentLength =
simple_strtoul(param->value, &tmpptr, 0);
pr_debug("MaxXmitDataSegmentLength: %s\n",
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 08a3bac..1df06d5 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -178,7 +178,6 @@
INIT_LIST_HEAD(&cmd->i_conn_node);
INIT_LIST_HEAD(&cmd->datain_list);
INIT_LIST_HEAD(&cmd->cmd_r2t_list);
- init_completion(&cmd->reject_comp);
spin_lock_init(&cmd->datain_lock);
spin_lock_init(&cmd->dataout_timeout_lock);
spin_lock_init(&cmd->istate_lock);
@@ -284,13 +283,12 @@
* Commands may be received out of order if MC/S is in use.
* Ensure they are executed in CmdSN order.
*/
-int iscsit_sequence_cmd(
- struct iscsi_conn *conn,
- struct iscsi_cmd *cmd,
- __be32 cmdsn)
+int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char *buf, __be32 cmdsn)
{
- int ret;
- int cmdsn_ret;
+ int ret, cmdsn_ret;
+ bool reject = false;
+ u8 reason = ISCSI_REASON_BOOKMARK_NO_RESOURCES;
mutex_lock(&conn->sess->cmdsn_mutex);
@@ -300,9 +298,19 @@
ret = iscsit_execute_cmd(cmd, 0);
if ((ret >= 0) && !list_empty(&conn->sess->sess_ooo_cmdsn_list))
iscsit_execute_ooo_cmdsns(conn->sess);
+ else if (ret < 0) {
+ reject = true;
+ ret = CMDSN_ERROR_CANNOT_RECOVER;
+ }
break;
case CMDSN_HIGHER_THAN_EXP:
ret = iscsit_handle_ooo_cmdsn(conn->sess, cmd, be32_to_cpu(cmdsn));
+ if (ret < 0) {
+ reject = true;
+ ret = CMDSN_ERROR_CANNOT_RECOVER;
+ break;
+ }
+ ret = CMDSN_HIGHER_THAN_EXP;
break;
case CMDSN_LOWER_THAN_EXP:
cmd->i_state = ISTATE_REMOVE;
@@ -310,11 +318,16 @@
ret = cmdsn_ret;
break;
default:
+ reason = ISCSI_REASON_PROTOCOL_ERROR;
+ reject = true;
ret = cmdsn_ret;
break;
}
mutex_unlock(&conn->sess->cmdsn_mutex);
+ if (reject)
+ iscsit_reject_cmd(cmd, reason, buf);
+
return ret;
}
EXPORT_SYMBOL(iscsit_sequence_cmd);
@@ -681,6 +694,7 @@
kfree(cmd->seq_list);
kfree(cmd->tmr_req);
kfree(cmd->iov_data);
+ kfree(cmd->text_in_ptr);
kmem_cache_free(lio_cmd_cache, cmd);
}
diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h
index a442265..e4fc34a 100644
--- a/drivers/target/iscsi/iscsi_target_util.h
+++ b/drivers/target/iscsi/iscsi_target_util.h
@@ -13,7 +13,8 @@
extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32);
extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *);
extern struct iscsi_r2t *iscsit_get_holder_for_r2tsn(struct iscsi_cmd *, u32);
-int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, __be32 cmdsn);
+extern int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
+ unsigned char * ,__be32 cmdsn);
extern int iscsit_check_unsolicited_dataout(struct iscsi_cmd *, unsigned char *);
extern struct iscsi_cmd *iscsit_find_cmd_from_itt(struct iscsi_conn *, itt_t);
extern struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(struct iscsi_conn *,
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index 7c90814..568ad25 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -786,7 +786,7 @@
return 0;
}
-static int tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd)
+static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd)
{
struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr;
@@ -796,7 +796,6 @@
*/
atomic_set(&tl_tmr->tmr_complete, 1);
wake_up(&tl_tmr->tl_tmr_wait);
- return 0;
}
static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba)
diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c
index d3536f5..e51b09a 100644
--- a/drivers/target/sbp/sbp_target.c
+++ b/drivers/target/sbp/sbp_target.c
@@ -1842,9 +1842,8 @@
return sbp_send_sense(req);
}
-static int sbp_queue_tm_rsp(struct se_cmd *se_cmd)
+static void sbp_queue_tm_rsp(struct se_cmd *se_cmd)
{
- return 0;
}
static int sbp_check_stop_free(struct se_cmd *se_cmd)
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index 4a8bd36..e4d2293 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -983,7 +983,6 @@
struct se_node_acl *se_nacl;
struct t10_pr_registration *pr_reg;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
@@ -992,12 +991,11 @@
return sprintf(page, "No SPC-3 Reservation holder\n");
se_nacl = pr_reg->pr_reg_nacl;
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
return sprintf(page, "SPC-3 Reservation: %s Initiator: %s%s\n",
se_nacl->se_tpg->se_tpg_tfo->get_fabric_name(),
- se_nacl->initiatorname, (prf_isid) ? &i_buf[0] : "");
+ se_nacl->initiatorname, i_buf);
}
static ssize_t target_core_dev_pr_show_spc2_res(struct se_device *dev,
@@ -1116,7 +1114,7 @@
unsigned char buf[384];
char i_buf[PR_REG_ISID_ID_LEN];
ssize_t len = 0;
- int reg_count = 0, prf_isid;
+ int reg_count = 0;
len += sprintf(page+len, "SPC-3 PR Registrations:\n");
@@ -1127,12 +1125,11 @@
memset(buf, 0, 384);
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
tfo = pr_reg->pr_reg_nacl->se_tpg->se_tpg_tfo;
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
+ core_pr_dump_initiator_port(pr_reg, i_buf,
PR_REG_ISID_ID_LEN);
sprintf(buf, "%s Node: %s%s Key: 0x%016Lx PRgen: 0x%08x\n",
tfo->get_fabric_name(),
- pr_reg->pr_reg_nacl->initiatorname, (prf_isid) ?
- &i_buf[0] : "", pr_reg->pr_res_key,
+ pr_reg->pr_reg_nacl->initiatorname, i_buf, pr_reg->pr_res_key,
pr_reg->pr_res_generation);
if (len + strlen(buf) >= PAGE_SIZE)
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 4630481..8f4142f 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -1410,7 +1410,6 @@
INIT_LIST_HEAD(&dev->t10_alua.tg_pt_gps_list);
spin_lock_init(&dev->t10_alua.tg_pt_gps_lock);
- dev->t10_pr.pr_aptpl_buf_len = PR_APTPL_BUF_LEN;
dev->t10_wwn.t10_dev = dev;
dev->t10_alua.t10_dev = dev;
@@ -1545,7 +1544,7 @@
{
struct se_hba *hba;
struct se_device *dev;
- char buf[16];
+ char buf[] = "rd_pages=8,rd_nullio=1";
int ret;
hba = core_alloc_hba("rd_mcp", 0, HBA_FLAGS_INTERNAL_USE);
@@ -1558,8 +1557,6 @@
goto out_free_hba;
}
- memset(buf, 0, 16);
- sprintf(buf, "rd_pages=8");
hba->transport->set_configfs_dev_params(dev, buf, sizeof(buf));
ret = target_configure_device(dev);
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index 04c775c..eb56eb1 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -965,6 +965,19 @@
/* End of tfc_tpg_attrib_cit */
+/* Start of tfc_tpg_auth_cit */
+
+CONFIGFS_EATTR_OPS(target_fabric_tpg_auth, se_portal_group, tpg_auth_group);
+
+static struct configfs_item_operations target_fabric_tpg_auth_item_ops = {
+ .show_attribute = target_fabric_tpg_auth_attr_show,
+ .store_attribute = target_fabric_tpg_auth_attr_store,
+};
+
+TF_CIT_SETUP(tpg_auth, &target_fabric_tpg_auth_item_ops, NULL, NULL);
+
+/* End of tfc_tpg_attrib_cit */
+
/* Start of tfc_tpg_param_cit */
CONFIGFS_EATTR_OPS(target_fabric_tpg_param, se_portal_group, tpg_param_group);
@@ -1030,8 +1043,9 @@
se_tpg->tpg_group.default_groups[1] = &se_tpg->tpg_np_group;
se_tpg->tpg_group.default_groups[2] = &se_tpg->tpg_acl_group;
se_tpg->tpg_group.default_groups[3] = &se_tpg->tpg_attrib_group;
- se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_param_group;
- se_tpg->tpg_group.default_groups[5] = NULL;
+ se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_auth_group;
+ se_tpg->tpg_group.default_groups[5] = &se_tpg->tpg_param_group;
+ se_tpg->tpg_group.default_groups[6] = NULL;
config_group_init_type_name(&se_tpg->tpg_group, name,
&TF_CIT_TMPL(tf)->tfc_tpg_base_cit);
@@ -1043,6 +1057,8 @@
&TF_CIT_TMPL(tf)->tfc_tpg_nacl_cit);
config_group_init_type_name(&se_tpg->tpg_attrib_group, "attrib",
&TF_CIT_TMPL(tf)->tfc_tpg_attrib_cit);
+ config_group_init_type_name(&se_tpg->tpg_auth_group, "auth",
+ &TF_CIT_TMPL(tf)->tfc_tpg_auth_cit);
config_group_init_type_name(&se_tpg->tpg_param_group, "param",
&TF_CIT_TMPL(tf)->tfc_tpg_param_cit);
@@ -1202,6 +1218,7 @@
target_fabric_setup_tpg_np_cit(tf);
target_fabric_setup_tpg_np_base_cit(tf);
target_fabric_setup_tpg_attrib_cit(tf);
+ target_fabric_setup_tpg_auth_cit(tf);
target_fabric_setup_tpg_param_cit(tf);
target_fabric_setup_tpg_nacl_cit(tf);
target_fabric_setup_tpg_nacl_base_cit(tf);
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 3240f2c..bd78faf 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -53,18 +53,28 @@
struct list_head dest_list;
};
-int core_pr_dump_initiator_port(
+void core_pr_dump_initiator_port(
struct t10_pr_registration *pr_reg,
char *buf,
u32 size)
{
if (!pr_reg->isid_present_at_reg)
- return 0;
+ buf[0] = '\0';
- snprintf(buf, size, ",i,0x%s", &pr_reg->pr_reg_isid[0]);
- return 1;
+ snprintf(buf, size, ",i,0x%s", pr_reg->pr_reg_isid);
}
+enum register_type {
+ REGISTER,
+ REGISTER_AND_IGNORE_EXISTING_KEY,
+ REGISTER_AND_MOVE,
+};
+
+enum preempt_type {
+ PREEMPT,
+ PREEMPT_AND_ABORT,
+};
+
static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *,
struct t10_pr_registration *, int);
@@ -596,14 +606,6 @@
return NULL;
}
- pr_reg->pr_aptpl_buf = kzalloc(dev->t10_pr.pr_aptpl_buf_len,
- GFP_ATOMIC);
- if (!pr_reg->pr_aptpl_buf) {
- pr_err("Unable to allocate pr_reg->pr_aptpl_buf\n");
- kmem_cache_free(t10_pr_reg_cache, pr_reg);
- return NULL;
- }
-
INIT_LIST_HEAD(&pr_reg->pr_reg_list);
INIT_LIST_HEAD(&pr_reg->pr_reg_abort_list);
INIT_LIST_HEAD(&pr_reg->pr_reg_aptpl_list);
@@ -794,7 +796,6 @@
pr_err("Unable to allocate struct t10_pr_registration\n");
return -ENOMEM;
}
- pr_reg->pr_aptpl_buf = kzalloc(pr_tmpl->pr_aptpl_buf_len, GFP_KERNEL);
INIT_LIST_HEAD(&pr_reg->pr_reg_list);
INIT_LIST_HEAD(&pr_reg->pr_reg_abort_list);
@@ -848,11 +849,9 @@
struct t10_pr_registration *pr_reg)
{
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
spin_lock(&dev->dev_reservation_lock);
dev->dev_pr_res_holder = pr_reg;
@@ -865,11 +864,11 @@
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
pr_debug("SPC-3 PR [%s] RESERVE Node: %s%s\n",
tpg->se_tpg_tfo->get_fabric_name(), node_acl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ i_buf);
}
static void __core_scsi3_add_registration(struct se_device *, struct se_node_acl *,
- struct t10_pr_registration *, int, int);
+ struct t10_pr_registration *, enum register_type, int);
static int __core_scsi3_check_aptpl_registration(
struct se_device *dev,
@@ -962,21 +961,19 @@
struct se_device *dev,
struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg,
- int register_type)
+ enum register_type register_type)
{
struct se_portal_group *se_tpg = nacl->se_tpg;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(&i_buf[0], 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
pr_debug("SPC-3 PR [%s] Service Action: REGISTER%s Initiator"
- " Node: %s%s\n", tfo->get_fabric_name(), (register_type == 2) ?
- "_AND_MOVE" : (register_type == 1) ?
+ " Node: %s%s\n", tfo->get_fabric_name(), (register_type == REGISTER_AND_MOVE) ?
+ "_AND_MOVE" : (register_type == REGISTER_AND_IGNORE_EXISTING_KEY) ?
"_AND_IGNORE_EXISTING_KEY" : "", nacl->initiatorname,
- (prf_isid) ? i_buf : "");
+ i_buf);
pr_debug("SPC-3 PR [%s] registration on Target Port: %s,0x%04x\n",
tfo->get_fabric_name(), tfo->tpg_get_wwn(se_tpg),
tfo->tpg_get_tag(se_tpg));
@@ -998,7 +995,7 @@
struct se_device *dev,
struct se_node_acl *nacl,
struct t10_pr_registration *pr_reg,
- int register_type,
+ enum register_type register_type,
int register_move)
{
struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo;
@@ -1064,7 +1061,7 @@
u64 sa_res_key,
int all_tg_pt,
int aptpl,
- int register_type,
+ enum register_type register_type,
int register_move)
{
struct t10_pr_registration *pr_reg;
@@ -1225,11 +1222,9 @@
pr_reg->pr_reg_nacl->se_tpg->se_tpg_tfo;
struct t10_reservation *pr_tmpl = &dev->t10_pr;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
pr_reg->pr_reg_deve->def_pr_registered = 0;
pr_reg->pr_reg_deve->pr_res_key = 0;
@@ -1257,7 +1252,7 @@
pr_debug("SPC-3 PR [%s] Service Action: UNREGISTER Initiator"
" Node: %s%s\n", tfo->get_fabric_name(),
pr_reg->pr_reg_nacl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ i_buf);
pr_debug("SPC-3 PR [%s] for %s TCM Subsystem %s Object Target"
" Port(s)\n", tfo->get_fabric_name(),
(pr_reg->pr_reg_all_tg_pt) ? "ALL" : "SINGLE",
@@ -1269,7 +1264,6 @@
if (!preempt_and_abort_list) {
pr_reg->pr_reg_deve = NULL;
pr_reg->pr_reg_nacl = NULL;
- kfree(pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, pr_reg);
return;
}
@@ -1338,7 +1332,6 @@
list_for_each_entry_safe(pr_reg, pr_reg_tmp, &pr_tmpl->aptpl_reg_list,
pr_reg_aptpl_list) {
list_del(&pr_reg->pr_reg_aptpl_list);
- kfree(pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, pr_reg);
}
spin_unlock(&pr_tmpl->aptpl_reg_lock);
@@ -1453,7 +1446,7 @@
char *iport_ptr = NULL, dest_iport[64], i_buf[PR_REG_ISID_ID_LEN];
sense_reason_t ret;
u32 tpdl, tid_len = 0;
- int dest_local_nexus, prf_isid;
+ int dest_local_nexus;
u32 dest_rtpi = 0;
memset(dest_iport, 0, 64);
@@ -1764,8 +1757,7 @@
kfree(tidh);
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(dest_pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(dest_pr_reg, i_buf, PR_REG_ISID_ID_LEN);
__core_scsi3_add_registration(cmd->se_dev, dest_node_acl,
dest_pr_reg, 0, 0);
@@ -1773,8 +1765,7 @@
pr_debug("SPC-3 PR [%s] SPEC_I_PT: Successfully"
" registered Transport ID for Node: %s%s Mapped LUN:"
" %u\n", dest_tpg->se_tpg_tfo->get_fabric_name(),
- dest_node_acl->initiatorname, (prf_isid) ?
- &i_buf[0] : "", dest_se_deve->mapped_lun);
+ dest_node_acl->initiatorname, i_buf, dest_se_deve->mapped_lun);
if (dest_local_nexus)
continue;
@@ -1813,7 +1804,6 @@
kmem_cache_free(t10_pr_reg_cache, pr_reg_tmp);
}
- kfree(dest_pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, dest_pr_reg);
if (dest_local_nexus)
@@ -1826,14 +1816,10 @@
return ret;
}
-/*
- * Called with struct se_device->dev_reservation_lock held
- */
-static int __core_scsi3_update_aptpl_buf(
+static int core_scsi3_update_aptpl_buf(
struct se_device *dev,
unsigned char *buf,
- u32 pr_aptpl_buf_len,
- int clear_aptpl_metadata)
+ u32 pr_aptpl_buf_len)
{
struct se_lun *lun;
struct se_portal_group *tpg;
@@ -1841,20 +1827,13 @@
unsigned char tmp[512], isid_buf[32];
ssize_t len = 0;
int reg_count = 0;
+ int ret = 0;
- memset(buf, 0, pr_aptpl_buf_len);
- /*
- * Called to clear metadata once APTPL has been deactivated.
- */
- if (clear_aptpl_metadata) {
- snprintf(buf, pr_aptpl_buf_len,
- "No Registrations or Reservations\n");
- return 0;
- }
+ spin_lock(&dev->dev_reservation_lock);
+ spin_lock(&dev->t10_pr.registration_lock);
/*
* Walk the registration list..
*/
- spin_lock(&dev->t10_pr.registration_lock);
list_for_each_entry(pr_reg, &dev->t10_pr.registration_list,
pr_reg_list) {
@@ -1900,8 +1879,8 @@
if ((len + strlen(tmp) >= pr_aptpl_buf_len)) {
pr_err("Unable to update renaming"
" APTPL metadata\n");
- spin_unlock(&dev->t10_pr.registration_lock);
- return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ goto out;
}
len += sprintf(buf+len, "%s", tmp);
@@ -1918,48 +1897,32 @@
if ((len + strlen(tmp) >= pr_aptpl_buf_len)) {
pr_err("Unable to update renaming"
" APTPL metadata\n");
- spin_unlock(&dev->t10_pr.registration_lock);
- return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ goto out;
}
len += sprintf(buf+len, "%s", tmp);
reg_count++;
}
- spin_unlock(&dev->t10_pr.registration_lock);
if (!reg_count)
len += sprintf(buf+len, "No Registrations or Reservations");
- return 0;
-}
-
-static int core_scsi3_update_aptpl_buf(
- struct se_device *dev,
- unsigned char *buf,
- u32 pr_aptpl_buf_len,
- int clear_aptpl_metadata)
-{
- int ret;
-
- spin_lock(&dev->dev_reservation_lock);
- ret = __core_scsi3_update_aptpl_buf(dev, buf, pr_aptpl_buf_len,
- clear_aptpl_metadata);
+out:
+ spin_unlock(&dev->t10_pr.registration_lock);
spin_unlock(&dev->dev_reservation_lock);
return ret;
}
-/*
- * Called with struct se_device->aptpl_file_mutex held
- */
static int __core_scsi3_write_aptpl_to_file(
struct se_device *dev,
- unsigned char *buf,
- u32 pr_aptpl_buf_len)
+ unsigned char *buf)
{
struct t10_wwn *wwn = &dev->t10_wwn;
struct file *file;
int flags = O_RDWR | O_CREAT | O_TRUNC;
char path[512];
+ u32 pr_aptpl_buf_len;
int ret;
memset(path, 0, 512);
@@ -1978,8 +1941,7 @@
return PTR_ERR(file);
}
- if (!pr_aptpl_buf_len)
- pr_aptpl_buf_len = (strlen(&buf[0]) + 1); /* Add extra for NULL */
+ pr_aptpl_buf_len = (strlen(buf) + 1); /* Add extra for NULL */
ret = kernel_write(file, buf, pr_aptpl_buf_len, 0);
@@ -1990,60 +1952,64 @@
return ret ? -EIO : 0;
}
-static int
-core_scsi3_update_and_write_aptpl(struct se_device *dev, unsigned char *in_buf,
- u32 in_pr_aptpl_buf_len)
+/*
+ * Clear the APTPL metadata if APTPL has been disabled, otherwise
+ * write out the updated metadata to struct file for this SCSI device.
+ */
+static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl)
{
- unsigned char null_buf[64], *buf;
- u32 pr_aptpl_buf_len;
- int clear_aptpl_metadata = 0;
- int ret;
+ unsigned char *buf;
+ int rc;
- /*
- * Can be called with a NULL pointer from PROUT service action CLEAR
- */
- if (!in_buf) {
- memset(null_buf, 0, 64);
- buf = &null_buf[0];
- /*
- * This will clear the APTPL metadata to:
- * "No Registrations or Reservations" status
- */
- pr_aptpl_buf_len = 64;
- clear_aptpl_metadata = 1;
- } else {
- buf = in_buf;
- pr_aptpl_buf_len = in_pr_aptpl_buf_len;
+ if (!aptpl) {
+ char *null_buf = "No Registrations or Reservations\n";
+
+ rc = __core_scsi3_write_aptpl_to_file(dev, null_buf);
+ dev->t10_pr.pr_aptpl_active = 0;
+ pr_debug("SPC-3 PR: Set APTPL Bit Deactivated\n");
+
+ if (rc)
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+
+ return 0;
}
- ret = core_scsi3_update_aptpl_buf(dev, buf, pr_aptpl_buf_len,
- clear_aptpl_metadata);
- if (ret != 0)
- return ret;
+ buf = kzalloc(PR_APTPL_BUF_LEN, GFP_KERNEL);
+ if (!buf)
+ return TCM_OUT_OF_RESOURCES;
- /*
- * __core_scsi3_write_aptpl_to_file() will call strlen()
- * on the passed buf to determine pr_aptpl_buf_len.
- */
- return __core_scsi3_write_aptpl_to_file(dev, buf, 0);
+ rc = core_scsi3_update_aptpl_buf(dev, buf, PR_APTPL_BUF_LEN);
+ if (rc < 0) {
+ kfree(buf);
+ return TCM_OUT_OF_RESOURCES;
+ }
+
+ rc = __core_scsi3_write_aptpl_to_file(dev, buf);
+ if (rc != 0) {
+ pr_err("SPC-3 PR: Could not update APTPL\n");
+ kfree(buf);
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ dev->t10_pr.pr_aptpl_active = 1;
+ kfree(buf);
+ pr_debug("SPC-3 PR: Set APTPL Bit Activated\n");
+ return 0;
}
static sense_reason_t
core_scsi3_emulate_pro_register(struct se_cmd *cmd, u64 res_key, u64 sa_res_key,
- int aptpl, int all_tg_pt, int spec_i_pt, int ignore_key)
+ bool aptpl, bool all_tg_pt, bool spec_i_pt, enum register_type register_type)
{
struct se_session *se_sess = cmd->se_sess;
struct se_device *dev = cmd->se_dev;
struct se_dev_entry *se_deve;
struct se_lun *se_lun = cmd->se_lun;
struct se_portal_group *se_tpg;
- struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_reg_tmp, *pr_reg_e;
+ struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_reg_tmp;
struct t10_reservation *pr_tmpl = &dev->t10_pr;
- /* Used for APTPL metadata w/ UNREGISTER */
- unsigned char *pr_aptpl_buf = NULL;
unsigned char isid_buf[PR_REG_ISID_LEN], *isid_ptr = NULL;
sense_reason_t ret = TCM_NO_SENSE;
- int pr_holder = 0, type;
+ int pr_holder = 0;
if (!se_sess || !se_lun) {
pr_err("SPC-3 PR: se_sess || struct se_lun is NULL!\n");
@@ -2061,8 +2027,8 @@
/*
* Follow logic from spc4r17 Section 5.7.7, Register Behaviors Table 47
*/
- pr_reg_e = core_scsi3_locate_pr_reg(dev, se_sess->se_node_acl, se_sess);
- if (!pr_reg_e) {
+ pr_reg = core_scsi3_locate_pr_reg(dev, se_sess->se_node_acl, se_sess);
+ if (!pr_reg) {
if (res_key) {
pr_warn("SPC-3 PR: Reservation Key non-zero"
" for SA REGISTER, returning CONFLICT\n");
@@ -2083,7 +2049,7 @@
if (core_scsi3_alloc_registration(cmd->se_dev,
se_sess->se_node_acl, se_deve, isid_ptr,
sa_res_key, all_tg_pt, aptpl,
- ignore_key, 0)) {
+ register_type, 0)) {
pr_err("Unable to allocate"
" struct t10_pr_registration\n");
return TCM_INVALID_PARAMETER_LIST;
@@ -2102,97 +2068,68 @@
if (ret != 0)
return ret;
}
- /*
- * Nothing left to do for the APTPL=0 case.
- */
- if (!aptpl) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(cmd->se_dev, NULL, 0);
- pr_debug("SPC-3 PR: Set APTPL Bit Deactivated for"
- " REGISTER\n");
- return 0;
- }
- /*
- * Locate the newly allocated local I_T Nexus *pr_reg, and
- * update the APTPL metadata information using its
- * preallocated *pr_reg->pr_aptpl_buf.
- */
- pr_reg = core_scsi3_locate_pr_reg(cmd->se_dev,
- se_sess->se_node_acl, se_sess);
- if (core_scsi3_update_and_write_aptpl(cmd->se_dev,
- &pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_tmpl->pr_aptpl_active = 1;
- pr_debug("SPC-3 PR: Set APTPL Bit Activated for REGISTER\n");
- }
-
- goto out_put_pr_reg;
+ return core_scsi3_update_and_write_aptpl(dev, aptpl);
}
- /*
- * Locate the existing *pr_reg via struct se_node_acl pointers
- */
- pr_reg = pr_reg_e;
- type = pr_reg->pr_res_type;
+ /* ok, existing registration */
- if (!ignore_key) {
- if (res_key != pr_reg->pr_res_key) {
- pr_err("SPC-3 PR REGISTER: Received"
- " res_key: 0x%016Lx does not match"
- " existing SA REGISTER res_key:"
- " 0x%016Lx\n", res_key,
- pr_reg->pr_res_key);
- ret = TCM_RESERVATION_CONFLICT;
- goto out_put_pr_reg;
- }
+ if ((register_type == REGISTER) && (res_key != pr_reg->pr_res_key)) {
+ pr_err("SPC-3 PR REGISTER: Received"
+ " res_key: 0x%016Lx does not match"
+ " existing SA REGISTER res_key:"
+ " 0x%016Lx\n", res_key,
+ pr_reg->pr_res_key);
+ ret = TCM_RESERVATION_CONFLICT;
+ goto out;
}
if (spec_i_pt) {
- pr_err("SPC-3 PR UNREGISTER: SPEC_I_PT"
- " set while sa_res_key=0\n");
+ pr_err("SPC-3 PR REGISTER: SPEC_I_PT"
+ " set on a registered nexus\n");
ret = TCM_INVALID_PARAMETER_LIST;
- goto out_put_pr_reg;
+ goto out;
}
/*
* An existing ALL_TG_PT=1 registration being released
* must also set ALL_TG_PT=1 in the incoming PROUT.
*/
- if (pr_reg->pr_reg_all_tg_pt && !(all_tg_pt)) {
- pr_err("SPC-3 PR UNREGISTER: ALL_TG_PT=1"
+ if (pr_reg->pr_reg_all_tg_pt && !all_tg_pt) {
+ pr_err("SPC-3 PR REGISTER: ALL_TG_PT=1"
" registration exists, but ALL_TG_PT=1 bit not"
" present in received PROUT\n");
ret = TCM_INVALID_CDB_FIELD;
- goto out_put_pr_reg;
+ goto out;
}
/*
- * Allocate APTPL metadata buffer used for UNREGISTER ops
+ * sa_res_key=1 Change Reservation Key for registered I_T Nexus.
*/
- if (aptpl) {
- pr_aptpl_buf = kzalloc(pr_tmpl->pr_aptpl_buf_len,
- GFP_KERNEL);
- if (!pr_aptpl_buf) {
- pr_err("Unable to allocate"
- " pr_aptpl_buf\n");
- ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
- goto out_put_pr_reg;
- }
- }
+ if (sa_res_key) {
+ /*
+ * Increment PRgeneration counter for struct se_device"
+ * upon a successful REGISTER, see spc4r17 section 6.3.2
+ * READ_KEYS service action.
+ */
+ pr_reg->pr_res_generation = core_scsi3_pr_generation(cmd->se_dev);
+ pr_reg->pr_res_key = sa_res_key;
+ pr_debug("SPC-3 PR [%s] REGISTER%s: Changed Reservation"
+ " Key for %s to: 0x%016Lx PRgeneration:"
+ " 0x%08x\n", cmd->se_tfo->get_fabric_name(),
+ (register_type == REGISTER_AND_IGNORE_EXISTING_KEY) ? "_AND_IGNORE_EXISTING_KEY" : "",
+ pr_reg->pr_reg_nacl->initiatorname,
+ pr_reg->pr_res_key, pr_reg->pr_res_generation);
- /*
- * sa_res_key=0 Unregister Reservation Key for registered I_T
- * Nexus sa_res_key=1 Change Reservation Key for registered I_T
- * Nexus.
- */
- if (!sa_res_key) {
+ } else {
+ /*
+ * sa_res_key=0 Unregister Reservation Key for registered I_T Nexus.
+ */
pr_holder = core_scsi3_check_implict_release(
cmd->se_dev, pr_reg);
if (pr_holder < 0) {
- kfree(pr_aptpl_buf);
ret = TCM_RESERVATION_CONFLICT;
- goto out_put_pr_reg;
+ goto out;
}
spin_lock(&pr_tmpl->registration_lock);
@@ -2237,8 +2174,8 @@
* RESERVATIONS RELEASED.
*/
if (pr_holder &&
- (type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY ||
- type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY)) {
+ (pr_reg->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_REGONLY ||
+ pr_reg->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_REGONLY)) {
list_for_each_entry(pr_reg_p,
&pr_tmpl->registration_list,
pr_reg_list) {
@@ -2250,60 +2187,13 @@
ASCQ_2AH_RESERVATIONS_RELEASED);
}
}
+
spin_unlock(&pr_tmpl->registration_lock);
-
- if (!aptpl) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(dev, NULL, 0);
- pr_debug("SPC-3 PR: Set APTPL Bit Deactivated"
- " for UNREGISTER\n");
- return 0;
- }
-
- if (!core_scsi3_update_and_write_aptpl(dev, &pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_tmpl->pr_aptpl_active = 1;
- pr_debug("SPC-3 PR: Set APTPL Bit Activated"
- " for UNREGISTER\n");
- }
-
- goto out_free_aptpl_buf;
}
- /*
- * Increment PRgeneration counter for struct se_device"
- * upon a successful REGISTER, see spc4r17 section 6.3.2
- * READ_KEYS service action.
- */
- pr_reg->pr_res_generation = core_scsi3_pr_generation(cmd->se_dev);
- pr_reg->pr_res_key = sa_res_key;
- pr_debug("SPC-3 PR [%s] REGISTER%s: Changed Reservation"
- " Key for %s to: 0x%016Lx PRgeneration:"
- " 0x%08x\n", cmd->se_tfo->get_fabric_name(),
- (ignore_key) ? "_AND_IGNORE_EXISTING_KEY" : "",
- pr_reg->pr_reg_nacl->initiatorname,
- pr_reg->pr_res_key, pr_reg->pr_res_generation);
+ ret = core_scsi3_update_and_write_aptpl(dev, aptpl);
- if (!aptpl) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(dev, NULL, 0);
- pr_debug("SPC-3 PR: Set APTPL Bit Deactivated"
- " for REGISTER\n");
- ret = 0;
- goto out_put_pr_reg;
- }
-
- if (!core_scsi3_update_and_write_aptpl(dev, &pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_tmpl->pr_aptpl_active = 1;
- pr_debug("SPC-3 PR: Set APTPL Bit Activated"
- " for REGISTER\n");
- }
-
-out_free_aptpl_buf:
- kfree(pr_aptpl_buf);
- ret = 0;
-out_put_pr_reg:
+out:
core_scsi3_put_pr_reg(pr_reg);
return ret;
}
@@ -2340,7 +2230,6 @@
struct t10_reservation *pr_tmpl = &dev->t10_pr;
char i_buf[PR_REG_ISID_ID_LEN];
sense_reason_t ret;
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
@@ -2466,8 +2355,7 @@
pr_reg->pr_res_type = type;
pr_reg->pr_res_holder = 1;
dev->dev_pr_res_holder = pr_reg;
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
pr_debug("SPC-3 PR [%s] Service Action: RESERVE created new"
" reservation holder TYPE: %s ALL_TG_PT: %d\n",
@@ -2476,17 +2364,11 @@
pr_debug("SPC-3 PR [%s] RESERVE Node: %s%s\n",
cmd->se_tfo->get_fabric_name(),
se_sess->se_node_acl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ i_buf);
spin_unlock(&dev->dev_reservation_lock);
- if (pr_tmpl->pr_aptpl_active) {
- if (!core_scsi3_update_and_write_aptpl(cmd->se_dev,
- &pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_debug("SPC-3 PR: Updated APTPL metadata"
- " for RESERVE\n");
- }
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
ret = 0;
out_put_pr_reg:
@@ -2524,11 +2406,9 @@
{
struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
* Go ahead and release the current PR reservation holder.
*/
@@ -2541,7 +2421,7 @@
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
pr_debug("SPC-3 PR [%s] RELEASE Node: %s%s\n",
tfo->get_fabric_name(), se_nacl->initiatorname,
- (prf_isid) ? &i_buf[0] : "");
+ i_buf);
/*
* Clear TYPE and SCOPE for the next PROUT Service Action: RESERVE
*/
@@ -2702,12 +2582,9 @@
spin_unlock(&pr_tmpl->registration_lock);
write_aptpl:
- if (pr_tmpl->pr_aptpl_active) {
- if (!core_scsi3_update_and_write_aptpl(cmd->se_dev,
- &pr_reg->pr_aptpl_buf[0], pr_tmpl->pr_aptpl_buf_len)) {
- pr_debug("SPC-3 PR: Updated APTPL metadata for RELEASE\n");
- }
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
+
out_put_pr_reg:
core_scsi3_put_pr_reg(pr_reg);
return ret;
@@ -2791,11 +2668,7 @@
pr_debug("SPC-3 PR [%s] Service Action: CLEAR complete\n",
cmd->se_tfo->get_fabric_name());
- if (pr_tmpl->pr_aptpl_active) {
- core_scsi3_update_and_write_aptpl(cmd->se_dev, NULL, 0);
- pr_debug("SPC-3 PR: Updated APTPL metadata"
- " for CLEAR\n");
- }
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, false);
core_scsi3_pr_generation(dev);
return 0;
@@ -2810,16 +2683,14 @@
struct list_head *preempt_and_abort_list,
int type,
int scope,
- int abort)
+ enum preempt_type preempt_type)
{
struct se_node_acl *nacl = pr_reg->pr_reg_nacl;
struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo;
char i_buf[PR_REG_ISID_ID_LEN];
- int prf_isid;
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
* Do an implict RELEASE of the existing reservation.
*/
@@ -2834,12 +2705,12 @@
pr_debug("SPC-3 PR [%s] Service Action: PREEMPT%s created new"
" reservation holder TYPE: %s ALL_TG_PT: %d\n",
- tfo->get_fabric_name(), (abort) ? "_AND_ABORT" : "",
+ tfo->get_fabric_name(), (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "",
core_scsi3_pr_dump_type(type),
(pr_reg->pr_reg_all_tg_pt) ? 1 : 0);
pr_debug("SPC-3 PR [%s] PREEMPT%s from Node: %s%s\n",
- tfo->get_fabric_name(), (abort) ? "_AND_ABORT" : "",
- nacl->initiatorname, (prf_isid) ? &i_buf[0] : "");
+ tfo->get_fabric_name(), (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "",
+ nacl->initiatorname, i_buf);
/*
* For PREEMPT_AND_ABORT, add the preempting reservation's
* struct t10_pr_registration to the list that will be compared
@@ -2869,14 +2740,13 @@
pr_reg->pr_reg_deve = NULL;
pr_reg->pr_reg_nacl = NULL;
- kfree(pr_reg->pr_aptpl_buf);
kmem_cache_free(t10_pr_reg_cache, pr_reg);
}
}
static sense_reason_t
core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
- u64 sa_res_key, int abort)
+ u64 sa_res_key, enum preempt_type preempt_type)
{
struct se_device *dev = cmd->se_dev;
struct se_node_acl *pr_reg_nacl;
@@ -2896,7 +2766,7 @@
if (!pr_reg_n) {
pr_err("SPC-3 PR: Unable to locate"
" PR_REGISTERED *pr_reg for PREEMPT%s\n",
- (abort) ? "_AND_ABORT" : "");
+ (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "");
return TCM_RESERVATION_CONFLICT;
}
if (pr_reg_n->pr_res_key != res_key) {
@@ -2965,7 +2835,7 @@
pr_reg_nacl = pr_reg->pr_reg_nacl;
pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
__core_scsi3_free_registration(dev, pr_reg,
- (abort) ? &preempt_and_abort_list :
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
NULL, calling_it_nexus);
released_regs++;
} else {
@@ -2993,7 +2863,7 @@
pr_reg_nacl = pr_reg->pr_reg_nacl;
pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
__core_scsi3_free_registration(dev, pr_reg,
- (abort) ? &preempt_and_abort_list :
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
NULL, 0);
released_regs++;
}
@@ -3022,24 +2892,17 @@
*/
if (pr_res_holder && all_reg && !(sa_res_key)) {
__core_scsi3_complete_pro_preempt(dev, pr_reg_n,
- (abort) ? &preempt_and_abort_list : NULL,
- type, scope, abort);
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL,
+ type, scope, preempt_type);
- if (abort)
+ if (preempt_type == PREEMPT_AND_ABORT)
core_scsi3_release_preempt_and_abort(
&preempt_and_abort_list, pr_reg_n);
}
spin_unlock(&dev->dev_reservation_lock);
- if (pr_tmpl->pr_aptpl_active) {
- if (!core_scsi3_update_and_write_aptpl(cmd->se_dev,
- &pr_reg_n->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_debug("SPC-3 PR: Updated APTPL"
- " metadata for PREEMPT%s\n", (abort) ?
- "_AND_ABORT" : "");
- }
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
core_scsi3_put_pr_reg(pr_reg_n);
core_scsi3_pr_generation(cmd->se_dev);
@@ -3103,7 +2966,7 @@
pr_reg_nacl = pr_reg->pr_reg_nacl;
pr_res_mapped_lun = pr_reg->pr_res_mapped_lun;
__core_scsi3_free_registration(dev, pr_reg,
- (abort) ? &preempt_and_abort_list : NULL,
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL,
calling_it_nexus);
/*
* e) Establish a unit attention condition for the initiator
@@ -3120,8 +2983,8 @@
* I_T nexus using the contents of the SCOPE and TYPE fields;
*/
__core_scsi3_complete_pro_preempt(dev, pr_reg_n,
- (abort) ? &preempt_and_abort_list : NULL,
- type, scope, abort);
+ (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list : NULL,
+ type, scope, preempt_type);
/*
* d) Process tasks as defined in 5.7.1;
* e) See above..
@@ -3161,20 +3024,14 @@
* been removed from the primary pr_reg list), except the
* new persistent reservation holder, the calling Initiator Port.
*/
- if (abort) {
+ if (preempt_type == PREEMPT_AND_ABORT) {
core_tmr_lun_reset(dev, NULL, &preempt_and_abort_list, cmd);
core_scsi3_release_preempt_and_abort(&preempt_and_abort_list,
pr_reg_n);
}
- if (pr_tmpl->pr_aptpl_active) {
- if (!core_scsi3_update_and_write_aptpl(cmd->se_dev,
- &pr_reg_n->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_debug("SPC-3 PR: Updated APTPL metadata for PREEMPT"
- "%s\n", abort ? "_AND_ABORT" : "");
- }
- }
+ if (pr_tmpl->pr_aptpl_active)
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, true);
core_scsi3_put_pr_reg(pr_reg_n);
core_scsi3_pr_generation(cmd->se_dev);
@@ -3183,7 +3040,7 @@
static sense_reason_t
core_scsi3_emulate_pro_preempt(struct se_cmd *cmd, int type, int scope,
- u64 res_key, u64 sa_res_key, int abort)
+ u64 res_key, u64 sa_res_key, enum preempt_type preempt_type)
{
switch (type) {
case PR_TYPE_WRITE_EXCLUSIVE:
@@ -3193,10 +3050,10 @@
case PR_TYPE_WRITE_EXCLUSIVE_ALLREG:
case PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
return core_scsi3_pro_preempt(cmd, type, scope, res_key,
- sa_res_key, abort);
+ sa_res_key, preempt_type);
default:
pr_err("SPC-3 PR: Unknown Service Action PREEMPT%s"
- " Type: 0x%02x\n", (abort) ? "_AND_ABORT" : "", type);
+ " Type: 0x%02x\n", (preempt_type == PREEMPT_AND_ABORT) ? "_AND_ABORT" : "", type);
return TCM_INVALID_CDB_FIELD;
}
}
@@ -3220,7 +3077,7 @@
unsigned char *initiator_str;
char *iport_ptr = NULL, dest_iport[64], i_buf[PR_REG_ISID_ID_LEN];
u32 tid_len, tmp_tid_len;
- int new_reg = 0, type, scope, matching_iname, prf_isid;
+ int new_reg = 0, type, scope, matching_iname;
sense_reason_t ret;
unsigned short rtpi;
unsigned char proto_ident;
@@ -3564,8 +3421,7 @@
dest_pr_reg->pr_res_holder = 1;
dest_pr_reg->pr_res_type = type;
pr_reg->pr_res_scope = scope;
- prf_isid = core_pr_dump_initiator_port(pr_reg, &i_buf[0],
- PR_REG_ISID_ID_LEN);
+ core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);
/*
* Increment PRGeneration for existing registrations..
*/
@@ -3581,7 +3437,7 @@
pr_debug("SPC-3 PR Successfully moved reservation from"
" %s Fabric Node: %s%s -> %s Fabric Node: %s %s\n",
tf_ops->get_fabric_name(), pr_reg_nacl->initiatorname,
- (prf_isid) ? &i_buf[0] : "", dest_tf_ops->get_fabric_name(),
+ i_buf, dest_tf_ops->get_fabric_name(),
dest_node_acl->initiatorname, (iport_ptr != NULL) ?
iport_ptr : "");
/*
@@ -3602,24 +3458,7 @@
} else
core_scsi3_put_pr_reg(pr_reg);
- /*
- * Clear the APTPL metadata if APTPL has been disabled, otherwise
- * write out the updated metadata to struct file for this SCSI device.
- */
- if (!aptpl) {
- pr_tmpl->pr_aptpl_active = 0;
- core_scsi3_update_and_write_aptpl(cmd->se_dev, NULL, 0);
- pr_debug("SPC-3 PR: Set APTPL Bit Deactivated for"
- " REGISTER_AND_MOVE\n");
- } else {
- pr_tmpl->pr_aptpl_active = 1;
- if (!core_scsi3_update_and_write_aptpl(cmd->se_dev,
- &dest_pr_reg->pr_aptpl_buf[0],
- pr_tmpl->pr_aptpl_buf_len)) {
- pr_debug("SPC-3 PR: Set APTPL Bit Activated for"
- " REGISTER_AND_MOVE\n");
- }
- }
+ core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl);
transport_kunmap_data_sg(cmd);
@@ -3752,7 +3591,7 @@
switch (sa) {
case PRO_REGISTER:
ret = core_scsi3_emulate_pro_register(cmd,
- res_key, sa_res_key, aptpl, all_tg_pt, spec_i_pt, 0);
+ res_key, sa_res_key, aptpl, all_tg_pt, spec_i_pt, REGISTER);
break;
case PRO_RESERVE:
ret = core_scsi3_emulate_pro_reserve(cmd, type, scope, res_key);
@@ -3765,15 +3604,15 @@
break;
case PRO_PREEMPT:
ret = core_scsi3_emulate_pro_preempt(cmd, type, scope,
- res_key, sa_res_key, 0);
+ res_key, sa_res_key, PREEMPT);
break;
case PRO_PREEMPT_AND_ABORT:
ret = core_scsi3_emulate_pro_preempt(cmd, type, scope,
- res_key, sa_res_key, 1);
+ res_key, sa_res_key, PREEMPT_AND_ABORT);
break;
case PRO_REGISTER_AND_IGNORE_EXISTING_KEY:
ret = core_scsi3_emulate_pro_register(cmd,
- 0, sa_res_key, aptpl, all_tg_pt, spec_i_pt, 1);
+ 0, sa_res_key, aptpl, all_tg_pt, spec_i_pt, REGISTER_AND_IGNORE_EXISTING_KEY);
break;
case PRO_REGISTER_AND_MOVE:
ret = core_scsi3_emulate_pro_register_and_move(cmd, res_key,
diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h
index b4a0042..ed75cdd 100644
--- a/drivers/target/target_core_pr.h
+++ b/drivers/target/target_core_pr.h
@@ -45,7 +45,7 @@
extern struct kmem_cache *t10_pr_reg_cache;
-extern int core_pr_dump_initiator_port(struct t10_pr_registration *,
+extern void core_pr_dump_initiator_port(struct t10_pr_registration *,
char *, u32);
extern sense_reason_t target_scsi2_reservation_release(struct se_cmd *);
extern sense_reason_t target_scsi2_reservation_reserve(struct se_cmd *);
diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
index 0921a64..51127d1 100644
--- a/drivers/target/target_core_rd.c
+++ b/drivers/target/target_core_rd.c
@@ -139,6 +139,11 @@
rd_dev->rd_page_count);
return -EINVAL;
}
+
+ /* Don't need backing pages for NULLIO */
+ if (rd_dev->rd_flags & RDF_NULLIO)
+ return 0;
+
total_sg_needed = rd_dev->rd_page_count;
sg_tables = (total_sg_needed / max_sg_per_table) + 1;
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index bbc5b0e..8a46277 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -38,11 +38,27 @@
sbc_emulate_readcapacity(struct se_cmd *cmd)
{
struct se_device *dev = cmd->se_dev;
+ unsigned char *cdb = cmd->t_task_cdb;
unsigned long long blocks_long = dev->transport->get_blocks(dev);
unsigned char *rbuf;
unsigned char buf[8];
u32 blocks;
+ /*
+ * SBC-2 says:
+ * If the PMI bit is set to zero and the LOGICAL BLOCK
+ * ADDRESS field is not set to zero, the device server shall
+ * terminate the command with CHECK CONDITION status with
+ * the sense key set to ILLEGAL REQUEST and the additional
+ * sense code set to INVALID FIELD IN CDB.
+ *
+ * In SBC-3, these fields are obsolete, but some SCSI
+ * compliance tests actually check this, so we might as well
+ * follow SBC-2.
+ */
+ if (!(cdb[8] & 1) && !!(cdb[2] | cdb[3] | cdb[4] | cdb[5]))
+ return TCM_INVALID_CDB_FIELD;
+
if (blocks_long >= 0x00000000ffffffff)
blocks = 0xffffffff;
else
@@ -581,7 +597,7 @@
pr_err("cmd exceeds last lba %llu "
"(lba %llu, sectors %u)\n",
end_lba, cmd->t_task_lba, sectors);
- return TCM_INVALID_CDB_FIELD;
+ return TCM_ADDRESS_OUT_OF_RANGE;
}
size = sbc_get_size(cmd, sectors);
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index d0b4dd9..0d7cacb 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -85,13 +85,8 @@
static void core_tmr_handle_tas_abort(
struct se_node_acl *tmr_nacl,
struct se_cmd *cmd,
- int tas,
- int fe_count)
+ int tas)
{
- if (!fe_count) {
- transport_cmd_finish_abort(cmd, 1);
- return;
- }
/*
* TASK ABORTED status (TAS) bit support
*/
@@ -253,7 +248,6 @@
LIST_HEAD(drain_task_list);
struct se_cmd *cmd, *next;
unsigned long flags;
- int fe_count;
/*
* Complete outstanding commands with TASK_ABORTED SAM status.
@@ -329,12 +323,10 @@
spin_lock_irqsave(&cmd->t_state_lock, flags);
target_stop_cmd(cmd, &flags);
- fe_count = atomic_read(&cmd->t_fe_count);
-
cmd->transport_state |= CMD_T_ABORTED;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
+ core_tmr_handle_tas_abort(tmr_nacl, cmd, tas);
}
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 21e3158..7172d00 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -52,6 +52,9 @@
#include "target_core_pr.h"
#include "target_core_ua.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/target.h>
+
static struct workqueue_struct *target_completion_wq;
static struct kmem_cache *se_sess_cache;
struct kmem_cache *se_ua_cache;
@@ -446,11 +449,15 @@
spin_unlock_irqrestore(&dev->execute_task_lock, flags);
}
-static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists)
+static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
+ bool write_pending)
{
unsigned long flags;
spin_lock_irqsave(&cmd->t_state_lock, flags);
+ if (write_pending)
+ cmd->t_state = TRANSPORT_WRITE_PENDING;
+
/*
* Determine if IOCTL context caller in requesting the stopping of this
* command for LUN shutdown purposes.
@@ -515,7 +522,7 @@
static int transport_cmd_check_stop_to_fabric(struct se_cmd *cmd)
{
- return transport_cmd_check_stop(cmd, true);
+ return transport_cmd_check_stop(cmd, true, false);
}
static void transport_lun_remove_cmd(struct se_cmd *cmd)
@@ -526,13 +533,6 @@
if (!lun)
return;
- spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (cmd->transport_state & CMD_T_DEV_ACTIVE) {
- cmd->transport_state &= ~CMD_T_DEV_ACTIVE;
- target_remove_from_state_list(cmd);
- }
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
spin_lock_irqsave(&lun->lun_cmd_lock, flags);
if (!list_empty(&cmd->se_lun_node))
list_del_init(&cmd->se_lun_node);
@@ -1092,7 +1092,6 @@
target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb)
{
struct se_device *dev = cmd->se_dev;
- unsigned long flags;
sense_reason_t ret;
/*
@@ -1127,6 +1126,8 @@
*/
memcpy(cmd->t_task_cdb, cdb, scsi_command_size(cdb));
+ trace_target_sequencer_start(cmd);
+
/*
* Check for an existing UNIT ATTENTION condition
*/
@@ -1152,9 +1153,7 @@
if (ret)
return ret;
- spin_lock_irqsave(&cmd->t_state_lock, flags);
cmd->se_cmd_flags |= SCF_SUPPORTED_SAM_OPCODE;
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
spin_lock(&cmd->se_lun->lun_sep_lock);
if (cmd->se_lun->lun_sep)
@@ -1552,7 +1551,8 @@
cmd->orig_fe_lun, 0x2C,
ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS);
- ret = cmd->se_tfo->queue_status(cmd);
+ trace_target_cmd_complete(cmd);
+ ret = cmd->se_tfo-> queue_status(cmd);
if (ret == -EAGAIN || ret == -ENOMEM)
goto queue_full;
goto check_stop;
@@ -1583,10 +1583,6 @@
{
sense_reason_t ret;
- spin_lock_irq(&cmd->t_state_lock);
- cmd->transport_state |= (CMD_T_BUSY|CMD_T_SENT);
- spin_unlock_irq(&cmd->t_state_lock);
-
if (cmd->execute_cmd) {
ret = cmd->execute_cmd(cmd);
if (ret) {
@@ -1693,11 +1689,17 @@
}
cmd->t_state = TRANSPORT_PROCESSING;
- cmd->transport_state |= CMD_T_ACTIVE;
+ cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
spin_unlock_irq(&cmd->t_state_lock);
- if (!target_handle_task_attr(cmd))
- __target_execute_cmd(cmd);
+ if (target_handle_task_attr(cmd)) {
+ spin_lock_irq(&cmd->t_state_lock);
+ cmd->transport_state &= ~CMD_T_BUSY|CMD_T_SENT;
+ spin_unlock_irq(&cmd->t_state_lock);
+ return;
+ }
+
+ __target_execute_cmd(cmd);
}
EXPORT_SYMBOL(target_execute_cmd);
@@ -1770,6 +1772,7 @@
transport_complete_task_attr(cmd);
if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) {
+ trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_status(cmd);
if (ret)
goto out;
@@ -1777,6 +1780,7 @@
switch (cmd->data_direction) {
case DMA_FROM_DEVICE:
+ trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_data_in(cmd);
break;
case DMA_TO_DEVICE:
@@ -1787,6 +1791,7 @@
}
/* Fall through for DMA_TO_DEVICE */
case DMA_NONE:
+ trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_status(cmd);
break;
default:
@@ -1865,6 +1870,7 @@
}
spin_unlock(&cmd->se_lun->lun_sep_lock);
+ trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_data_in(cmd);
if (ret == -EAGAIN || ret == -ENOMEM)
goto queue_full;
@@ -1893,6 +1899,7 @@
}
/* Fall through for DMA_TO_DEVICE */
case DMA_NONE:
+ trace_target_cmd_complete(cmd);
ret = cmd->se_tfo->queue_status(cmd);
if (ret == -EAGAIN || ret == -ENOMEM)
goto queue_full;
@@ -1956,11 +1963,7 @@
* If this cmd has been setup with target_get_sess_cmd(), drop
* the kref and call ->release_cmd() in kref callback.
*/
- if (cmd->check_release != 0)
- return target_put_sess_cmd(cmd->se_sess, cmd);
-
- cmd->se_tfo->release_cmd(cmd);
- return 1;
+ return target_put_sess_cmd(cmd->se_sess, cmd);
}
/**
@@ -1971,21 +1974,6 @@
*/
static int transport_put_cmd(struct se_cmd *cmd)
{
- unsigned long flags;
-
- spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (atomic_read(&cmd->t_fe_count) &&
- !atomic_dec_and_test(&cmd->t_fe_count)) {
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- return 0;
- }
-
- if (cmd->transport_state & CMD_T_DEV_ACTIVE) {
- cmd->transport_state &= ~CMD_T_DEV_ACTIVE;
- target_remove_from_state_list(cmd);
- }
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
transport_free_pages(cmd);
return transport_release_cmd(cmd);
}
@@ -2103,9 +2091,6 @@
if (ret < 0)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
-
- atomic_inc(&cmd->t_fe_count);
-
/*
* If this command is not a write we can execute it right here,
* for write buffers we need to notify the fabric driver first
@@ -2116,12 +2101,7 @@
target_execute_cmd(cmd);
return 0;
}
-
- spin_lock_irq(&cmd->t_state_lock);
- cmd->t_state = TRANSPORT_WRITE_PENDING;
- spin_unlock_irq(&cmd->t_state_lock);
-
- transport_cmd_check_stop(cmd, false);
+ transport_cmd_check_stop(cmd, false, true);
ret = cmd->se_tfo->write_pending(cmd);
if (ret == -EAGAIN || ret == -ENOMEM)
@@ -2202,8 +2182,6 @@
goto out;
}
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
- se_cmd->check_release = 1;
-
out:
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
return ret;
@@ -2319,7 +2297,7 @@
pr_debug("ConfigFS ITT[0x%08x] - CMD_T_STOP, skipping\n",
cmd->se_tfo->get_task_tag(cmd));
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- transport_cmd_check_stop(cmd, false);
+ transport_cmd_check_stop(cmd, false, false);
return -EPERM;
}
cmd->transport_state |= CMD_T_LUN_FE_STOP;
@@ -2427,7 +2405,7 @@
spin_unlock_irqrestore(&cmd->t_state_lock,
cmd_flags);
- transport_cmd_check_stop(cmd, false);
+ transport_cmd_check_stop(cmd, false, false);
complete(&cmd->transport_lun_fe_stop_comp);
spin_lock_irqsave(&lun->lun_cmd_lock, lun_flags);
continue;
@@ -2778,6 +2756,7 @@
cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER;
after_reason:
+ trace_target_cmd_complete(cmd);
return cmd->se_tfo->queue_status(cmd);
}
EXPORT_SYMBOL(transport_send_check_condition_and_sense);
@@ -2794,6 +2773,7 @@
cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd));
cmd->se_cmd_flags |= SCF_SENT_DELAYED_TAS;
+ trace_target_cmd_complete(cmd);
cmd->se_tfo->queue_status(cmd);
return 1;
@@ -2831,6 +2811,7 @@
" ITT: 0x%08x\n", cmd->t_task_cdb[0],
cmd->se_tfo->get_task_tag(cmd));
+ trace_target_cmd_complete(cmd);
cmd->se_tfo->queue_status(cmd);
}
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index eea6935..0dd54a4 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -161,7 +161,7 @@
int ft_write_pending_status(struct se_cmd *);
u32 ft_get_task_tag(struct se_cmd *);
int ft_get_cmd_state(struct se_cmd *);
-int ft_queue_tm_resp(struct se_cmd *);
+void ft_queue_tm_resp(struct se_cmd *);
/*
* other internal functions.
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index b406f17..0e5a1cae 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -394,14 +394,14 @@
/*
* Send status from completed task management request.
*/
-int ft_queue_tm_resp(struct se_cmd *se_cmd)
+void ft_queue_tm_resp(struct se_cmd *se_cmd)
{
struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
struct se_tmr_req *tmr = se_cmd->se_tmr_req;
enum fcp_resp_rsp_codes code;
if (cmd->aborted)
- return 0;
+ return;
switch (tmr->response) {
case TMR_FUNCTION_COMPLETE:
code = FCP_TMF_CMPL;
@@ -413,10 +413,7 @@
code = FCP_TMF_REJECTED;
break;
case TMR_TASK_DOES_NOT_EXIST:
- case TMR_TASK_STILL_ALLEGIANT:
- case TMR_TASK_FAILOVER_NOT_SUPPORTED:
case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
- case TMR_FUNCTION_AUTHORIZATION_FAILED:
default:
code = FCP_TMF_FAILED;
break;
@@ -424,7 +421,6 @@
pr_debug("tmr fn %d resp %d fcp code %d\n",
tmr->function, tmr->response, code);
ft_send_resp_code(cmd, code);
- return 0;
}
static void ft_send_work(struct work_struct *work);
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 5e3c025..e988c81 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -169,4 +169,19 @@
enforce idle time which results in more package C-state residency. The
user interface is exposed via generic thermal framework.
+config X86_PKG_TEMP_THERMAL
+ tristate "X86 package temperature thermal driver"
+ depends on X86_THERMAL_VECTOR
+ select THERMAL_GOV_USER_SPACE
+ default m
+ help
+ Enable this to register CPU digital sensor for package temperature as
+ thermal zone. Each package will have its own thermal zone. There are
+ two trip points which can be set by user to get notifications via thermal
+ notification methods.
+
+menu "Texas Instruments thermal drivers"
+source "drivers/thermal/ti-soc-thermal/Kconfig"
+endmenu
+
endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index c054d41..67184a2 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -23,4 +23,5 @@
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
-
+obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
+obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c
index 54ffd64..5e53212 100644
--- a/drivers/thermal/armada_thermal.c
+++ b/drivers/thermal/armada_thermal.c
@@ -200,7 +200,6 @@
platform_get_drvdata(pdev);
thermal_zone_device_unregister(armada_thermal);
- platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -211,7 +210,7 @@
.driver = {
.name = "armada_thermal",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(armada_thermal_id_table),
+ .of_match_table = armada_thermal_id_table,
},
};
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index c94bf2e..82e15db 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -167,7 +167,7 @@
continue;
/* get the frequency order */
- if (freq != CPUFREQ_ENTRY_INVALID && descend != -1)
+ if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
descend = !!(freq > table[i].frequency);
freq = table[i].frequency;
diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c
index a088d13..828f5e3 100644
--- a/drivers/thermal/dove_thermal.c
+++ b/drivers/thermal/dove_thermal.c
@@ -134,16 +134,11 @@
struct resource *res;
int ret;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "Failed to get platform resource\n");
- return -ENODEV;
- }
-
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->sensor = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->sensor))
return PTR_ERR(priv->sensor);
@@ -178,7 +173,6 @@
platform_get_drvdata(pdev);
thermal_zone_device_unregister(dove_thermal);
- platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -191,7 +185,7 @@
.driver = {
.name = "dove_thermal",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(dove_thermal_id_table),
+ .of_match_table = dove_thermal_id_table,
},
};
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index 4cbe3ee..9af4b93 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -997,7 +997,6 @@
return 0;
err_clk:
- platform_set_drvdata(pdev, NULL);
clk_unprepare(data->clk);
return ret;
}
@@ -1012,8 +1011,6 @@
clk_unprepare(data->clk);
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c
index dfeceaf..3b034a0 100644
--- a/drivers/thermal/kirkwood_thermal.c
+++ b/drivers/thermal/kirkwood_thermal.c
@@ -75,16 +75,11 @@
struct kirkwood_thermal_priv *priv;
struct resource *res;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "Failed to get platform resource\n");
- return -ENODEV;
- }
-
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->sensor = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->sensor))
return PTR_ERR(priv->sensor);
@@ -108,7 +103,6 @@
platform_get_drvdata(pdev);
thermal_zone_device_unregister(kirkwood_thermal);
- platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -121,7 +115,7 @@
.driver = {
.name = "kirkwood_thermal",
.owner = THIS_MODULE,
- .of_match_table = of_match_ptr(kirkwood_thermal_id_table),
+ .of_match_table = kirkwood_thermal_id_table,
},
};
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index 8d7edd4..88f92e1 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -389,11 +389,6 @@
* platform has IRQ support.
* Then, drier use common register
*/
- res = platform_get_resource(pdev, IORESOURCE_MEM, mres++);
- if (!res) {
- dev_err(dev, "Could not get platform resource\n");
- return -ENODEV;
- }
ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0,
dev_name(dev), common);
@@ -405,6 +400,7 @@
/*
* rcar_has_irq_support() will be enabled
*/
+ res = platform_get_resource(pdev, IORESOURCE_MEM, mres++);
common->base = devm_ioremap_resource(dev, res);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
@@ -458,7 +454,7 @@
platform_set_drvdata(pdev, common);
- dev_info(dev, "%d sensor proved\n", i);
+ dev_info(dev, "%d sensor probed\n", i);
return 0;
@@ -487,8 +483,6 @@
rcar_thermal_irq_disable(priv);
}
- platform_set_drvdata(pdev, NULL);
-
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c
index 3c5ee56..ab79ea4 100644
--- a/drivers/thermal/spear_thermal.c
+++ b/drivers/thermal/spear_thermal.c
@@ -104,7 +104,7 @@
struct thermal_zone_device *spear_thermal = NULL;
struct spear_thermal_dev *stdev;
struct device_node *np = pdev->dev.of_node;
- struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *res;
int ret = 0, val;
if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
@@ -112,11 +112,6 @@
return -EINVAL;
}
- if (!stres) {
- dev_err(&pdev->dev, "memory resource missing\n");
- return -ENODEV;
- }
-
stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL);
if (!stdev) {
dev_err(&pdev->dev, "kzalloc fail\n");
@@ -124,12 +119,10 @@
}
/* Enable thermal sensor */
- stdev->thermal_base = devm_ioremap(&pdev->dev, stres->start,
- resource_size(stres));
- if (!stdev->thermal_base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- return -ENOMEM;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ stdev->thermal_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(stdev->thermal_base))
+ return PTR_ERR(stdev->thermal_base);
stdev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(stdev->clk)) {
@@ -174,7 +167,6 @@
struct spear_thermal_dev *stdev = spear_thermal->devdata;
thermal_zone_device_unregister(spear_thermal);
- platform_set_drvdata(pdev, NULL);
/* Disable SPEAr Thermal Sensor */
actual_mask = readl_relaxed(stdev->thermal_base);
@@ -198,7 +190,7 @@
.name = "spear_thermal",
.owner = THIS_MODULE,
.pm = &spear_thermal_pm_ops,
- .of_match_table = of_match_ptr(spear_thermal_id_table),
+ .of_match_table = spear_thermal_id_table,
},
};
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index d755440..1f02e8e 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -33,6 +33,7 @@
#include <linux/idr.h>
#include <linux/thermal.h>
#include <linux/reboot.h>
+#include <linux/string.h>
#include <net/netlink.h>
#include <net/genetlink.h>
@@ -155,7 +156,8 @@
{
enum thermal_trend trend;
- if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) {
+ if (tz->emul_temperature || !tz->ops->get_trend ||
+ tz->ops->get_trend(tz, trip, &trend)) {
if (tz->temperature > tz->last_temperature)
trend = THERMAL_TREND_RAISING;
else if (tz->temperature < tz->last_temperature)
@@ -713,10 +715,13 @@
int ret = -EINVAL;
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_governor *gov;
+ char name[THERMAL_NAME_LENGTH];
+
+ snprintf(name, sizeof(name), "%s", buf);
mutex_lock(&thermal_governor_lock);
- gov = __find_governor(buf);
+ gov = __find_governor(strim(name));
if (!gov)
goto exit;
@@ -1624,7 +1629,7 @@
if (!ops || !ops->get_temp)
return ERR_PTR(-EINVAL);
- if (trips > 0 && !ops->get_trip_type)
+ if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
return ERR_PTR(-EINVAL);
tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
diff --git a/drivers/staging/ti-soc-thermal/Kconfig b/drivers/thermal/ti-soc-thermal/Kconfig
similarity index 80%
rename from drivers/staging/ti-soc-thermal/Kconfig
rename to drivers/thermal/ti-soc-thermal/Kconfig
index e81375f..bd4c7be 100644
--- a/drivers/staging/ti-soc-thermal/Kconfig
+++ b/drivers/thermal/ti-soc-thermal/Kconfig
@@ -46,3 +46,15 @@
This includes alert interrupts generation and also the TSHUT
support.
+
+config DRA752_THERMAL
+ bool "Texas Instruments DRA752 thermal support"
+ depends on TI_SOC_THERMAL
+ depends on SOC_DRA7XX
+ help
+ If you say yes here you get thermal support for the Texas Instruments
+ DRA752 SoC family. The current chip supported are:
+ - DRA752
+
+ This includes alert interrupts generation and also the TSHUT
+ support.
diff --git a/drivers/staging/ti-soc-thermal/Makefile b/drivers/thermal/ti-soc-thermal/Makefile
similarity index 80%
rename from drivers/staging/ti-soc-thermal/Makefile
rename to drivers/thermal/ti-soc-thermal/Makefile
index 0ca034f..1226b24 100644
--- a/drivers/staging/ti-soc-thermal/Makefile
+++ b/drivers/thermal/ti-soc-thermal/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal.o
ti-soc-thermal-y := ti-bandgap.o
ti-soc-thermal-$(CONFIG_TI_THERMAL) += ti-thermal-common.o
+ti-soc-thermal-$(CONFIG_DRA752_THERMAL) += dra752-thermal-data.o
ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o
ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o
diff --git a/drivers/staging/ti-soc-thermal/TODO b/drivers/thermal/ti-soc-thermal/TODO
similarity index 100%
rename from drivers/staging/ti-soc-thermal/TODO
rename to drivers/thermal/ti-soc-thermal/TODO
diff --git a/drivers/thermal/ti-soc-thermal/dra752-bandgap.h b/drivers/thermal/ti-soc-thermal/dra752-bandgap.h
new file mode 100644
index 0000000..6b0f2b1
--- /dev/null
+++ b/drivers/thermal/ti-soc-thermal/dra752-bandgap.h
@@ -0,0 +1,280 @@
+/*
+ * DRA752 bandgap registers, bitfields and temperature definitions
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ * Contact:
+ * Eduardo Valentin <eduardo.valentin@ti.com>
+ * Tero Kristo <t-kristo@ti.com>
+ *
+ * This is an auto generated file.
+ *
+ * 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
+ *
+ */
+#ifndef __DRA752_BANDGAP_H
+#define __DRA752_BANDGAP_H
+
+/**
+ * *** DRA752 ***
+ *
+ * Below, in sequence, are the Register definitions,
+ * the bitfields and the temperature definitions for DRA752.
+ */
+
+/**
+ * DRA752 register definitions
+ *
+ * Registers are defined as offsets. The offsets are
+ * relative to FUSE_OPP_BGAP_GPU on DRA752.
+ * DRA752_BANDGAP_BASE 0x4a0021e0
+ *
+ * Register below are grouped by domain (not necessarily in offset order)
+ */
+
+
+/* DRA752.common register offsets */
+#define DRA752_BANDGAP_CTRL_1_OFFSET 0x1a0
+#define DRA752_BANDGAP_STATUS_1_OFFSET 0x1c8
+#define DRA752_BANDGAP_CTRL_2_OFFSET 0x39c
+#define DRA752_BANDGAP_STATUS_2_OFFSET 0x3b8
+
+/* DRA752.core register offsets */
+#define DRA752_STD_FUSE_OPP_BGAP_CORE_OFFSET 0x8
+#define DRA752_TEMP_SENSOR_CORE_OFFSET 0x154
+#define DRA752_BANDGAP_THRESHOLD_CORE_OFFSET 0x1ac
+#define DRA752_BANDGAP_TSHUT_CORE_OFFSET 0x1b8
+#define DRA752_BANDGAP_CUMUL_DTEMP_CORE_OFFSET 0x1c4
+#define DRA752_DTEMP_CORE_0_OFFSET 0x208
+#define DRA752_DTEMP_CORE_1_OFFSET 0x20c
+#define DRA752_DTEMP_CORE_2_OFFSET 0x210
+#define DRA752_DTEMP_CORE_3_OFFSET 0x214
+#define DRA752_DTEMP_CORE_4_OFFSET 0x218
+
+/* DRA752.iva register offsets */
+#define DRA752_STD_FUSE_OPP_BGAP_IVA_OFFSET 0x388
+#define DRA752_TEMP_SENSOR_IVA_OFFSET 0x398
+#define DRA752_BANDGAP_THRESHOLD_IVA_OFFSET 0x3a4
+#define DRA752_BANDGAP_TSHUT_IVA_OFFSET 0x3ac
+#define DRA752_BANDGAP_CUMUL_DTEMP_IVA_OFFSET 0x3b4
+#define DRA752_DTEMP_IVA_0_OFFSET 0x3d0
+#define DRA752_DTEMP_IVA_1_OFFSET 0x3d4
+#define DRA752_DTEMP_IVA_2_OFFSET 0x3d8
+#define DRA752_DTEMP_IVA_3_OFFSET 0x3dc
+#define DRA752_DTEMP_IVA_4_OFFSET 0x3e0
+
+/* DRA752.mpu register offsets */
+#define DRA752_STD_FUSE_OPP_BGAP_MPU_OFFSET 0x4
+#define DRA752_TEMP_SENSOR_MPU_OFFSET 0x14c
+#define DRA752_BANDGAP_THRESHOLD_MPU_OFFSET 0x1a4
+#define DRA752_BANDGAP_TSHUT_MPU_OFFSET 0x1b0
+#define DRA752_BANDGAP_CUMUL_DTEMP_MPU_OFFSET 0x1bc
+#define DRA752_DTEMP_MPU_0_OFFSET 0x1e0
+#define DRA752_DTEMP_MPU_1_OFFSET 0x1e4
+#define DRA752_DTEMP_MPU_2_OFFSET 0x1e8
+#define DRA752_DTEMP_MPU_3_OFFSET 0x1ec
+#define DRA752_DTEMP_MPU_4_OFFSET 0x1f0
+
+/* DRA752.dspeve register offsets */
+#define DRA752_STD_FUSE_OPP_BGAP_DSPEVE_OFFSET 0x384
+#define DRA752_TEMP_SENSOR_DSPEVE_OFFSET 0x394
+#define DRA752_BANDGAP_THRESHOLD_DSPEVE_OFFSET 0x3a0
+#define DRA752_BANDGAP_TSHUT_DSPEVE_OFFSET 0x3a8
+#define DRA752_BANDGAP_CUMUL_DTEMP_DSPEVE_OFFSET 0x3b0
+#define DRA752_DTEMP_DSPEVE_0_OFFSET 0x3bc
+#define DRA752_DTEMP_DSPEVE_1_OFFSET 0x3c0
+#define DRA752_DTEMP_DSPEVE_2_OFFSET 0x3c4
+#define DRA752_DTEMP_DSPEVE_3_OFFSET 0x3c8
+#define DRA752_DTEMP_DSPEVE_4_OFFSET 0x3cc
+
+/* DRA752.gpu register offsets */
+#define DRA752_STD_FUSE_OPP_BGAP_GPU_OFFSET 0x0
+#define DRA752_TEMP_SENSOR_GPU_OFFSET 0x150
+#define DRA752_BANDGAP_THRESHOLD_GPU_OFFSET 0x1a8
+#define DRA752_BANDGAP_TSHUT_GPU_OFFSET 0x1b4
+#define DRA752_BANDGAP_CUMUL_DTEMP_GPU_OFFSET 0x1c0
+#define DRA752_DTEMP_GPU_0_OFFSET 0x1f4
+#define DRA752_DTEMP_GPU_1_OFFSET 0x1f8
+#define DRA752_DTEMP_GPU_2_OFFSET 0x1fc
+#define DRA752_DTEMP_GPU_3_OFFSET 0x200
+#define DRA752_DTEMP_GPU_4_OFFSET 0x204
+
+/**
+ * Register bitfields for DRA752
+ *
+ * All the macros bellow define the required bits for
+ * controlling temperature on DRA752. Bit defines are
+ * grouped by register.
+ */
+
+/* DRA752.BANDGAP_STATUS_1 */
+#define DRA752_BANDGAP_STATUS_1_ALERT_MASK BIT(31)
+#define DRA752_BANDGAP_STATUS_1_HOT_CORE_MASK BIT(5)
+#define DRA752_BANDGAP_STATUS_1_COLD_CORE_MASK BIT(4)
+#define DRA752_BANDGAP_STATUS_1_HOT_GPU_MASK BIT(3)
+#define DRA752_BANDGAP_STATUS_1_COLD_GPU_MASK BIT(2)
+#define DRA752_BANDGAP_STATUS_1_HOT_MPU_MASK BIT(1)
+#define DRA752_BANDGAP_STATUS_1_COLD_MPU_MASK BIT(0)
+
+/* DRA752.BANDGAP_CTRL_2 */
+#define DRA752_BANDGAP_CTRL_2_FREEZE_IVA_MASK BIT(22)
+#define DRA752_BANDGAP_CTRL_2_FREEZE_DSPEVE_MASK BIT(21)
+#define DRA752_BANDGAP_CTRL_2_CLEAR_IVA_MASK BIT(19)
+#define DRA752_BANDGAP_CTRL_2_CLEAR_DSPEVE_MASK BIT(18)
+#define DRA752_BANDGAP_CTRL_2_CLEAR_ACCUM_IVA_MASK BIT(16)
+#define DRA752_BANDGAP_CTRL_2_CLEAR_ACCUM_DSPEVE_MASK BIT(15)
+#define DRA752_BANDGAP_CTRL_2_MASK_HOT_IVA_MASK BIT(3)
+#define DRA752_BANDGAP_CTRL_2_MASK_COLD_IVA_MASK BIT(2)
+#define DRA752_BANDGAP_CTRL_2_MASK_HOT_DSPEVE_MASK BIT(1)
+#define DRA752_BANDGAP_CTRL_2_MASK_COLD_DSPEVE_MASK BIT(0)
+
+/* DRA752.BANDGAP_STATUS_2 */
+#define DRA752_BANDGAP_STATUS_2_HOT_IVA_MASK BIT(3)
+#define DRA752_BANDGAP_STATUS_2_COLD_IVA_MASK BIT(2)
+#define DRA752_BANDGAP_STATUS_2_HOT_DSPEVE_MASK BIT(1)
+#define DRA752_BANDGAP_STATUS_2_COLD_DSPEVE_MASK BIT(0)
+
+/* DRA752.BANDGAP_CTRL_1 */
+#define DRA752_BANDGAP_CTRL_1_SIDLEMODE_MASK (0x3 << 30)
+#define DRA752_BANDGAP_CTRL_1_COUNTER_DELAY_MASK (0x7 << 27)
+#define DRA752_BANDGAP_CTRL_1_FREEZE_CORE_MASK BIT(23)
+#define DRA752_BANDGAP_CTRL_1_FREEZE_GPU_MASK BIT(22)
+#define DRA752_BANDGAP_CTRL_1_FREEZE_MPU_MASK BIT(21)
+#define DRA752_BANDGAP_CTRL_1_CLEAR_CORE_MASK BIT(20)
+#define DRA752_BANDGAP_CTRL_1_CLEAR_GPU_MASK BIT(19)
+#define DRA752_BANDGAP_CTRL_1_CLEAR_MPU_MASK BIT(18)
+#define DRA752_BANDGAP_CTRL_1_CLEAR_ACCUM_CORE_MASK BIT(17)
+#define DRA752_BANDGAP_CTRL_1_CLEAR_ACCUM_GPU_MASK BIT(16)
+#define DRA752_BANDGAP_CTRL_1_CLEAR_ACCUM_MPU_MASK BIT(15)
+#define DRA752_BANDGAP_CTRL_1_MASK_HOT_CORE_MASK BIT(5)
+#define DRA752_BANDGAP_CTRL_1_MASK_COLD_CORE_MASK BIT(4)
+#define DRA752_BANDGAP_CTRL_1_MASK_HOT_GPU_MASK BIT(3)
+#define DRA752_BANDGAP_CTRL_1_MASK_COLD_GPU_MASK BIT(2)
+#define DRA752_BANDGAP_CTRL_1_MASK_HOT_MPU_MASK BIT(1)
+#define DRA752_BANDGAP_CTRL_1_MASK_COLD_MPU_MASK BIT(0)
+
+/* DRA752.TEMP_SENSOR */
+#define DRA752_TEMP_SENSOR_TMPSOFF_MASK BIT(11)
+#define DRA752_TEMP_SENSOR_EOCZ_MASK BIT(10)
+#define DRA752_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0)
+
+/* DRA752.BANDGAP_THRESHOLD */
+#define DRA752_BANDGAP_THRESHOLD_HOT_MASK (0x3ff << 16)
+#define DRA752_BANDGAP_THRESHOLD_COLD_MASK (0x3ff << 0)
+
+/* DRA752.TSHUT_THRESHOLD */
+#define DRA752_TSHUT_THRESHOLD_MUXCTRL_MASK BIT(31)
+#define DRA752_TSHUT_THRESHOLD_HOT_MASK (0x3ff << 16)
+#define DRA752_TSHUT_THRESHOLD_COLD_MASK (0x3ff << 0)
+
+/* DRA752.BANDGAP_CUMUL_DTEMP_CORE */
+#define DRA752_BANDGAP_CUMUL_DTEMP_CORE_MASK (0xffffffff << 0)
+
+/* DRA752.BANDGAP_CUMUL_DTEMP_IVA */
+#define DRA752_BANDGAP_CUMUL_DTEMP_IVA_MASK (0xffffffff << 0)
+
+/* DRA752.BANDGAP_CUMUL_DTEMP_MPU */
+#define DRA752_BANDGAP_CUMUL_DTEMP_MPU_MASK (0xffffffff << 0)
+
+/* DRA752.BANDGAP_CUMUL_DTEMP_DSPEVE */
+#define DRA752_BANDGAP_CUMUL_DTEMP_DSPEVE_MASK (0xffffffff << 0)
+
+/* DRA752.BANDGAP_CUMUL_DTEMP_GPU */
+#define DRA752_BANDGAP_CUMUL_DTEMP_GPU_MASK (0xffffffff << 0)
+
+/**
+ * Temperature limits and thresholds for DRA752
+ *
+ * All the macros bellow are definitions for handling the
+ * ADC conversions and representation of temperature limits
+ * and thresholds for DRA752. Definitions are grouped
+ * by temperature domain.
+ */
+
+/* DRA752.common temperature definitions */
+/* ADC conversion table limits */
+#define DRA752_ADC_START_VALUE 540
+#define DRA752_ADC_END_VALUE 945
+
+/* DRA752.GPU temperature definitions */
+/* bandgap clock limits */
+#define DRA752_GPU_MAX_FREQ 1500000
+#define DRA752_GPU_MIN_FREQ 1000000
+/* sensor limits */
+#define DRA752_GPU_MIN_TEMP -40000
+#define DRA752_GPU_MAX_TEMP 125000
+#define DRA752_GPU_HYST_VAL 5000
+/* interrupts thresholds */
+#define DRA752_GPU_TSHUT_HOT 915
+#define DRA752_GPU_TSHUT_COLD 900
+#define DRA752_GPU_T_HOT 800
+#define DRA752_GPU_T_COLD 795
+
+/* DRA752.MPU temperature definitions */
+/* bandgap clock limits */
+#define DRA752_MPU_MAX_FREQ 1500000
+#define DRA752_MPU_MIN_FREQ 1000000
+/* sensor limits */
+#define DRA752_MPU_MIN_TEMP -40000
+#define DRA752_MPU_MAX_TEMP 125000
+#define DRA752_MPU_HYST_VAL 5000
+/* interrupts thresholds */
+#define DRA752_MPU_TSHUT_HOT 915
+#define DRA752_MPU_TSHUT_COLD 900
+#define DRA752_MPU_T_HOT 800
+#define DRA752_MPU_T_COLD 795
+
+/* DRA752.CORE temperature definitions */
+/* bandgap clock limits */
+#define DRA752_CORE_MAX_FREQ 1500000
+#define DRA752_CORE_MIN_FREQ 1000000
+/* sensor limits */
+#define DRA752_CORE_MIN_TEMP -40000
+#define DRA752_CORE_MAX_TEMP 125000
+#define DRA752_CORE_HYST_VAL 5000
+/* interrupts thresholds */
+#define DRA752_CORE_TSHUT_HOT 915
+#define DRA752_CORE_TSHUT_COLD 900
+#define DRA752_CORE_T_HOT 800
+#define DRA752_CORE_T_COLD 795
+
+/* DRA752.DSPEVE temperature definitions */
+/* bandgap clock limits */
+#define DRA752_DSPEVE_MAX_FREQ 1500000
+#define DRA752_DSPEVE_MIN_FREQ 1000000
+/* sensor limits */
+#define DRA752_DSPEVE_MIN_TEMP -40000
+#define DRA752_DSPEVE_MAX_TEMP 125000
+#define DRA752_DSPEVE_HYST_VAL 5000
+/* interrupts thresholds */
+#define DRA752_DSPEVE_TSHUT_HOT 915
+#define DRA752_DSPEVE_TSHUT_COLD 900
+#define DRA752_DSPEVE_T_HOT 800
+#define DRA752_DSPEVE_T_COLD 795
+
+/* DRA752.IVA temperature definitions */
+/* bandgap clock limits */
+#define DRA752_IVA_MAX_FREQ 1500000
+#define DRA752_IVA_MIN_FREQ 1000000
+/* sensor limits */
+#define DRA752_IVA_MIN_TEMP -40000
+#define DRA752_IVA_MAX_TEMP 125000
+#define DRA752_IVA_HYST_VAL 5000
+/* interrupts thresholds */
+#define DRA752_IVA_TSHUT_HOT 915
+#define DRA752_IVA_TSHUT_COLD 900
+#define DRA752_IVA_T_HOT 800
+#define DRA752_IVA_T_COLD 795
+
+#endif /* __DRA752_BANDGAP_H */
diff --git a/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c
new file mode 100644
index 0000000..e5d8326
--- /dev/null
+++ b/drivers/thermal/ti-soc-thermal/dra752-thermal-data.c
@@ -0,0 +1,476 @@
+/*
+ * DRA752 thermal data.
+ *
+ * Copyright (C) 2013 Texas Instruments Inc.
+ * Contact:
+ * Eduardo Valentin <eduardo.valentin@ti.com>
+ * Tero Kristo <t-kristo@ti.com>
+ *
+ * This file is partially autogenerated.
+ *
+ * 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 "ti-thermal.h"
+#include "ti-bandgap.h"
+#include "dra752-bandgap.h"
+
+/*
+ * DRA752 has five instances of thermal sensor: MPU, GPU, CORE,
+ * IVA and DSPEVE need to describe the individual registers and
+ * bit fields.
+ */
+
+/*
+ * DRA752 CORE thermal sensor register offsets and bit-fields
+ */
+static struct temp_sensor_registers
+dra752_core_temp_sensor_registers = {
+ .temp_sensor_ctrl = DRA752_TEMP_SENSOR_CORE_OFFSET,
+ .bgap_tempsoff_mask = DRA752_TEMP_SENSOR_TMPSOFF_MASK,
+ .bgap_eocz_mask = DRA752_TEMP_SENSOR_EOCZ_MASK,
+ .bgap_dtemp_mask = DRA752_TEMP_SENSOR_DTEMP_MASK,
+ .bgap_mask_ctrl = DRA752_BANDGAP_CTRL_1_OFFSET,
+ .mask_hot_mask = DRA752_BANDGAP_CTRL_1_MASK_HOT_CORE_MASK,
+ .mask_cold_mask = DRA752_BANDGAP_CTRL_1_MASK_COLD_CORE_MASK,
+ .mask_sidlemode_mask = DRA752_BANDGAP_CTRL_1_SIDLEMODE_MASK,
+ .mask_freeze_mask = DRA752_BANDGAP_CTRL_1_FREEZE_CORE_MASK,
+ .mask_clear_mask = DRA752_BANDGAP_CTRL_1_CLEAR_CORE_MASK,
+ .mask_clear_accum_mask = DRA752_BANDGAP_CTRL_1_CLEAR_ACCUM_CORE_MASK,
+ .bgap_threshold = DRA752_BANDGAP_THRESHOLD_CORE_OFFSET,
+ .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK,
+ .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK,
+ .tshut_threshold = DRA752_BANDGAP_TSHUT_CORE_OFFSET,
+ .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK,
+ .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK,
+ .bgap_status = DRA752_BANDGAP_STATUS_1_OFFSET,
+ .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK,
+ .status_hot_mask = DRA752_BANDGAP_STATUS_1_HOT_CORE_MASK,
+ .status_cold_mask = DRA752_BANDGAP_STATUS_1_COLD_CORE_MASK,
+ .bgap_cumul_dtemp = DRA752_BANDGAP_CUMUL_DTEMP_CORE_OFFSET,
+ .ctrl_dtemp_0 = DRA752_DTEMP_CORE_0_OFFSET,
+ .ctrl_dtemp_1 = DRA752_DTEMP_CORE_1_OFFSET,
+ .ctrl_dtemp_2 = DRA752_DTEMP_CORE_2_OFFSET,
+ .ctrl_dtemp_3 = DRA752_DTEMP_CORE_3_OFFSET,
+ .ctrl_dtemp_4 = DRA752_DTEMP_CORE_4_OFFSET,
+ .bgap_efuse = DRA752_STD_FUSE_OPP_BGAP_CORE_OFFSET,
+};
+
+/*
+ * DRA752 IVA thermal sensor register offsets and bit-fields
+ */
+static struct temp_sensor_registers
+dra752_iva_temp_sensor_registers = {
+ .temp_sensor_ctrl = DRA752_TEMP_SENSOR_IVA_OFFSET,
+ .bgap_tempsoff_mask = DRA752_TEMP_SENSOR_TMPSOFF_MASK,
+ .bgap_eocz_mask = DRA752_TEMP_SENSOR_EOCZ_MASK,
+ .bgap_dtemp_mask = DRA752_TEMP_SENSOR_DTEMP_MASK,
+ .bgap_mask_ctrl = DRA752_BANDGAP_CTRL_2_OFFSET,
+ .mask_hot_mask = DRA752_BANDGAP_CTRL_2_MASK_HOT_IVA_MASK,
+ .mask_cold_mask = DRA752_BANDGAP_CTRL_2_MASK_COLD_IVA_MASK,
+ .mask_sidlemode_mask = DRA752_BANDGAP_CTRL_1_SIDLEMODE_MASK,
+ .mask_freeze_mask = DRA752_BANDGAP_CTRL_2_FREEZE_IVA_MASK,
+ .mask_clear_mask = DRA752_BANDGAP_CTRL_2_CLEAR_IVA_MASK,
+ .mask_clear_accum_mask = DRA752_BANDGAP_CTRL_2_CLEAR_ACCUM_IVA_MASK,
+ .bgap_threshold = DRA752_BANDGAP_THRESHOLD_IVA_OFFSET,
+ .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK,
+ .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK,
+ .tshut_threshold = DRA752_BANDGAP_TSHUT_IVA_OFFSET,
+ .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK,
+ .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK,
+ .bgap_status = DRA752_BANDGAP_STATUS_2_OFFSET,
+ .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK,
+ .status_hot_mask = DRA752_BANDGAP_STATUS_2_HOT_IVA_MASK,
+ .status_cold_mask = DRA752_BANDGAP_STATUS_2_COLD_IVA_MASK,
+ .bgap_cumul_dtemp = DRA752_BANDGAP_CUMUL_DTEMP_IVA_OFFSET,
+ .ctrl_dtemp_0 = DRA752_DTEMP_IVA_0_OFFSET,
+ .ctrl_dtemp_1 = DRA752_DTEMP_IVA_1_OFFSET,
+ .ctrl_dtemp_2 = DRA752_DTEMP_IVA_2_OFFSET,
+ .ctrl_dtemp_3 = DRA752_DTEMP_IVA_3_OFFSET,
+ .ctrl_dtemp_4 = DRA752_DTEMP_IVA_4_OFFSET,
+ .bgap_efuse = DRA752_STD_FUSE_OPP_BGAP_IVA_OFFSET,
+};
+
+/*
+ * DRA752 MPU thermal sensor register offsets and bit-fields
+ */
+static struct temp_sensor_registers
+dra752_mpu_temp_sensor_registers = {
+ .temp_sensor_ctrl = DRA752_TEMP_SENSOR_MPU_OFFSET,
+ .bgap_tempsoff_mask = DRA752_TEMP_SENSOR_TMPSOFF_MASK,
+ .bgap_eocz_mask = DRA752_TEMP_SENSOR_EOCZ_MASK,
+ .bgap_dtemp_mask = DRA752_TEMP_SENSOR_DTEMP_MASK,
+ .bgap_mask_ctrl = DRA752_BANDGAP_CTRL_1_OFFSET,
+ .mask_hot_mask = DRA752_BANDGAP_CTRL_1_MASK_HOT_MPU_MASK,
+ .mask_cold_mask = DRA752_BANDGAP_CTRL_1_MASK_COLD_MPU_MASK,
+ .mask_sidlemode_mask = DRA752_BANDGAP_CTRL_1_SIDLEMODE_MASK,
+ .mask_freeze_mask = DRA752_BANDGAP_CTRL_1_FREEZE_MPU_MASK,
+ .mask_clear_mask = DRA752_BANDGAP_CTRL_1_CLEAR_MPU_MASK,
+ .mask_clear_accum_mask = DRA752_BANDGAP_CTRL_1_CLEAR_ACCUM_MPU_MASK,
+ .bgap_threshold = DRA752_BANDGAP_THRESHOLD_MPU_OFFSET,
+ .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK,
+ .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK,
+ .tshut_threshold = DRA752_BANDGAP_TSHUT_MPU_OFFSET,
+ .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK,
+ .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK,
+ .bgap_status = DRA752_BANDGAP_STATUS_1_OFFSET,
+ .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK,
+ .status_hot_mask = DRA752_BANDGAP_STATUS_1_HOT_MPU_MASK,
+ .status_cold_mask = DRA752_BANDGAP_STATUS_1_COLD_MPU_MASK,
+ .bgap_cumul_dtemp = DRA752_BANDGAP_CUMUL_DTEMP_MPU_OFFSET,
+ .ctrl_dtemp_0 = DRA752_DTEMP_MPU_0_OFFSET,
+ .ctrl_dtemp_1 = DRA752_DTEMP_MPU_1_OFFSET,
+ .ctrl_dtemp_2 = DRA752_DTEMP_MPU_2_OFFSET,
+ .ctrl_dtemp_3 = DRA752_DTEMP_MPU_3_OFFSET,
+ .ctrl_dtemp_4 = DRA752_DTEMP_MPU_4_OFFSET,
+ .bgap_efuse = DRA752_STD_FUSE_OPP_BGAP_MPU_OFFSET,
+};
+
+/*
+ * DRA752 DSPEVE thermal sensor register offsets and bit-fields
+ */
+static struct temp_sensor_registers
+dra752_dspeve_temp_sensor_registers = {
+ .temp_sensor_ctrl = DRA752_TEMP_SENSOR_DSPEVE_OFFSET,
+ .bgap_tempsoff_mask = DRA752_TEMP_SENSOR_TMPSOFF_MASK,
+ .bgap_eocz_mask = DRA752_TEMP_SENSOR_EOCZ_MASK,
+ .bgap_dtemp_mask = DRA752_TEMP_SENSOR_DTEMP_MASK,
+ .bgap_mask_ctrl = DRA752_BANDGAP_CTRL_2_OFFSET,
+ .mask_hot_mask = DRA752_BANDGAP_CTRL_2_MASK_HOT_DSPEVE_MASK,
+ .mask_cold_mask = DRA752_BANDGAP_CTRL_2_MASK_COLD_DSPEVE_MASK,
+ .mask_sidlemode_mask = DRA752_BANDGAP_CTRL_1_SIDLEMODE_MASK,
+ .mask_freeze_mask = DRA752_BANDGAP_CTRL_2_FREEZE_DSPEVE_MASK,
+ .mask_clear_mask = DRA752_BANDGAP_CTRL_2_CLEAR_DSPEVE_MASK,
+ .mask_clear_accum_mask = DRA752_BANDGAP_CTRL_2_CLEAR_ACCUM_DSPEVE_MASK,
+ .bgap_threshold = DRA752_BANDGAP_THRESHOLD_DSPEVE_OFFSET,
+ .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK,
+ .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK,
+ .tshut_threshold = DRA752_BANDGAP_TSHUT_DSPEVE_OFFSET,
+ .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK,
+ .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK,
+ .bgap_status = DRA752_BANDGAP_STATUS_2_OFFSET,
+ .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK,
+ .status_hot_mask = DRA752_BANDGAP_STATUS_2_HOT_DSPEVE_MASK,
+ .status_cold_mask = DRA752_BANDGAP_STATUS_2_COLD_DSPEVE_MASK,
+ .bgap_cumul_dtemp = DRA752_BANDGAP_CUMUL_DTEMP_DSPEVE_OFFSET,
+ .ctrl_dtemp_0 = DRA752_DTEMP_DSPEVE_0_OFFSET,
+ .ctrl_dtemp_1 = DRA752_DTEMP_DSPEVE_1_OFFSET,
+ .ctrl_dtemp_2 = DRA752_DTEMP_DSPEVE_2_OFFSET,
+ .ctrl_dtemp_3 = DRA752_DTEMP_DSPEVE_3_OFFSET,
+ .ctrl_dtemp_4 = DRA752_DTEMP_DSPEVE_4_OFFSET,
+ .bgap_efuse = DRA752_STD_FUSE_OPP_BGAP_DSPEVE_OFFSET,
+};
+
+/*
+ * DRA752 GPU thermal sensor register offsets and bit-fields
+ */
+static struct temp_sensor_registers
+dra752_gpu_temp_sensor_registers = {
+ .temp_sensor_ctrl = DRA752_TEMP_SENSOR_GPU_OFFSET,
+ .bgap_tempsoff_mask = DRA752_TEMP_SENSOR_TMPSOFF_MASK,
+ .bgap_eocz_mask = DRA752_TEMP_SENSOR_EOCZ_MASK,
+ .bgap_dtemp_mask = DRA752_TEMP_SENSOR_DTEMP_MASK,
+ .bgap_mask_ctrl = DRA752_BANDGAP_CTRL_1_OFFSET,
+ .mask_hot_mask = DRA752_BANDGAP_CTRL_1_MASK_HOT_GPU_MASK,
+ .mask_cold_mask = DRA752_BANDGAP_CTRL_1_MASK_COLD_GPU_MASK,
+ .mask_sidlemode_mask = DRA752_BANDGAP_CTRL_1_SIDLEMODE_MASK,
+ .mask_freeze_mask = DRA752_BANDGAP_CTRL_1_FREEZE_GPU_MASK,
+ .mask_clear_mask = DRA752_BANDGAP_CTRL_1_CLEAR_GPU_MASK,
+ .mask_clear_accum_mask = DRA752_BANDGAP_CTRL_1_CLEAR_ACCUM_GPU_MASK,
+ .bgap_threshold = DRA752_BANDGAP_THRESHOLD_GPU_OFFSET,
+ .threshold_thot_mask = DRA752_BANDGAP_THRESHOLD_HOT_MASK,
+ .threshold_tcold_mask = DRA752_BANDGAP_THRESHOLD_COLD_MASK,
+ .tshut_threshold = DRA752_BANDGAP_TSHUT_GPU_OFFSET,
+ .tshut_hot_mask = DRA752_TSHUT_THRESHOLD_HOT_MASK,
+ .tshut_cold_mask = DRA752_TSHUT_THRESHOLD_COLD_MASK,
+ .bgap_status = DRA752_BANDGAP_STATUS_1_OFFSET,
+ .status_bgap_alert_mask = DRA752_BANDGAP_STATUS_1_ALERT_MASK,
+ .status_hot_mask = DRA752_BANDGAP_STATUS_1_HOT_GPU_MASK,
+ .status_cold_mask = DRA752_BANDGAP_STATUS_1_COLD_GPU_MASK,
+ .bgap_cumul_dtemp = DRA752_BANDGAP_CUMUL_DTEMP_GPU_OFFSET,
+ .ctrl_dtemp_0 = DRA752_DTEMP_GPU_0_OFFSET,
+ .ctrl_dtemp_1 = DRA752_DTEMP_GPU_1_OFFSET,
+ .ctrl_dtemp_2 = DRA752_DTEMP_GPU_2_OFFSET,
+ .ctrl_dtemp_3 = DRA752_DTEMP_GPU_3_OFFSET,
+ .ctrl_dtemp_4 = DRA752_DTEMP_GPU_4_OFFSET,
+ .bgap_efuse = DRA752_STD_FUSE_OPP_BGAP_GPU_OFFSET,
+};
+
+/* Thresholds and limits for DRA752 MPU temperature sensor */
+static struct temp_sensor_data dra752_mpu_temp_sensor_data = {
+ .tshut_hot = DRA752_MPU_TSHUT_HOT,
+ .tshut_cold = DRA752_MPU_TSHUT_COLD,
+ .t_hot = DRA752_MPU_T_HOT,
+ .t_cold = DRA752_MPU_T_COLD,
+ .min_freq = DRA752_MPU_MIN_FREQ,
+ .max_freq = DRA752_MPU_MAX_FREQ,
+ .max_temp = DRA752_MPU_MAX_TEMP,
+ .min_temp = DRA752_MPU_MIN_TEMP,
+ .hyst_val = DRA752_MPU_HYST_VAL,
+ .update_int1 = 1000,
+ .update_int2 = 2000,
+};
+
+/* Thresholds and limits for DRA752 GPU temperature sensor */
+static struct temp_sensor_data dra752_gpu_temp_sensor_data = {
+ .tshut_hot = DRA752_GPU_TSHUT_HOT,
+ .tshut_cold = DRA752_GPU_TSHUT_COLD,
+ .t_hot = DRA752_GPU_T_HOT,
+ .t_cold = DRA752_GPU_T_COLD,
+ .min_freq = DRA752_GPU_MIN_FREQ,
+ .max_freq = DRA752_GPU_MAX_FREQ,
+ .max_temp = DRA752_GPU_MAX_TEMP,
+ .min_temp = DRA752_GPU_MIN_TEMP,
+ .hyst_val = DRA752_GPU_HYST_VAL,
+ .update_int1 = 1000,
+ .update_int2 = 2000,
+};
+
+/* Thresholds and limits for DRA752 CORE temperature sensor */
+static struct temp_sensor_data dra752_core_temp_sensor_data = {
+ .tshut_hot = DRA752_CORE_TSHUT_HOT,
+ .tshut_cold = DRA752_CORE_TSHUT_COLD,
+ .t_hot = DRA752_CORE_T_HOT,
+ .t_cold = DRA752_CORE_T_COLD,
+ .min_freq = DRA752_CORE_MIN_FREQ,
+ .max_freq = DRA752_CORE_MAX_FREQ,
+ .max_temp = DRA752_CORE_MAX_TEMP,
+ .min_temp = DRA752_CORE_MIN_TEMP,
+ .hyst_val = DRA752_CORE_HYST_VAL,
+ .update_int1 = 1000,
+ .update_int2 = 2000,
+};
+
+/* Thresholds and limits for DRA752 DSPEVE temperature sensor */
+static struct temp_sensor_data dra752_dspeve_temp_sensor_data = {
+ .tshut_hot = DRA752_DSPEVE_TSHUT_HOT,
+ .tshut_cold = DRA752_DSPEVE_TSHUT_COLD,
+ .t_hot = DRA752_DSPEVE_T_HOT,
+ .t_cold = DRA752_DSPEVE_T_COLD,
+ .min_freq = DRA752_DSPEVE_MIN_FREQ,
+ .max_freq = DRA752_DSPEVE_MAX_FREQ,
+ .max_temp = DRA752_DSPEVE_MAX_TEMP,
+ .min_temp = DRA752_DSPEVE_MIN_TEMP,
+ .hyst_val = DRA752_DSPEVE_HYST_VAL,
+ .update_int1 = 1000,
+ .update_int2 = 2000,
+};
+
+/* Thresholds and limits for DRA752 IVA temperature sensor */
+static struct temp_sensor_data dra752_iva_temp_sensor_data = {
+ .tshut_hot = DRA752_IVA_TSHUT_HOT,
+ .tshut_cold = DRA752_IVA_TSHUT_COLD,
+ .t_hot = DRA752_IVA_T_HOT,
+ .t_cold = DRA752_IVA_T_COLD,
+ .min_freq = DRA752_IVA_MIN_FREQ,
+ .max_freq = DRA752_IVA_MAX_FREQ,
+ .max_temp = DRA752_IVA_MAX_TEMP,
+ .min_temp = DRA752_IVA_MIN_TEMP,
+ .hyst_val = DRA752_IVA_HYST_VAL,
+ .update_int1 = 1000,
+ .update_int2 = 2000,
+};
+
+/*
+ * DRA752 : Temperature values in milli degree celsius
+ * ADC code values from 540 to 945
+ */
+static
+int dra752_adc_to_temp[DRA752_ADC_END_VALUE - DRA752_ADC_START_VALUE + 1] = {
+ /* Index 540 - 549 */
+ -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200,
+ -37800,
+ /* Index 550 - 559 */
+ -37400, -37000, -36600, -36200, -35800, -35300, -34700, -34200, -33800,
+ -33400,
+ /* Index 560 - 569 */
+ -33000, -32600, -32200, -31800, -31400, -31000, -30600, -30200, -29800,
+ -29400,
+ /* Index 570 - 579 */
+ -29000, -28600, -28200, -27700, -27100, -26600, -26200, -25800, -25400,
+ -25000,
+ /* Index 580 - 589 */
+ -24600, -24200, -23800, -23400, -23000, -22600, -22200, -21800, -21400,
+ -21000,
+ /* Index 590 - 599 */
+ -20500, -19900, -19400, -19000, -18600, -18200, -17800, -17400, -17000,
+ -16600,
+ /* Index 600 - 609 */
+ -16200, -15800, -15400, -15000, -14600, -14200, -13800, -13400, -13000,
+ -12500,
+ /* Index 610 - 619 */
+ -11900, -11400, -11000, -10600, -10200, -9800, -9400, -9000, -8600,
+ -8200,
+ /* Index 620 - 629 */
+ -7800, -7400, -7000, -6600, -6200, -5800, -5400, -5000, -4500,
+ -3900,
+ /* Index 630 - 639 */
+ -3400, -3000, -2600, -2200, -1800, -1400, -1000, -600, -200,
+ 200,
+ /* Index 640 - 649 */
+ 600, 1000, 1400, 1800, 2200, 2600, 3000, 3400, 3900,
+ 4500,
+ /* Index 650 - 659 */
+ 5000, 5400, 5800, 6200, 6600, 7000, 7400, 7800, 8200,
+ 8600,
+ /* Index 660 - 669 */
+ 9000, 9400, 9800, 10200, 10600, 11000, 11400, 11800, 12200,
+ 12700,
+ /* Index 670 - 679 */
+ 13300, 13800, 14200, 14600, 15000, 15400, 15800, 16200, 16600,
+ 17000,
+ /* Index 680 - 689 */
+ 17400, 17800, 18200, 18600, 19000, 19400, 19800, 20200, 20600,
+ 21000,
+ /* Index 690 - 699 */
+ 21400, 21900, 22500, 23000, 23400, 23800, 24200, 24600, 25000,
+ 25400,
+ /* Index 700 - 709 */
+ 25800, 26200, 26600, 27000, 27400, 27800, 28200, 28600, 29000,
+ 29400,
+ /* Index 710 - 719 */
+ 29800, 30200, 30600, 31000, 31400, 31900, 32500, 33000, 33400,
+ 33800,
+ /* Index 720 - 729 */
+ 34200, 34600, 35000, 35400, 35800, 36200, 36600, 37000, 37400,
+ 37800,
+ /* Index 730 - 739 */
+ 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41000, 41400,
+ 41800,
+ /* Index 740 - 749 */
+ 42200, 42600, 43100, 43700, 44200, 44600, 45000, 45400, 45800,
+ 46200,
+ /* Index 750 - 759 */
+ 46600, 47000, 47400, 47800, 48200, 48600, 49000, 49400, 49800,
+ 50200,
+ /* Index 760 - 769 */
+ 50600, 51000, 51400, 51800, 52200, 52600, 53000, 53400, 53800,
+ 54200,
+ /* Index 770 - 779 */
+ 54600, 55000, 55400, 55900, 56500, 57000, 57400, 57800, 58200,
+ 58600,
+ /* Index 780 - 789 */
+ 59000, 59400, 59800, 60200, 60600, 61000, 61400, 61800, 62200,
+ 62600,
+ /* Index 790 - 799 */
+ 63000, 63400, 63800, 64200, 64600, 65000, 65400, 65800, 66200,
+ 66600,
+ /* Index 800 - 809 */
+ 67000, 67400, 67800, 68200, 68600, 69000, 69400, 69800, 70200,
+ 70600,
+ /* Index 810 - 819 */
+ 71000, 71500, 72100, 72600, 73000, 73400, 73800, 74200, 74600,
+ 75000,
+ /* Index 820 - 829 */
+ 75400, 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600,
+ 79000,
+ /* Index 830 - 839 */
+ 79400, 79800, 80200, 80600, 81000, 81400, 81800, 82200, 82600,
+ 83000,
+ /* Index 840 - 849 */
+ 83400, 83800, 84200, 84600, 85000, 85400, 85800, 86200, 86600,
+ 87000,
+ /* Index 850 - 859 */
+ 87400, 87800, 88200, 88600, 89000, 89400, 89800, 90200, 90600,
+ 91000,
+ /* Index 860 - 869 */
+ 91400, 91800, 92200, 92600, 93000, 93400, 93800, 94200, 94600,
+ 95000,
+ /* Index 870 - 879 */
+ 95400, 95800, 96200, 96600, 97000, 97500, 98100, 98600, 99000,
+ 99400,
+ /* Index 880 - 889 */
+ 99800, 100200, 100600, 101000, 101400, 101800, 102200, 102600, 103000,
+ 103400,
+ /* Index 890 - 899 */
+ 103800, 104200, 104600, 105000, 105400, 105800, 106200, 106600, 107000,
+ 107400,
+ /* Index 900 - 909 */
+ 107800, 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000,
+ 111400,
+ /* Index 910 - 919 */
+ 111800, 112200, 112600, 113000, 113400, 113800, 114200, 114600, 115000,
+ 115400,
+ /* Index 920 - 929 */
+ 115800, 116200, 116600, 117000, 117400, 117800, 118200, 118600, 119000,
+ 119400,
+ /* Index 930 - 939 */
+ 119800, 120200, 120600, 121000, 121400, 121800, 122200, 122600, 123000,
+ 123400,
+ /* Index 940 - 945 */
+ 123800, 124200, 124600, 124900, 125000, 125000,
+};
+
+/* DRA752 data */
+const struct ti_bandgap_data dra752_data = {
+ .features = TI_BANDGAP_FEATURE_TSHUT_CONFIG |
+ TI_BANDGAP_FEATURE_FREEZE_BIT |
+ TI_BANDGAP_FEATURE_TALERT |
+ TI_BANDGAP_FEATURE_COUNTER_DELAY |
+ TI_BANDGAP_FEATURE_HISTORY_BUFFER,
+ .fclock_name = "l3instr_ts_gclk_div",
+ .div_ck_name = "l3instr_ts_gclk_div",
+ .conv_table = dra752_adc_to_temp,
+ .adc_start_val = DRA752_ADC_START_VALUE,
+ .adc_end_val = DRA752_ADC_END_VALUE,
+ .expose_sensor = ti_thermal_expose_sensor,
+ .remove_sensor = ti_thermal_remove_sensor,
+ .sensors = {
+ {
+ .registers = &dra752_mpu_temp_sensor_registers,
+ .ts_data = &dra752_mpu_temp_sensor_data,
+ .domain = "cpu",
+ .register_cooling = ti_thermal_register_cpu_cooling,
+ .unregister_cooling = ti_thermal_unregister_cpu_cooling,
+ .slope = DRA752_GRADIENT_SLOPE,
+ .constant = DRA752_GRADIENT_CONST,
+ .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB,
+ .constant_pcb = DRA752_GRADIENT_CONST_W_PCB,
+ },
+ {
+ .registers = &dra752_gpu_temp_sensor_registers,
+ .ts_data = &dra752_gpu_temp_sensor_data,
+ .domain = "gpu",
+ .slope = DRA752_GRADIENT_SLOPE,
+ .constant = DRA752_GRADIENT_CONST,
+ .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB,
+ .constant_pcb = DRA752_GRADIENT_CONST_W_PCB,
+ },
+ {
+ .registers = &dra752_core_temp_sensor_registers,
+ .ts_data = &dra752_core_temp_sensor_data,
+ .domain = "core",
+ .slope = DRA752_GRADIENT_SLOPE,
+ .constant = DRA752_GRADIENT_CONST,
+ .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB,
+ .constant_pcb = DRA752_GRADIENT_CONST_W_PCB,
+ },
+ {
+ .registers = &dra752_dspeve_temp_sensor_registers,
+ .ts_data = &dra752_dspeve_temp_sensor_data,
+ .domain = "dspeve",
+ .slope = DRA752_GRADIENT_SLOPE,
+ .constant = DRA752_GRADIENT_CONST,
+ .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB,
+ .constant_pcb = DRA752_GRADIENT_CONST_W_PCB,
+ },
+ {
+ .registers = &dra752_iva_temp_sensor_registers,
+ .ts_data = &dra752_iva_temp_sensor_data,
+ .domain = "iva",
+ .slope = DRA752_GRADIENT_SLOPE,
+ .constant = DRA752_GRADIENT_CONST,
+ .slope_pcb = DRA752_GRADIENT_SLOPE_W_PCB,
+ .constant_pcb = DRA752_GRADIENT_CONST_W_PCB,
+ },
+ },
+ .sensor_count = 5,
+};
diff --git a/drivers/staging/ti-soc-thermal/omap4-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c
similarity index 100%
rename from drivers/staging/ti-soc-thermal/omap4-thermal-data.c
rename to drivers/thermal/ti-soc-thermal/omap4-thermal-data.c
diff --git a/drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h b/drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h
similarity index 100%
rename from drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h
rename to drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h
diff --git a/drivers/staging/ti-soc-thermal/omap5-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c
similarity index 100%
rename from drivers/staging/ti-soc-thermal/omap5-thermal-data.c
rename to drivers/thermal/ti-soc-thermal/omap5-thermal-data.c
diff --git a/drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h b/drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h
similarity index 100%
rename from drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h
rename to drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h
diff --git a/drivers/staging/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
similarity index 98%
rename from drivers/staging/ti-soc-thermal/ti-bandgap.c
rename to drivers/thermal/ti-soc-thermal/ti-bandgap.c
index f20c1cf..9dfd471 100644
--- a/drivers/staging/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -38,6 +38,7 @@
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
#include <linux/io.h>
#include "ti-bandgap.h"
@@ -469,7 +470,7 @@
{
int ret = 0;
- if (IS_ERR_OR_NULL(bgp)) {
+ if (!bgp || IS_ERR(bgp)) {
pr_err("%s: invalid bandgap pointer\n", __func__);
ret = -EINVAL;
goto exit;
@@ -992,9 +993,12 @@
goto exit;
}
+ spin_lock(&bgp->lock);
+
tsr = bgp->conf->sensors[id].registers;
/* Freeze and read the last 2 valid readings */
+ RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 1);
reg1 = tsr->ctrl_dtemp_1;
reg2 = tsr->ctrl_dtemp_2;
@@ -1008,22 +1012,25 @@
/* Convert from adc values to mCelsius temperature */
ret = ti_bandgap_adc_to_mcelsius(bgp, temp1, &t1);
if (ret)
- goto exit;
+ goto unfreeze;
ret = ti_bandgap_adc_to_mcelsius(bgp, temp2, &t2);
if (ret)
- goto exit;
+ goto unfreeze;
/* Fetch the update interval */
ret = ti_bandgap_read_update_interval(bgp, id, &interval);
if (ret || !interval)
- goto exit;
+ goto unfreeze;
*trend = (t1 - t2) / interval;
dev_dbg(bgp->dev, "The temperatures are t1 = %d and t2 = %d and trend =%d\n",
t1, t2, *trend);
+unfreeze:
+ RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 0);
+ spin_unlock(&bgp->lock);
exit:
return ret;
}
@@ -1123,7 +1130,6 @@
const struct of_device_id *of_id;
struct ti_bandgap *bgp;
struct resource *res;
- u32 prop;
int i;
/* just for the sake */
@@ -1167,11 +1173,7 @@
} while (res);
if (TI_BANDGAP_HAS(bgp, TSHUT)) {
- if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) {
- dev_err(&pdev->dev, "missing tshut gpio in device tree\n");
- return ERR_PTR(-EINVAL);
- }
- bgp->tshut_gpio = prop;
+ bgp->tshut_gpio = of_get_gpio(node, 0);
if (!gpio_is_valid(bgp->tshut_gpio)) {
dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n",
bgp->tshut_gpio);
@@ -1191,7 +1193,7 @@
int clk_rate, ret = 0, i;
bgp = ti_bandgap_build(pdev);
- if (IS_ERR_OR_NULL(bgp)) {
+ if (IS_ERR(bgp)) {
dev_err(&pdev->dev, "failed to fetch platform data\n");
return PTR_ERR(bgp);
}
@@ -1207,17 +1209,19 @@
}
bgp->fclock = clk_get(NULL, bgp->conf->fclock_name);
- ret = IS_ERR_OR_NULL(bgp->fclock);
+ ret = IS_ERR(bgp->fclock);
if (ret) {
dev_err(&pdev->dev, "failed to request fclock reference\n");
+ ret = PTR_ERR(bgp->fclock);
goto free_irqs;
}
bgp->div_clk = clk_get(NULL, bgp->conf->div_ck_name);
- ret = IS_ERR_OR_NULL(bgp->div_clk);
+ ret = IS_ERR(bgp->div_clk);
if (ret) {
dev_err(&pdev->dev,
"failed to request div_ts_ck clock ref\n");
+ ret = PTR_ERR(bgp->div_clk);
goto free_irqs;
}
@@ -1523,6 +1527,12 @@
.data = (void *)&omap5430_data,
},
#endif
+#ifdef CONFIG_DRA752_THERMAL
+ {
+ .compatible = "ti,dra752-bandgap",
+ .data = (void *)&dra752_data,
+ },
+#endif
/* Sentinel */
{ },
};
diff --git a/drivers/staging/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h
similarity index 98%
rename from drivers/staging/ti-soc-thermal/ti-bandgap.h
rename to drivers/thermal/ti-soc-thermal/ti-bandgap.h
index 5f4794a..b3adf72 100644
--- a/drivers/staging/ti-soc-thermal/ti-bandgap.h
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h
@@ -400,4 +400,9 @@
#define omap5430_data NULL
#endif
+#ifdef CONFIG_DRA752_THERMAL
+extern const struct ti_bandgap_data dra752_data;
+#else
+#define dra752_data NULL
+#endif
#endif
diff --git a/drivers/staging/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
similarity index 97%
rename from drivers/staging/ti-soc-thermal/ti-thermal-common.c
rename to drivers/thermal/ti-soc-thermal/ti-thermal-common.c
index 8e67ebf..4c5f55c37 100644
--- a/drivers/staging/ti-soc-thermal/ti-thermal-common.c
+++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
@@ -101,7 +101,7 @@
pcb_tz = data->pcb_tz;
/* In case pcb zone is available, use the extrapolation rule with it */
- if (!IS_ERR_OR_NULL(pcb_tz)) {
+ if (!IS_ERR(pcb_tz)) {
ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
if (!ret) {
tmp -= pcb_temp; /* got a valid PCB temp */
@@ -124,7 +124,7 @@
struct ti_thermal_data *data = thermal->devdata;
int id;
- if (IS_ERR_OR_NULL(data))
+ if (!data || IS_ERR(data))
return -ENODEV;
/* check if this is the cooling device we registered */
@@ -146,7 +146,7 @@
{
struct ti_thermal_data *data = thermal->devdata;
- if (IS_ERR_OR_NULL(data))
+ if (!data || IS_ERR(data))
return -ENODEV;
/* check if this is the cooling device we registered */
@@ -282,6 +282,7 @@
data->sensor_id = id;
data->bgp = bgp;
data->mode = THERMAL_DEVICE_ENABLED;
+ /* pcb_tz will be either valid or PTR_ERR() */
data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
INIT_WORK(&data->thermal_wq, ti_thermal_work);
@@ -295,7 +296,7 @@
data = ti_bandgap_get_sensor_data(bgp, id);
- if (IS_ERR_OR_NULL(data))
+ if (!data || IS_ERR(data))
data = ti_thermal_build_data(bgp, id);
if (!data)
@@ -306,7 +307,7 @@
OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops,
NULL, FAST_TEMP_MONITORING_RATE,
FAST_TEMP_MONITORING_RATE);
- if (IS_ERR_OR_NULL(data->ti_thermal)) {
+ if (IS_ERR(data->ti_thermal)) {
dev_err(bgp->dev, "thermal zone device is NULL\n");
return PTR_ERR(data->ti_thermal);
}
@@ -343,7 +344,7 @@
struct ti_thermal_data *data;
data = ti_bandgap_get_sensor_data(bgp, id);
- if (IS_ERR_OR_NULL(data))
+ if (!data || IS_ERR(data))
data = ti_thermal_build_data(bgp, id);
if (!data)
@@ -356,7 +357,7 @@
/* Register cooling device */
data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
- if (IS_ERR_OR_NULL(data->cool_dev)) {
+ if (IS_ERR(data->cool_dev)) {
dev_err(bgp->dev,
"Failed to register cpufreq cooling device\n");
return PTR_ERR(data->cool_dev);
diff --git a/drivers/staging/ti-soc-thermal/ti-thermal.h b/drivers/thermal/ti-soc-thermal/ti-thermal.h
similarity index 95%
rename from drivers/staging/ti-soc-thermal/ti-thermal.h
rename to drivers/thermal/ti-soc-thermal/ti-thermal.h
index 5055777..f8b7ffe 100644
--- a/drivers/staging/ti-soc-thermal/ti-thermal.h
+++ b/drivers/thermal/ti-soc-thermal/ti-thermal.h
@@ -38,6 +38,9 @@
#define OMAP_GRADIENT_SLOPE_5430_GPU 117
#define OMAP_GRADIENT_CONST_5430_GPU -2992
+#define DRA752_GRADIENT_SLOPE 0
+#define DRA752_GRADIENT_CONST 2000
+
/* PCB sensor calculation constants */
#define OMAP_GRADIENT_SLOPE_W_PCB_4430 0
#define OMAP_GRADIENT_CONST_W_PCB_4430 20000
@@ -51,6 +54,9 @@
#define OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU 464
#define OMAP_GRADIENT_CONST_W_PCB_5430_GPU -5102
+#define DRA752_GRADIENT_SLOPE_W_PCB 0
+#define DRA752_GRADIENT_CONST_W_PCB 2000
+
/* trip points of interest in milicelsius (at hotspot level) */
#define OMAP_TRIP_COLD 100000
#define OMAP_TRIP_HOT 110000
diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c
new file mode 100644
index 0000000..5de56f6
--- /dev/null
+++ b/drivers/thermal/x86_pkg_temp_thermal.c
@@ -0,0 +1,642 @@
+/*
+ * x86_pkg_temp_thermal driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/thermal.h>
+#include <linux/debugfs.h>
+#include <asm/cpu_device_id.h>
+#include <asm/mce.h>
+
+/*
+* Rate control delay: Idea is to introduce denounce effect
+* This should be long enough to avoid reduce events, when
+* threshold is set to a temperature, which is constantly
+* violated, but at the short enough to take any action.
+* The action can be remove threshold or change it to next
+* interesting setting. Based on experiments, in around
+* every 5 seconds under load will give us a significant
+* temperature change.
+*/
+#define PKG_TEMP_THERMAL_NOTIFY_DELAY 5000
+static int notify_delay_ms = PKG_TEMP_THERMAL_NOTIFY_DELAY;
+module_param(notify_delay_ms, int, 0644);
+MODULE_PARM_DESC(notify_delay_ms,
+ "User space notification delay in milli seconds.");
+
+/* Number of trip points in thermal zone. Currently it can't
+* be more than 2. MSR can allow setting and getting notifications
+* for only 2 thresholds. This define enforces this, if there
+* is some wrong values returned by cpuid for number of thresholds.
+*/
+#define MAX_NUMBER_OF_TRIPS 2
+
+struct phy_dev_entry {
+ struct list_head list;
+ u16 phys_proc_id;
+ u16 first_cpu;
+ u32 tj_max;
+ int ref_cnt;
+ u32 start_pkg_therm_low;
+ u32 start_pkg_therm_high;
+ struct thermal_zone_device *tzone;
+};
+
+/* List maintaining number of package instances */
+static LIST_HEAD(phy_dev_list);
+static DEFINE_MUTEX(phy_dev_list_mutex);
+
+/* Interrupt to work function schedule queue */
+static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work);
+
+/* To track if the work is already scheduled on a package */
+static u8 *pkg_work_scheduled;
+
+/* Spin lock to prevent races with pkg_work_scheduled */
+static spinlock_t pkg_work_lock;
+static u16 max_phy_id;
+
+/* Debug counters to show using debugfs */
+static struct dentry *debugfs;
+static unsigned int pkg_interrupt_cnt;
+static unsigned int pkg_work_cnt;
+
+static int pkg_temp_debugfs_init(void)
+{
+ struct dentry *d;
+
+ debugfs = debugfs_create_dir("pkg_temp_thermal", NULL);
+ if (!debugfs)
+ return -ENOENT;
+
+ d = debugfs_create_u32("pkg_thres_interrupt", S_IRUGO, debugfs,
+ (u32 *)&pkg_interrupt_cnt);
+ if (!d)
+ goto err_out;
+
+ d = debugfs_create_u32("pkg_thres_work", S_IRUGO, debugfs,
+ (u32 *)&pkg_work_cnt);
+ if (!d)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ debugfs_remove_recursive(debugfs);
+ return -ENOENT;
+}
+
+static struct phy_dev_entry
+ *pkg_temp_thermal_get_phy_entry(unsigned int cpu)
+{
+ u16 phys_proc_id = topology_physical_package_id(cpu);
+ struct phy_dev_entry *phy_ptr;
+
+ mutex_lock(&phy_dev_list_mutex);
+
+ list_for_each_entry(phy_ptr, &phy_dev_list, list)
+ if (phy_ptr->phys_proc_id == phys_proc_id) {
+ mutex_unlock(&phy_dev_list_mutex);
+ return phy_ptr;
+ }
+
+ mutex_unlock(&phy_dev_list_mutex);
+
+ return NULL;
+}
+
+/*
+* tj-max is is interesting because threshold is set relative to this
+* temperature.
+*/
+static int get_tj_max(int cpu, u32 *tj_max)
+{
+ u32 eax, edx;
+ u32 val;
+ int err;
+
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+ if (err)
+ goto err_ret;
+ else {
+ val = (eax >> 16) & 0xff;
+ if (val)
+ *tj_max = val * 1000;
+ else {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ }
+
+ return 0;
+err_ret:
+ *tj_max = 0;
+ return err;
+}
+
+static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
+{
+ u32 eax, edx;
+ struct phy_dev_entry *phy_dev_entry;
+
+ phy_dev_entry = tzd->devdata;
+ rdmsr_on_cpu(phy_dev_entry->first_cpu, MSR_IA32_PACKAGE_THERM_STATUS,
+ &eax, &edx);
+ if (eax & 0x80000000) {
+ *temp = phy_dev_entry->tj_max -
+ ((eax >> 16) & 0x7f) * 1000;
+ pr_debug("sys_get_curr_temp %ld\n", *temp);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int sys_get_trip_temp(struct thermal_zone_device *tzd,
+ int trip, unsigned long *temp)
+{
+ u32 eax, edx;
+ struct phy_dev_entry *phy_dev_entry;
+ u32 mask, shift;
+ unsigned long thres_reg_value;
+ int ret;
+
+ if (trip >= MAX_NUMBER_OF_TRIPS)
+ return -EINVAL;
+
+ phy_dev_entry = tzd->devdata;
+
+ if (trip) {
+ mask = THERM_MASK_THRESHOLD1;
+ shift = THERM_SHIFT_THRESHOLD1;
+ } else {
+ mask = THERM_MASK_THRESHOLD0;
+ shift = THERM_SHIFT_THRESHOLD0;
+ }
+
+ ret = rdmsr_on_cpu(phy_dev_entry->first_cpu,
+ MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx);
+ if (ret < 0)
+ return -EINVAL;
+
+ thres_reg_value = (eax & mask) >> shift;
+ if (thres_reg_value)
+ *temp = phy_dev_entry->tj_max - thres_reg_value * 1000;
+ else
+ *temp = 0;
+ pr_debug("sys_get_trip_temp %ld\n", *temp);
+
+ return 0;
+}
+
+int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
+ unsigned long temp)
+{
+ u32 l, h;
+ struct phy_dev_entry *phy_dev_entry;
+ u32 mask, shift, intr;
+ int ret;
+
+ phy_dev_entry = tzd->devdata;
+
+ if (trip >= MAX_NUMBER_OF_TRIPS || temp >= phy_dev_entry->tj_max)
+ return -EINVAL;
+
+ ret = rdmsr_on_cpu(phy_dev_entry->first_cpu,
+ MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ &l, &h);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (trip) {
+ mask = THERM_MASK_THRESHOLD1;
+ shift = THERM_SHIFT_THRESHOLD1;
+ intr = THERM_INT_THRESHOLD1_ENABLE;
+ } else {
+ mask = THERM_MASK_THRESHOLD0;
+ shift = THERM_SHIFT_THRESHOLD0;
+ intr = THERM_INT_THRESHOLD0_ENABLE;
+ }
+ l &= ~mask;
+ /*
+ * When users space sets a trip temperature == 0, which is indication
+ * that, it is no longer interested in receiving notifications.
+ */
+ if (!temp)
+ l &= ~intr;
+ else {
+ l |= (phy_dev_entry->tj_max - temp)/1000 << shift;
+ l |= intr;
+ }
+
+ return wrmsr_on_cpu(phy_dev_entry->first_cpu,
+ MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ l, h);
+}
+
+static int sys_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+
+ *type = THERMAL_TRIP_PASSIVE;
+
+ return 0;
+}
+
+/* Thermal zone callback registry */
+static struct thermal_zone_device_ops tzone_ops = {
+ .get_temp = sys_get_curr_temp,
+ .get_trip_temp = sys_get_trip_temp,
+ .get_trip_type = sys_get_trip_type,
+ .set_trip_temp = sys_set_trip_temp,
+};
+
+static bool pkg_temp_thermal_platform_thermal_rate_control(void)
+{
+ return true;
+}
+
+/* Enable threshold interrupt on local package/cpu */
+static inline void enable_pkg_thres_interrupt(void)
+{
+ u32 l, h;
+ u8 thres_0, thres_1;
+
+ rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
+ /* only enable/disable if it had valid threshold value */
+ thres_0 = (l & THERM_MASK_THRESHOLD0) >> THERM_SHIFT_THRESHOLD0;
+ thres_1 = (l & THERM_MASK_THRESHOLD1) >> THERM_SHIFT_THRESHOLD1;
+ if (thres_0)
+ l |= THERM_INT_THRESHOLD0_ENABLE;
+ if (thres_1)
+ l |= THERM_INT_THRESHOLD1_ENABLE;
+ wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
+}
+
+/* Disable threshold interrupt on local package/cpu */
+static inline void disable_pkg_thres_interrupt(void)
+{
+ u32 l, h;
+ rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
+ wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ l & (~THERM_INT_THRESHOLD0_ENABLE) &
+ (~THERM_INT_THRESHOLD1_ENABLE), h);
+}
+
+static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
+{
+ __u64 msr_val;
+ int cpu = smp_processor_id();
+ int phy_id = topology_physical_package_id(cpu);
+ struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu);
+ bool notify = false;
+
+ if (!phdev)
+ return;
+
+ spin_lock(&pkg_work_lock);
+ ++pkg_work_cnt;
+ if (unlikely(phy_id > max_phy_id)) {
+ spin_unlock(&pkg_work_lock);
+ return;
+ }
+ pkg_work_scheduled[phy_id] = 0;
+ spin_unlock(&pkg_work_lock);
+
+ enable_pkg_thres_interrupt();
+ rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
+ if (msr_val & THERM_LOG_THRESHOLD0) {
+ wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS,
+ msr_val & ~THERM_LOG_THRESHOLD0);
+ notify = true;
+ }
+ if (msr_val & THERM_LOG_THRESHOLD1) {
+ wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS,
+ msr_val & ~THERM_LOG_THRESHOLD1);
+ notify = true;
+ }
+ if (notify) {
+ pr_debug("thermal_zone_device_update\n");
+ thermal_zone_device_update(phdev->tzone);
+ }
+}
+
+static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val)
+{
+ unsigned long flags;
+ int cpu = smp_processor_id();
+ int phy_id = topology_physical_package_id(cpu);
+
+ /*
+ * When a package is in interrupted state, all CPU's in that package
+ * are in the same interrupt state. So scheduling on any one CPU in
+ * the package is enough and simply return for others.
+ */
+ spin_lock_irqsave(&pkg_work_lock, flags);
+ ++pkg_interrupt_cnt;
+ if (unlikely(phy_id > max_phy_id) || unlikely(!pkg_work_scheduled) ||
+ pkg_work_scheduled[phy_id]) {
+ disable_pkg_thres_interrupt();
+ spin_unlock_irqrestore(&pkg_work_lock, flags);
+ return -EINVAL;
+ }
+ pkg_work_scheduled[phy_id] = 1;
+ spin_unlock_irqrestore(&pkg_work_lock, flags);
+
+ disable_pkg_thres_interrupt();
+ schedule_delayed_work_on(cpu,
+ &per_cpu(pkg_temp_thermal_threshold_work, cpu),
+ msecs_to_jiffies(notify_delay_ms));
+ return 0;
+}
+
+static int find_siblings_cpu(int cpu)
+{
+ int i;
+ int id = topology_physical_package_id(cpu);
+
+ for_each_online_cpu(i)
+ if (i != cpu && topology_physical_package_id(i) == id)
+ return i;
+
+ return 0;
+}
+
+static int pkg_temp_thermal_device_add(unsigned int cpu)
+{
+ int err;
+ u32 tj_max;
+ struct phy_dev_entry *phy_dev_entry;
+ char buffer[30];
+ int thres_count;
+ u32 eax, ebx, ecx, edx;
+
+ cpuid(6, &eax, &ebx, &ecx, &edx);
+ thres_count = ebx & 0x07;
+ if (!thres_count)
+ return -ENODEV;
+
+ thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
+
+ err = get_tj_max(cpu, &tj_max);
+ if (err)
+ goto err_ret;
+
+ mutex_lock(&phy_dev_list_mutex);
+
+ phy_dev_entry = kzalloc(sizeof(*phy_dev_entry), GFP_KERNEL);
+ if (!phy_dev_entry) {
+ err = -ENOMEM;
+ goto err_ret_unlock;
+ }
+
+ spin_lock(&pkg_work_lock);
+ if (topology_physical_package_id(cpu) > max_phy_id)
+ max_phy_id = topology_physical_package_id(cpu);
+ pkg_work_scheduled = krealloc(pkg_work_scheduled,
+ (max_phy_id+1) * sizeof(u8), GFP_ATOMIC);
+ if (!pkg_work_scheduled) {
+ spin_unlock(&pkg_work_lock);
+ err = -ENOMEM;
+ goto err_ret_free;
+ }
+ pkg_work_scheduled[topology_physical_package_id(cpu)] = 0;
+ spin_unlock(&pkg_work_lock);
+
+ phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu);
+ phy_dev_entry->first_cpu = cpu;
+ phy_dev_entry->tj_max = tj_max;
+ phy_dev_entry->ref_cnt = 1;
+ snprintf(buffer, sizeof(buffer), "pkg-temp-%d\n",
+ phy_dev_entry->phys_proc_id);
+ phy_dev_entry->tzone = thermal_zone_device_register(buffer,
+ thres_count,
+ (thres_count == MAX_NUMBER_OF_TRIPS) ?
+ 0x03 : 0x01,
+ phy_dev_entry, &tzone_ops, NULL, 0, 0);
+ if (IS_ERR(phy_dev_entry->tzone)) {
+ err = PTR_ERR(phy_dev_entry->tzone);
+ goto err_ret_free;
+ }
+ /* Store MSR value for package thermal interrupt, to restore at exit */
+ rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ &phy_dev_entry->start_pkg_therm_low,
+ &phy_dev_entry->start_pkg_therm_high);
+
+ list_add_tail(&phy_dev_entry->list, &phy_dev_list);
+ pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n",
+ phy_dev_entry->phys_proc_id, cpu);
+
+ mutex_unlock(&phy_dev_list_mutex);
+
+ return 0;
+
+err_ret_free:
+ kfree(phy_dev_entry);
+err_ret_unlock:
+ mutex_unlock(&phy_dev_list_mutex);
+
+err_ret:
+ return err;
+}
+
+static int pkg_temp_thermal_device_remove(unsigned int cpu)
+{
+ struct phy_dev_entry *n;
+ u16 phys_proc_id = topology_physical_package_id(cpu);
+ struct phy_dev_entry *phdev =
+ pkg_temp_thermal_get_phy_entry(cpu);
+
+ if (!phdev)
+ return -ENODEV;
+
+ mutex_lock(&phy_dev_list_mutex);
+ /* If we are loosing the first cpu for this package, we need change */
+ if (phdev->first_cpu == cpu) {
+ phdev->first_cpu = find_siblings_cpu(cpu);
+ pr_debug("thermal_device_remove: first cpu switched %d\n",
+ phdev->first_cpu);
+ }
+ /*
+ * It is possible that no siblings left as this was the last cpu
+ * going offline. We don't need to worry about this assignment
+ * as the phydev entry will be removed in this case and
+ * thermal zone is removed.
+ */
+ --phdev->ref_cnt;
+ pr_debug("thermal_device_remove: pkg: %d cpu %d ref_cnt %d\n",
+ phys_proc_id, cpu, phdev->ref_cnt);
+ if (!phdev->ref_cnt)
+ list_for_each_entry_safe(phdev, n, &phy_dev_list, list) {
+ if (phdev->phys_proc_id == phys_proc_id) {
+ thermal_zone_device_unregister(phdev->tzone);
+ list_del(&phdev->list);
+ kfree(phdev);
+ break;
+ }
+ }
+ mutex_unlock(&phy_dev_list_mutex);
+
+ return 0;
+}
+
+static int get_core_online(unsigned int cpu)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu);
+
+ /* Check if there is already an instance for this package */
+ if (!phdev) {
+ if (!cpu_has(c, X86_FEATURE_DTHERM) &&
+ !cpu_has(c, X86_FEATURE_PTS))
+ return -ENODEV;
+ if (pkg_temp_thermal_device_add(cpu))
+ return -ENODEV;
+ } else {
+ mutex_lock(&phy_dev_list_mutex);
+ ++phdev->ref_cnt;
+ pr_debug("get_core_online: cpu %d ref_cnt %d\n",
+ cpu, phdev->ref_cnt);
+ mutex_unlock(&phy_dev_list_mutex);
+ }
+ INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu),
+ pkg_temp_thermal_threshold_work_fn);
+
+ pr_debug("get_core_online: cpu %d successful\n", cpu);
+
+ return 0;
+}
+
+static void put_core_offline(unsigned int cpu)
+{
+ if (!pkg_temp_thermal_device_remove(cpu))
+ cancel_delayed_work_sync(
+ &per_cpu(pkg_temp_thermal_threshold_work, cpu));
+
+ pr_debug("put_core_offline: cpu %d\n", cpu);
+}
+
+static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long) hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_DOWN_FAILED:
+ get_core_online(cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ put_core_offline(cpu);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block pkg_temp_thermal_notifier __refdata = {
+ .notifier_call = pkg_temp_thermal_cpu_callback,
+};
+
+static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = {
+ { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM },
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids);
+
+static int __init pkg_temp_thermal_init(void)
+{
+ int i;
+
+ if (!x86_match_cpu(pkg_temp_thermal_ids))
+ return -ENODEV;
+
+ spin_lock_init(&pkg_work_lock);
+ platform_thermal_package_notify =
+ pkg_temp_thermal_platform_thermal_notify;
+ platform_thermal_package_rate_control =
+ pkg_temp_thermal_platform_thermal_rate_control;
+
+ get_online_cpus();
+ for_each_online_cpu(i)
+ if (get_core_online(i))
+ goto err_ret;
+ register_hotcpu_notifier(&pkg_temp_thermal_notifier);
+ put_online_cpus();
+
+ pkg_temp_debugfs_init(); /* Don't care if fails */
+
+ return 0;
+
+err_ret:
+ get_online_cpus();
+ for_each_online_cpu(i)
+ put_core_offline(i);
+ put_online_cpus();
+ kfree(pkg_work_scheduled);
+ platform_thermal_package_notify = NULL;
+ platform_thermal_package_rate_control = NULL;
+
+ return -ENODEV;
+}
+
+static void __exit pkg_temp_thermal_exit(void)
+{
+ struct phy_dev_entry *phdev, *n;
+ int i;
+
+ get_online_cpus();
+ unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
+ mutex_lock(&phy_dev_list_mutex);
+ list_for_each_entry_safe(phdev, n, &phy_dev_list, list) {
+ /* Retore old MSR value for package thermal interrupt */
+ wrmsr_on_cpu(phdev->first_cpu,
+ MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ phdev->start_pkg_therm_low,
+ phdev->start_pkg_therm_high);
+ thermal_zone_device_unregister(phdev->tzone);
+ list_del(&phdev->list);
+ kfree(phdev);
+ }
+ mutex_unlock(&phy_dev_list_mutex);
+ platform_thermal_package_notify = NULL;
+ platform_thermal_package_rate_control = NULL;
+ for_each_online_cpu(i)
+ cancel_delayed_work_sync(
+ &per_cpu(pkg_temp_thermal_threshold_work, i));
+ put_online_cpus();
+
+ kfree(pkg_work_scheduled);
+
+ debugfs_remove_recursive(debugfs);
+}
+
+module_init(pkg_temp_thermal_init)
+module_exit(pkg_temp_thermal_exit)
+
+MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index d07b6af..76a8daa 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -29,6 +29,8 @@
#include <linux/clk.h>
#include <linux/pm_runtime.h>
+#include <asm/byteorder.h>
+
#include "8250.h"
/* Offsets for the DesignWare specific registers */
@@ -57,6 +59,7 @@
int last_lcr;
int line;
struct clk *clk;
+ u8 usr_reg;
};
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
@@ -77,6 +80,13 @@
return readb(p->membase + offset);
}
+/* Read Back (rb) version to ensure register access ording. */
+static void dw8250_serial_out_rb(struct uart_port *p, int offset, int value)
+{
+ dw8250_serial_out(p, offset, value);
+ dw8250_serial_in(p, UART_LCR);
+}
+
static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
@@ -104,7 +114,7 @@
return 1;
} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
/* Clear the USR and write the LCR again. */
- (void)p->serial_in(p, DW_UART_USR);
+ (void)p->serial_in(p, d->usr_reg);
p->serial_out(p, UART_LCR, d->last_lcr);
return 1;
@@ -125,77 +135,6 @@
pm_runtime_put_sync_suspend(port->dev);
}
-static int dw8250_probe_of(struct uart_port *p)
-{
- struct device_node *np = p->dev->of_node;
- u32 val;
-
- if (!of_property_read_u32(np, "reg-io-width", &val)) {
- switch (val) {
- case 1:
- break;
- case 4:
- p->iotype = UPIO_MEM32;
- p->serial_in = dw8250_serial_in32;
- p->serial_out = dw8250_serial_out32;
- break;
- default:
- dev_err(p->dev, "unsupported reg-io-width (%u)\n", val);
- return -EINVAL;
- }
- }
-
- if (!of_property_read_u32(np, "reg-shift", &val))
- p->regshift = val;
-
- /* clock got configured through clk api, all done */
- if (p->uartclk)
- return 0;
-
- /* try to find out clock frequency from DT as fallback */
- if (of_property_read_u32(np, "clock-frequency", &val)) {
- dev_err(p->dev, "clk or clock-frequency not defined\n");
- return -EINVAL;
- }
- p->uartclk = val;
-
- return 0;
-}
-
-#ifdef CONFIG_ACPI
-static int dw8250_probe_acpi(struct uart_8250_port *up)
-{
- const struct acpi_device_id *id;
- struct uart_port *p = &up->port;
-
- id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
- if (!id)
- return -ENODEV;
-
- p->iotype = UPIO_MEM32;
- p->serial_in = dw8250_serial_in32;
- p->serial_out = dw8250_serial_out32;
- p->regshift = 2;
-
- if (!p->uartclk)
- p->uartclk = (unsigned int)id->driver_data;
-
- up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL);
- if (!up->dma)
- return -ENOMEM;
-
- up->dma->rxconf.src_maxburst = p->fifosize / 4;
- up->dma->txconf.dst_maxburst = p->fifosize / 4;
-
- return 0;
-}
-#else
-static inline int dw8250_probe_acpi(struct uart_8250_port *up)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_ACPI */
-
static void dw8250_setup_port(struct uart_8250_port *up)
{
struct uart_port *p = &up->port;
@@ -228,6 +167,97 @@
up->capabilities |= UART_CAP_AFE;
}
+static int dw8250_probe_of(struct uart_port *p,
+ struct dw8250_data *data)
+{
+ struct device_node *np = p->dev->of_node;
+ u32 val;
+ bool has_ucv = true;
+
+ if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
+#ifdef __BIG_ENDIAN
+ /*
+ * Low order bits of these 64-bit registers, when
+ * accessed as a byte, are 7 bytes further down in the
+ * address space in big endian mode.
+ */
+ p->membase += 7;
+#endif
+ p->serial_out = dw8250_serial_out_rb;
+ p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+ p->type = PORT_OCTEON;
+ data->usr_reg = 0x27;
+ has_ucv = false;
+ } else if (!of_property_read_u32(np, "reg-io-width", &val)) {
+ switch (val) {
+ case 1:
+ break;
+ case 4:
+ p->iotype = UPIO_MEM32;
+ p->serial_in = dw8250_serial_in32;
+ p->serial_out = dw8250_serial_out32;
+ break;
+ default:
+ dev_err(p->dev, "unsupported reg-io-width (%u)\n", val);
+ return -EINVAL;
+ }
+ }
+ if (has_ucv)
+ dw8250_setup_port(container_of(p, struct uart_8250_port, port));
+
+ if (!of_property_read_u32(np, "reg-shift", &val))
+ p->regshift = val;
+
+ /* clock got configured through clk api, all done */
+ if (p->uartclk)
+ return 0;
+
+ /* try to find out clock frequency from DT as fallback */
+ if (of_property_read_u32(np, "clock-frequency", &val)) {
+ dev_err(p->dev, "clk or clock-frequency not defined\n");
+ return -EINVAL;
+ }
+ p->uartclk = val;
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static int dw8250_probe_acpi(struct uart_8250_port *up)
+{
+ const struct acpi_device_id *id;
+ struct uart_port *p = &up->port;
+
+ dw8250_setup_port(up);
+
+ id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
+ if (!id)
+ return -ENODEV;
+
+ p->iotype = UPIO_MEM32;
+ p->serial_in = dw8250_serial_in32;
+ p->serial_out = dw8250_serial_out32;
+ p->regshift = 2;
+
+ if (!p->uartclk)
+ p->uartclk = (unsigned int)id->driver_data;
+
+ up->dma = devm_kzalloc(p->dev, sizeof(*up->dma), GFP_KERNEL);
+ if (!up->dma)
+ return -ENOMEM;
+
+ up->dma->rxconf.src_maxburst = p->fifosize / 4;
+ up->dma->txconf.dst_maxburst = p->fifosize / 4;
+
+ return 0;
+}
+#else
+static inline int dw8250_probe_acpi(struct uart_8250_port *up)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_ACPI */
+
static int dw8250_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {};
@@ -259,6 +289,7 @@
if (!data)
return -ENOMEM;
+ data->usr_reg = DW_UART_USR;
data->clk = devm_clk_get(&pdev->dev, NULL);
if (!IS_ERR(data->clk)) {
clk_prepare_enable(data->clk);
@@ -270,10 +301,8 @@
uart.port.serial_out = dw8250_serial_out;
uart.port.private_data = data;
- dw8250_setup_port(&uart);
-
if (pdev->dev.of_node) {
- err = dw8250_probe_of(&uart.port);
+ err = dw8250_probe_of(&uart.port, data);
if (err)
return err;
} else if (ACPI_HANDLE(&pdev->dev)) {
@@ -362,6 +391,7 @@
static const struct of_device_id dw8250_of_match[] = {
{ .compatible = "snps,dw-apb-uart" },
+ { .compatible = "cavium,octeon-3860-uart" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw8250_of_match);
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
index 5f91c7a..e2a1f50 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -406,7 +406,7 @@
if (video == NULL)
return -ENOMEM;
- video->parent = &cdev->gadget->dev;
+ video->v4l2_dev = &uvc->v4l2_dev;
video->fops = &uvc_v4l2_fops;
video->release = video_device_release;
strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
@@ -563,6 +563,7 @@
INFO(cdev, "uvc_function_unbind\n");
video_unregister_device(uvc->vdev);
+ v4l2_device_unregister(&uvc->v4l2_dev);
uvc->control_ep->driver_data = NULL;
uvc->video.ep->driver_data = NULL;
@@ -690,6 +691,11 @@
if ((ret = usb_function_deactivate(f)) < 0)
goto error;
+ if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
+ printk(KERN_INFO "v4l2_device_register failed\n");
+ goto error;
+ }
+
/* Initialise video. */
ret = uvc_video_init(&uvc->video);
if (ret < 0)
@@ -705,6 +711,7 @@
return 0;
error:
+ v4l2_device_unregister(&uvc->v4l2_dev);
if (uvc->vdev)
video_device_release(uvc->vdev);
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c
index 7cacd6a..0ff3339 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/tcm_usb_gadget.c
@@ -1467,9 +1467,8 @@
return 0;
}
-static int usbg_queue_tm_rsp(struct se_cmd *se_cmd)
+static void usbg_queue_tm_rsp(struct se_cmd *se_cmd)
{
- return 0;
}
static const char *usbg_check_wwn(const char *name)
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h
index 817e9e1..7a9111d 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/uvc.h
@@ -57,6 +57,7 @@
#include <linux/videodev2.h>
#include <linux/version.h>
#include <media/v4l2-fh.h>
+#include <media/v4l2-device.h>
#include "uvc_queue.h"
@@ -145,6 +146,7 @@
struct uvc_device
{
struct video_device *vdev;
+ struct v4l2_device v4l2_dev;
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 2817013..4263d01 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -283,7 +283,7 @@
config USB_OCTEON_EHCI
bool "Octeon on-chip EHCI support"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
default n
select USB_EHCI_BIG_ENDIAN_MMIO
help
@@ -488,7 +488,7 @@
config USB_OCTEON_OHCI
bool "Octeon on-chip OHCI support"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
default USB_OCTEON_EHCI
select USB_OHCI_BIG_ENDIAN_MMIO
select USB_OHCI_LITTLE_ENDIAN
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 259ad28..c488da5 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -76,6 +76,7 @@
struct notifier_block nb;
struct list_head vfio_next;
struct list_head container_next;
+ atomic_t opened;
};
struct vfio_device {
@@ -206,6 +207,7 @@
INIT_LIST_HEAD(&group->device_list);
mutex_init(&group->device_lock);
atomic_set(&group->container_users, 0);
+ atomic_set(&group->opened, 0);
group->iommu_group = iommu_group;
group->nb.notifier_call = vfio_iommu_group_notifier;
@@ -1236,12 +1238,22 @@
static int vfio_group_fops_open(struct inode *inode, struct file *filep)
{
struct vfio_group *group;
+ int opened;
group = vfio_group_get_from_minor(iminor(inode));
if (!group)
return -ENODEV;
+ /* Do we need multiple instances of the group open? Seems not. */
+ opened = atomic_cmpxchg(&group->opened, 0, 1);
+ if (opened) {
+ vfio_group_put(group);
+ return -EBUSY;
+ }
+
+ /* Is something still in use from a previous open? */
if (group->container) {
+ atomic_dec(&group->opened);
vfio_group_put(group);
return -EBUSY;
}
@@ -1259,6 +1271,8 @@
vfio_group_try_dissolve_container(group);
+ atomic_dec(&group->opened);
+
vfio_group_put(group);
return 0;
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 6f3fbc4..a9807de 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/pci.h> /* pci_bus_type */
+#include <linux/rbtree.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -47,19 +48,25 @@
MODULE_PARM_DESC(allow_unsafe_interrupts,
"Enable VFIO IOMMU support for on platforms without interrupt remapping support.");
+static bool disable_hugepages;
+module_param_named(disable_hugepages,
+ disable_hugepages, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(disable_hugepages,
+ "Disable VFIO IOMMU support for IOMMU hugepages.");
+
struct vfio_iommu {
struct iommu_domain *domain;
struct mutex lock;
- struct list_head dma_list;
+ struct rb_root dma_list;
struct list_head group_list;
bool cache;
};
struct vfio_dma {
- struct list_head next;
+ struct rb_node node;
dma_addr_t iova; /* Device address */
unsigned long vaddr; /* Process virtual addr */
- long npage; /* Number of pages */
+ size_t size; /* Map size (bytes) */
int prot; /* IOMMU_READ/WRITE */
};
@@ -73,7 +80,48 @@
* into DMA'ble space using the IOMMU
*/
-#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT)
+static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
+ dma_addr_t start, size_t size)
+{
+ struct rb_node *node = iommu->dma_list.rb_node;
+
+ while (node) {
+ struct vfio_dma *dma = rb_entry(node, struct vfio_dma, node);
+
+ if (start + size <= dma->iova)
+ node = node->rb_left;
+ else if (start >= dma->iova + dma->size)
+ node = node->rb_right;
+ else
+ return dma;
+ }
+
+ return NULL;
+}
+
+static void vfio_insert_dma(struct vfio_iommu *iommu, struct vfio_dma *new)
+{
+ struct rb_node **link = &iommu->dma_list.rb_node, *parent = NULL;
+ struct vfio_dma *dma;
+
+ while (*link) {
+ parent = *link;
+ dma = rb_entry(parent, struct vfio_dma, node);
+
+ if (new->iova + new->size <= dma->iova)
+ link = &(*link)->rb_left;
+ else
+ link = &(*link)->rb_right;
+ }
+
+ rb_link_node(&new->node, parent, link);
+ rb_insert_color(&new->node, &iommu->dma_list);
+}
+
+static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *old)
+{
+ rb_erase(&old->node, &iommu->dma_list);
+}
struct vwork {
struct mm_struct *mm;
@@ -100,8 +148,8 @@
struct vwork *vwork;
struct mm_struct *mm;
- if (!current->mm)
- return; /* process exited */
+ if (!current->mm || !npage)
+ return; /* process exited or nothing to do */
if (down_write_trylock(¤t->mm->mmap_sem)) {
current->mm->locked_vm += npage;
@@ -173,33 +221,6 @@
return 0;
}
-/* Unmap DMA region */
-static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
- long npage, int prot)
-{
- long i, unlocked = 0;
-
- for (i = 0; i < npage; i++, iova += PAGE_SIZE) {
- unsigned long pfn;
-
- pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT;
- if (pfn) {
- iommu_unmap(iommu->domain, iova, PAGE_SIZE);
- unlocked += put_pfn(pfn, prot);
- }
- }
- return unlocked;
-}
-
-static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
- long npage, int prot)
-{
- long unlocked;
-
- unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot);
- vfio_lock_acct(-unlocked);
-}
-
static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn)
{
struct page *page[1];
@@ -226,198 +247,306 @@
return ret;
}
-/* Map DMA region */
-static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova,
- unsigned long vaddr, long npage, int prot)
+/*
+ * Attempt to pin pages. We really don't want to track all the pfns and
+ * the iommu can only map chunks of consecutive pfns anyway, so get the
+ * first page and all consecutive pages with the same locking.
+ */
+static long vfio_pin_pages(unsigned long vaddr, long npage,
+ int prot, unsigned long *pfn_base)
{
- dma_addr_t start = iova;
- long i, locked = 0;
- int ret;
+ unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ bool lock_cap = capable(CAP_IPC_LOCK);
+ long ret, i;
- /* Verify that pages are not already mapped */
- for (i = 0; i < npage; i++, iova += PAGE_SIZE)
- if (iommu_iova_to_phys(iommu->domain, iova))
- return -EBUSY;
+ if (!current->mm)
+ return -ENODEV;
- iova = start;
+ ret = vaddr_get_pfn(vaddr, prot, pfn_base);
+ if (ret)
+ return ret;
- if (iommu->cache)
- prot |= IOMMU_CACHE;
+ if (is_invalid_reserved_pfn(*pfn_base))
+ return 1;
- /*
- * XXX We break mappings into pages and use get_user_pages_fast to
- * pin the pages in memory. It's been suggested that mlock might
- * provide a more efficient mechanism, but nothing prevents the
- * user from munlocking the pages, which could then allow the user
- * access to random host memory. We also have no guarantee from the
- * IOMMU API that the iommu driver can unmap sub-pages of previous
- * mappings. This means we might lose an entire range if a single
- * page within it is unmapped. Single page mappings are inefficient,
- * but provide the most flexibility for now.
- */
- for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) {
+ if (!lock_cap && current->mm->locked_vm + 1 > limit) {
+ put_pfn(*pfn_base, prot);
+ pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__,
+ limit << PAGE_SHIFT);
+ return -ENOMEM;
+ }
+
+ if (unlikely(disable_hugepages)) {
+ vfio_lock_acct(1);
+ return 1;
+ }
+
+ /* Lock all the consecutive pages from pfn_base */
+ for (i = 1, vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) {
unsigned long pfn = 0;
ret = vaddr_get_pfn(vaddr, prot, &pfn);
- if (ret) {
- __vfio_dma_do_unmap(iommu, start, i, prot);
- return ret;
+ if (ret)
+ break;
+
+ if (pfn != *pfn_base + i || is_invalid_reserved_pfn(pfn)) {
+ put_pfn(pfn, prot);
+ break;
}
- /*
- * Only add actual locked pages to accounting
- * XXX We're effectively marking a page locked for every
- * IOVA page even though it's possible the user could be
- * backing multiple IOVAs with the same vaddr. This over-
- * penalizes the user process, but we currently have no
- * easy way to do this properly.
- */
- if (!is_invalid_reserved_pfn(pfn))
- locked++;
-
- ret = iommu_map(iommu->domain, iova,
- (phys_addr_t)pfn << PAGE_SHIFT,
- PAGE_SIZE, prot);
- if (ret) {
- /* Back out mappings on error */
+ if (!lock_cap && current->mm->locked_vm + i + 1 > limit) {
put_pfn(pfn, prot);
- __vfio_dma_do_unmap(iommu, start, i, prot);
- return ret;
+ pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
+ __func__, limit << PAGE_SHIFT);
+ break;
}
}
- vfio_lock_acct(locked);
+
+ vfio_lock_acct(i);
+
+ return i;
+}
+
+static long vfio_unpin_pages(unsigned long pfn, long npage,
+ int prot, bool do_accounting)
+{
+ unsigned long unlocked = 0;
+ long i;
+
+ for (i = 0; i < npage; i++)
+ unlocked += put_pfn(pfn++, prot);
+
+ if (do_accounting)
+ vfio_lock_acct(-unlocked);
+
+ return unlocked;
+}
+
+static int vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
+ dma_addr_t iova, size_t *size)
+{
+ dma_addr_t start = iova, end = iova + *size;
+ long unlocked = 0;
+
+ while (iova < end) {
+ size_t unmapped;
+ phys_addr_t phys;
+
+ /*
+ * We use the IOMMU to track the physical address. This
+ * saves us from having a lot more entries in our mapping
+ * tree. The downside is that we don't track the size
+ * used to do the mapping. We request unmap of a single
+ * page, but expect IOMMUs that support large pages to
+ * unmap a larger chunk.
+ */
+ phys = iommu_iova_to_phys(iommu->domain, iova);
+ if (WARN_ON(!phys)) {
+ iova += PAGE_SIZE;
+ continue;
+ }
+
+ unmapped = iommu_unmap(iommu->domain, iova, PAGE_SIZE);
+ if (!unmapped)
+ break;
+
+ unlocked += vfio_unpin_pages(phys >> PAGE_SHIFT,
+ unmapped >> PAGE_SHIFT,
+ dma->prot, false);
+ iova += unmapped;
+ }
+
+ vfio_lock_acct(-unlocked);
+
+ *size = iova - start;
+
return 0;
}
-static inline bool ranges_overlap(dma_addr_t start1, size_t size1,
- dma_addr_t start2, size_t size2)
+static int vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start,
+ size_t *size, struct vfio_dma *dma)
{
- if (start1 < start2)
- return (start2 - start1 < size1);
- else if (start2 < start1)
- return (start1 - start2 < size2);
- return (size1 > 0 && size2 > 0);
-}
-
-static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
- dma_addr_t start, size_t size)
-{
- struct vfio_dma *dma;
-
- list_for_each_entry(dma, &iommu->dma_list, next) {
- if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
- start, size))
- return dma;
- }
- return NULL;
-}
-
-static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start,
- size_t size, struct vfio_dma *dma)
-{
+ size_t offset, overlap, tmp;
struct vfio_dma *split;
- long npage_lo, npage_hi;
+ int ret;
- /* Existing dma region is completely covered, unmap all */
- if (start <= dma->iova &&
- start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
- vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
- list_del(&dma->next);
- npage_lo = dma->npage;
+ if (!*size)
+ return 0;
+
+ /*
+ * Existing dma region is completely covered, unmap all. This is
+ * the likely case since userspace tends to map and unmap buffers
+ * in one shot rather than multiple mappings within a buffer.
+ */
+ if (likely(start <= dma->iova &&
+ start + *size >= dma->iova + dma->size)) {
+ *size = dma->size;
+ ret = vfio_unmap_unpin(iommu, dma, dma->iova, size);
+ if (ret)
+ return ret;
+
+ /*
+ * Did we remove more than we have? Should never happen
+ * since a vfio_dma is contiguous in iova and vaddr.
+ */
+ WARN_ON(*size != dma->size);
+
+ vfio_remove_dma(iommu, dma);
kfree(dma);
- return npage_lo;
+ return 0;
}
/* Overlap low address of existing range */
if (start <= dma->iova) {
- size_t overlap;
+ overlap = start + *size - dma->iova;
+ ret = vfio_unmap_unpin(iommu, dma, dma->iova, &overlap);
+ if (ret)
+ return ret;
- overlap = start + size - dma->iova;
- npage_lo = overlap >> PAGE_SHIFT;
+ vfio_remove_dma(iommu, dma);
- vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot);
- dma->iova += overlap;
- dma->vaddr += overlap;
- dma->npage -= npage_lo;
- return npage_lo;
+ /*
+ * Check, we may have removed to whole vfio_dma. If not
+ * fixup and re-insert.
+ */
+ if (overlap < dma->size) {
+ dma->iova += overlap;
+ dma->vaddr += overlap;
+ dma->size -= overlap;
+ vfio_insert_dma(iommu, dma);
+ } else
+ kfree(dma);
+
+ *size = overlap;
+ return 0;
}
/* Overlap high address of existing range */
- if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
- size_t overlap;
+ if (start + *size >= dma->iova + dma->size) {
+ offset = start - dma->iova;
+ overlap = dma->size - offset;
- overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start;
- npage_hi = overlap >> PAGE_SHIFT;
+ ret = vfio_unmap_unpin(iommu, dma, start, &overlap);
+ if (ret)
+ return ret;
- vfio_dma_unmap(iommu, start, npage_hi, dma->prot);
- dma->npage -= npage_hi;
- return npage_hi;
+ dma->size -= overlap;
+ *size = overlap;
+ return 0;
}
/* Split existing */
- npage_lo = (start - dma->iova) >> PAGE_SHIFT;
- npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo;
- split = kzalloc(sizeof *split, GFP_KERNEL);
+ /*
+ * Allocate our tracking structure early even though it may not
+ * be used. An Allocation failure later loses track of pages and
+ * is more difficult to unwind.
+ */
+ split = kzalloc(sizeof(*split), GFP_KERNEL);
if (!split)
return -ENOMEM;
- vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot);
+ offset = start - dma->iova;
- dma->npage = npage_lo;
+ ret = vfio_unmap_unpin(iommu, dma, start, size);
+ if (ret || !*size) {
+ kfree(split);
+ return ret;
+ }
- split->npage = npage_hi;
- split->iova = start + size;
- split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size;
- split->prot = dma->prot;
- list_add(&split->next, &iommu->dma_list);
- return size >> PAGE_SHIFT;
+ tmp = dma->size;
+
+ /* Resize the lower vfio_dma in place, before the below insert */
+ dma->size = offset;
+
+ /* Insert new for remainder, assuming it didn't all get unmapped */
+ if (likely(offset + *size < tmp)) {
+ split->size = tmp - offset - *size;
+ split->iova = dma->iova + offset + *size;
+ split->vaddr = dma->vaddr + offset + *size;
+ split->prot = dma->prot;
+ vfio_insert_dma(iommu, split);
+ } else
+ kfree(split);
+
+ return 0;
}
static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
struct vfio_iommu_type1_dma_unmap *unmap)
{
- long ret = 0, npage = unmap->size >> PAGE_SHIFT;
- struct vfio_dma *dma, *tmp;
uint64_t mask;
+ struct vfio_dma *dma;
+ size_t unmapped = 0, size;
+ int ret = 0;
mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
if (unmap->iova & mask)
return -EINVAL;
- if (unmap->size & mask)
+ if (!unmap->size || unmap->size & mask)
return -EINVAL;
- /* XXX We still break these down into PAGE_SIZE */
WARN_ON(mask & PAGE_MASK);
mutex_lock(&iommu->lock);
- list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) {
- if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
- unmap->iova, unmap->size)) {
- ret = vfio_remove_dma_overlap(iommu, unmap->iova,
- unmap->size, dma);
- if (ret > 0)
- npage -= ret;
- if (ret < 0 || npage == 0)
- break;
- }
+ while ((dma = vfio_find_dma(iommu, unmap->iova, unmap->size))) {
+ size = unmap->size;
+ ret = vfio_remove_dma_overlap(iommu, unmap->iova, &size, dma);
+ if (ret || !size)
+ break;
+ unmapped += size;
}
+
mutex_unlock(&iommu->lock);
- return ret > 0 ? 0 : (int)ret;
+
+ /*
+ * We may unmap more than requested, update the unmap struct so
+ * userspace can know.
+ */
+ unmap->size = unmapped;
+
+ return ret;
+}
+
+/*
+ * Turns out AMD IOMMU has a page table bug where it won't map large pages
+ * to a region that previously mapped smaller pages. This should be fixed
+ * soon, so this is just a temporary workaround to break mappings down into
+ * PAGE_SIZE. Better to map smaller pages than nothing.
+ */
+static int map_try_harder(struct vfio_iommu *iommu, dma_addr_t iova,
+ unsigned long pfn, long npage, int prot)
+{
+ long i;
+ int ret;
+
+ for (i = 0; i < npage; i++, pfn++, iova += PAGE_SIZE) {
+ ret = iommu_map(iommu->domain, iova,
+ (phys_addr_t)pfn << PAGE_SHIFT,
+ PAGE_SIZE, prot);
+ if (ret)
+ break;
+ }
+
+ for (; i < npage && i > 0; i--, iova -= PAGE_SIZE)
+ iommu_unmap(iommu->domain, iova, PAGE_SIZE);
+
+ return ret;
}
static int vfio_dma_do_map(struct vfio_iommu *iommu,
struct vfio_iommu_type1_dma_map *map)
{
- struct vfio_dma *dma, *pdma = NULL;
- dma_addr_t iova = map->iova;
- unsigned long locked, lock_limit, vaddr = map->vaddr;
+ dma_addr_t end, iova;
+ unsigned long vaddr = map->vaddr;
size_t size = map->size;
+ long npage;
int ret = 0, prot = 0;
uint64_t mask;
- long npage;
+
+ end = map->iova + map->size;
mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
@@ -430,104 +559,144 @@
if (!prot)
return -EINVAL; /* No READ/WRITE? */
+ if (iommu->cache)
+ prot |= IOMMU_CACHE;
+
if (vaddr & mask)
return -EINVAL;
- if (iova & mask)
+ if (map->iova & mask)
return -EINVAL;
- if (size & mask)
+ if (!map->size || map->size & mask)
return -EINVAL;
- /* XXX We still break these down into PAGE_SIZE */
WARN_ON(mask & PAGE_MASK);
/* Don't allow IOVA wrap */
- if (iova + size && iova + size < iova)
+ if (end && end < map->iova)
return -EINVAL;
/* Don't allow virtual address wrap */
- if (vaddr + size && vaddr + size < vaddr)
- return -EINVAL;
-
- npage = size >> PAGE_SHIFT;
- if (!npage)
+ if (vaddr + map->size && vaddr + map->size < vaddr)
return -EINVAL;
mutex_lock(&iommu->lock);
- if (vfio_find_dma(iommu, iova, size)) {
- ret = -EBUSY;
- goto out_lock;
+ if (vfio_find_dma(iommu, map->iova, map->size)) {
+ mutex_unlock(&iommu->lock);
+ return -EEXIST;
}
- /* account for locked pages */
- locked = current->mm->locked_vm + npage;
- lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
- if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
- pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
- __func__, rlimit(RLIMIT_MEMLOCK));
- ret = -ENOMEM;
- goto out_lock;
- }
+ for (iova = map->iova; iova < end; iova += size, vaddr += size) {
+ struct vfio_dma *dma = NULL;
+ unsigned long pfn;
+ long i;
- ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot);
- if (ret)
- goto out_lock;
-
- /* Check if we abut a region below - nothing below 0 */
- if (iova) {
- dma = vfio_find_dma(iommu, iova - 1, 1);
- if (dma && dma->prot == prot &&
- dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) {
-
- dma->npage += npage;
- iova = dma->iova;
- vaddr = dma->vaddr;
- npage = dma->npage;
- size = NPAGE_TO_SIZE(npage);
-
- pdma = dma;
+ /* Pin a contiguous chunk of memory */
+ npage = vfio_pin_pages(vaddr, (end - iova) >> PAGE_SHIFT,
+ prot, &pfn);
+ if (npage <= 0) {
+ WARN_ON(!npage);
+ ret = (int)npage;
+ break;
}
- }
- /* Check if we abut a region above - nothing above ~0 + 1 */
- if (iova + size) {
- dma = vfio_find_dma(iommu, iova + size, 1);
- if (dma && dma->prot == prot &&
- dma->vaddr == vaddr + size) {
+ /* Verify pages are not already mapped */
+ for (i = 0; i < npage; i++) {
+ if (iommu_iova_to_phys(iommu->domain,
+ iova + (i << PAGE_SHIFT))) {
+ vfio_unpin_pages(pfn, npage, prot, true);
+ ret = -EBUSY;
+ break;
+ }
+ }
- dma->npage += npage;
+ ret = iommu_map(iommu->domain, iova,
+ (phys_addr_t)pfn << PAGE_SHIFT,
+ npage << PAGE_SHIFT, prot);
+ if (ret) {
+ if (ret != -EBUSY ||
+ map_try_harder(iommu, iova, pfn, npage, prot)) {
+ vfio_unpin_pages(pfn, npage, prot, true);
+ break;
+ }
+ }
+
+ size = npage << PAGE_SHIFT;
+
+ /*
+ * Check if we abut a region below - nothing below 0.
+ * This is the most likely case when mapping chunks of
+ * physically contiguous regions within a virtual address
+ * range. Update the abutting entry in place since iova
+ * doesn't change.
+ */
+ if (likely(iova)) {
+ struct vfio_dma *tmp;
+ tmp = vfio_find_dma(iommu, iova - 1, 1);
+ if (tmp && tmp->prot == prot &&
+ tmp->vaddr + tmp->size == vaddr) {
+ tmp->size += size;
+ iova = tmp->iova;
+ size = tmp->size;
+ vaddr = tmp->vaddr;
+ dma = tmp;
+ }
+ }
+
+ /*
+ * Check if we abut a region above - nothing above ~0 + 1.
+ * If we abut above and below, remove and free. If only
+ * abut above, remove, modify, reinsert.
+ */
+ if (likely(iova + size)) {
+ struct vfio_dma *tmp;
+ tmp = vfio_find_dma(iommu, iova + size, 1);
+ if (tmp && tmp->prot == prot &&
+ tmp->vaddr == vaddr + size) {
+ vfio_remove_dma(iommu, tmp);
+ if (dma) {
+ dma->size += tmp->size;
+ kfree(tmp);
+ } else {
+ size += tmp->size;
+ tmp->size = size;
+ tmp->iova = iova;
+ tmp->vaddr = vaddr;
+ vfio_insert_dma(iommu, tmp);
+ dma = tmp;
+ }
+ }
+ }
+
+ if (!dma) {
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+ if (!dma) {
+ iommu_unmap(iommu->domain, iova, size);
+ vfio_unpin_pages(pfn, npage, prot, true);
+ ret = -ENOMEM;
+ break;
+ }
+
+ dma->size = size;
dma->iova = iova;
dma->vaddr = vaddr;
-
- /*
- * If merged above and below, remove previously
- * merged entry. New entry covers it.
- */
- if (pdma) {
- list_del(&pdma->next);
- kfree(pdma);
- }
- pdma = dma;
+ dma->prot = prot;
+ vfio_insert_dma(iommu, dma);
}
}
- /* Isolated, new region */
- if (!pdma) {
- dma = kzalloc(sizeof *dma, GFP_KERNEL);
- if (!dma) {
- ret = -ENOMEM;
- vfio_dma_unmap(iommu, iova, npage, prot);
- goto out_lock;
+ if (ret) {
+ struct vfio_dma *tmp;
+ iova = map->iova;
+ size = map->size;
+ while ((tmp = vfio_find_dma(iommu, iova, size))) {
+ int r = vfio_remove_dma_overlap(iommu, iova,
+ &size, tmp);
+ if (WARN_ON(r || !size))
+ break;
}
-
- dma->npage = npage;
- dma->iova = iova;
- dma->vaddr = vaddr;
- dma->prot = prot;
- list_add(&dma->next, &iommu->dma_list);
}
-out_lock:
mutex_unlock(&iommu->lock);
return ret;
}
@@ -606,7 +775,7 @@
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&iommu->group_list);
- INIT_LIST_HEAD(&iommu->dma_list);
+ iommu->dma_list = RB_ROOT;
mutex_init(&iommu->lock);
/*
@@ -640,7 +809,7 @@
{
struct vfio_iommu *iommu = iommu_data;
struct vfio_group *group, *group_tmp;
- struct vfio_dma *dma, *dma_tmp;
+ struct rb_node *node;
list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) {
iommu_detach_group(iommu->domain, group->iommu_group);
@@ -648,10 +817,12 @@
kfree(group);
}
- list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) {
- vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
- list_del(&dma->next);
- kfree(dma);
+ while ((node = rb_first(&iommu->dma_list))) {
+ struct vfio_dma *dma = rb_entry(node, struct vfio_dma, node);
+ size_t size = dma->size;
+ vfio_remove_dma_overlap(iommu, dma->iova, &size, dma);
+ if (WARN_ON(!size))
+ break;
}
iommu_domain_free(iommu->domain);
@@ -706,6 +877,7 @@
} else if (cmd == VFIO_IOMMU_UNMAP_DMA) {
struct vfio_iommu_type1_dma_unmap unmap;
+ long ret;
minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size);
@@ -715,7 +887,11 @@
if (unmap.argsz < minsz || unmap.flags)
return -EINVAL;
- return vfio_dma_do_unmap(iommu, &unmap);
+ ret = vfio_dma_do_unmap(iommu, &unmap);
+ if (ret)
+ return ret;
+
+ return copy_to_user((void __user *)arg, &unmap, minsz);
}
return -ENOTTY;
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
index 8b9226d..017a1e8 100644
--- a/drivers/vhost/Kconfig
+++ b/drivers/vhost/Kconfig
@@ -1,6 +1,7 @@
config VHOST_NET
tristate "Host kernel accelerator for virtio net"
depends on NET && EVENTFD && (TUN || !TUN) && (MACVTAP || !MACVTAP)
+ select VHOST
select VHOST_RING
---help---
This kernel module can be loaded in host kernel to accelerate
@@ -13,6 +14,7 @@
config VHOST_SCSI
tristate "VHOST_SCSI TCM fabric driver"
depends on TARGET_CORE && EVENTFD && m
+ select VHOST
select VHOST_RING
default n
---help---
@@ -24,3 +26,9 @@
---help---
This option is selected by any driver which needs to access
the host side of a virtio ring.
+
+config VHOST
+ tristate
+ ---help---
+ This option is selected by any driver which needs to access
+ the core of vhost.
diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
index 654e9afb..e0441c3 100644
--- a/drivers/vhost/Makefile
+++ b/drivers/vhost/Makefile
@@ -1,7 +1,8 @@
obj-$(CONFIG_VHOST_NET) += vhost_net.o
-vhost_net-y := vhost.o net.o
+vhost_net-y := net.o
obj-$(CONFIG_VHOST_SCSI) += vhost_scsi.o
vhost_scsi-y := scsi.o
obj-$(CONFIG_VHOST_RING) += vringh.o
+obj-$(CONFIG_VHOST) += vhost.o
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 8ca5ac7..027be91 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -168,7 +168,7 @@
}
}
-int vhost_net_set_ubuf_info(struct vhost_net *n)
+static int vhost_net_set_ubuf_info(struct vhost_net *n)
{
bool zcopy;
int i;
@@ -189,7 +189,7 @@
return -ENOMEM;
}
-void vhost_net_vq_reset(struct vhost_net *n)
+static void vhost_net_vq_reset(struct vhost_net *n)
{
int i;
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 7014202..06adf31 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -49,7 +49,6 @@
#include <linux/llist.h>
#include <linux/bitmap.h>
-#include "vhost.c"
#include "vhost.h"
#define TCM_VHOST_VERSION "v0.1"
@@ -116,7 +115,6 @@
struct se_node_acl se_node_acl;
};
-struct vhost_scsi;
struct tcm_vhost_tpg {
/* Vhost port target portal group tag for TCM */
u16 tport_tpgt;
@@ -218,7 +216,7 @@
((unsigned long)iov->iov_base & PAGE_MASK)) >> PAGE_SHIFT;
}
-void tcm_vhost_done_inflight(struct kref *kref)
+static void tcm_vhost_done_inflight(struct kref *kref)
{
struct vhost_scsi_inflight *inflight;
@@ -329,11 +327,12 @@
return 1;
}
-static u32 tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg,
- struct se_node_acl *se_nacl,
- struct t10_pr_registration *pr_reg,
- int *format_code,
- unsigned char *buf)
+static u32
+tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
{
struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
@@ -359,10 +358,11 @@
format_code, buf);
}
-static u32 tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg,
- struct se_node_acl *se_nacl,
- struct t10_pr_registration *pr_reg,
- int *format_code)
+static u32
+tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
{
struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
@@ -388,10 +388,11 @@
format_code);
}
-static char *tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
- const char *buf,
- u32 *out_tid_len,
- char **port_nexus_ptr)
+static char *
+tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
{
struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
@@ -417,8 +418,8 @@
port_nexus_ptr);
}
-static struct se_node_acl *tcm_vhost_alloc_fabric_acl(
- struct se_portal_group *se_tpg)
+static struct se_node_acl *
+tcm_vhost_alloc_fabric_acl(struct se_portal_group *se_tpg)
{
struct tcm_vhost_nacl *nacl;
@@ -431,8 +432,9 @@
return &nacl->se_node_acl;
}
-static void tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg,
- struct se_node_acl *se_nacl)
+static void
+tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
{
struct tcm_vhost_nacl *nacl = container_of(se_nacl,
struct tcm_vhost_nacl, se_node_acl);
@@ -446,7 +448,19 @@
static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
{
- return;
+ struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd, tvc_se_cmd);
+
+ if (tv_cmd->tvc_sgl_count) {
+ u32 i;
+ for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
+ put_page(sg_page(&tv_cmd->tvc_sgl[i]));
+
+ kfree(tv_cmd->tvc_sgl);
+ }
+
+ tcm_vhost_put_inflight(tv_cmd->inflight);
+ kfree(tv_cmd);
}
static int tcm_vhost_shutdown_session(struct se_session *se_sess)
@@ -491,34 +505,34 @@
return 0;
}
-static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd)
+static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *cmd)
{
- struct vhost_scsi *vs = tv_cmd->tvc_vhost;
+ struct vhost_scsi *vs = cmd->tvc_vhost;
- llist_add(&tv_cmd->tvc_completion_list, &vs->vs_completion_list);
+ llist_add(&cmd->tvc_completion_list, &vs->vs_completion_list);
vhost_work_queue(&vs->dev, &vs->vs_completion_work);
}
static int tcm_vhost_queue_data_in(struct se_cmd *se_cmd)
{
- struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd *cmd = container_of(se_cmd,
struct tcm_vhost_cmd, tvc_se_cmd);
- vhost_scsi_complete_cmd(tv_cmd);
+ vhost_scsi_complete_cmd(cmd);
return 0;
}
static int tcm_vhost_queue_status(struct se_cmd *se_cmd)
{
- struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd *cmd = container_of(se_cmd,
struct tcm_vhost_cmd, tvc_se_cmd);
- vhost_scsi_complete_cmd(tv_cmd);
+ vhost_scsi_complete_cmd(cmd);
return 0;
}
-static int tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd)
+static void tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd)
{
- return 0;
+ return;
}
static void tcm_vhost_free_evt(struct vhost_scsi *vs, struct tcm_vhost_evt *evt)
@@ -527,8 +541,9 @@
kfree(evt);
}
-static struct tcm_vhost_evt *tcm_vhost_allocate_evt(struct vhost_scsi *vs,
- u32 event, u32 reason)
+static struct tcm_vhost_evt *
+tcm_vhost_allocate_evt(struct vhost_scsi *vs,
+ u32 event, u32 reason)
{
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
struct tcm_vhost_evt *evt;
@@ -552,28 +567,22 @@
return evt;
}
-static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd)
+static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *cmd)
{
- struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
/* TODO locking against target/backend threads? */
- transport_generic_free_cmd(se_cmd, 1);
+ transport_generic_free_cmd(se_cmd, 0);
- if (tv_cmd->tvc_sgl_count) {
- u32 i;
- for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
- put_page(sg_page(&tv_cmd->tvc_sgl[i]));
-
- kfree(tv_cmd->tvc_sgl);
- }
-
- tcm_vhost_put_inflight(tv_cmd->inflight);
-
- kfree(tv_cmd);
}
-static void tcm_vhost_do_evt_work(struct vhost_scsi *vs,
- struct tcm_vhost_evt *evt)
+static int vhost_scsi_check_stop_free(struct se_cmd *se_cmd)
+{
+ return target_put_sess_cmd(se_cmd->se_sess, se_cmd);
+}
+
+static void
+tcm_vhost_do_evt_work(struct vhost_scsi *vs, struct tcm_vhost_evt *evt)
{
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
struct virtio_scsi_event *event = &evt->event;
@@ -652,7 +661,7 @@
vs_completion_work);
DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ);
struct virtio_scsi_cmd_resp v_rsp;
- struct tcm_vhost_cmd *tv_cmd;
+ struct tcm_vhost_cmd *cmd;
struct llist_node *llnode;
struct se_cmd *se_cmd;
int ret, vq;
@@ -660,32 +669,32 @@
bitmap_zero(signal, VHOST_SCSI_MAX_VQ);
llnode = llist_del_all(&vs->vs_completion_list);
while (llnode) {
- tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd,
+ cmd = llist_entry(llnode, struct tcm_vhost_cmd,
tvc_completion_list);
llnode = llist_next(llnode);
- se_cmd = &tv_cmd->tvc_se_cmd;
+ se_cmd = &cmd->tvc_se_cmd;
pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__,
- tv_cmd, se_cmd->residual_count, se_cmd->scsi_status);
+ cmd, se_cmd->residual_count, se_cmd->scsi_status);
memset(&v_rsp, 0, sizeof(v_rsp));
v_rsp.resid = se_cmd->residual_count;
/* TODO is status_qualifier field needed? */
v_rsp.status = se_cmd->scsi_status;
v_rsp.sense_len = se_cmd->scsi_sense_length;
- memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf,
+ memcpy(v_rsp.sense, cmd->tvc_sense_buf,
v_rsp.sense_len);
- ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp));
+ ret = copy_to_user(cmd->tvc_resp, &v_rsp, sizeof(v_rsp));
if (likely(ret == 0)) {
struct vhost_scsi_virtqueue *q;
- vhost_add_used(tv_cmd->tvc_vq, tv_cmd->tvc_vq_desc, 0);
- q = container_of(tv_cmd->tvc_vq, struct vhost_scsi_virtqueue, vq);
+ vhost_add_used(cmd->tvc_vq, cmd->tvc_vq_desc, 0);
+ q = container_of(cmd->tvc_vq, struct vhost_scsi_virtqueue, vq);
vq = q - vs->vqs;
__set_bit(vq, signal);
} else
pr_err("Faulted on virtio_scsi_cmd_resp\n");
- vhost_scsi_free_cmd(tv_cmd);
+ vhost_scsi_free_cmd(cmd);
}
vq = -1;
@@ -694,35 +703,35 @@
vhost_signal(&vs->dev, &vs->vqs[vq].vq);
}
-static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd(
- struct vhost_virtqueue *vq,
- struct tcm_vhost_tpg *tv_tpg,
- struct virtio_scsi_cmd_req *v_req,
- u32 exp_data_len,
- int data_direction)
+static struct tcm_vhost_cmd *
+vhost_scsi_allocate_cmd(struct vhost_virtqueue *vq,
+ struct tcm_vhost_tpg *tpg,
+ struct virtio_scsi_cmd_req *v_req,
+ u32 exp_data_len,
+ int data_direction)
{
- struct tcm_vhost_cmd *tv_cmd;
+ struct tcm_vhost_cmd *cmd;
struct tcm_vhost_nexus *tv_nexus;
- tv_nexus = tv_tpg->tpg_nexus;
+ tv_nexus = tpg->tpg_nexus;
if (!tv_nexus) {
pr_err("Unable to locate active struct tcm_vhost_nexus\n");
return ERR_PTR(-EIO);
}
- tv_cmd = kzalloc(sizeof(struct tcm_vhost_cmd), GFP_ATOMIC);
- if (!tv_cmd) {
+ cmd = kzalloc(sizeof(struct tcm_vhost_cmd), GFP_ATOMIC);
+ if (!cmd) {
pr_err("Unable to allocate struct tcm_vhost_cmd\n");
return ERR_PTR(-ENOMEM);
}
- tv_cmd->tvc_tag = v_req->tag;
- tv_cmd->tvc_task_attr = v_req->task_attr;
- tv_cmd->tvc_exp_data_len = exp_data_len;
- tv_cmd->tvc_data_direction = data_direction;
- tv_cmd->tvc_nexus = tv_nexus;
- tv_cmd->inflight = tcm_vhost_get_inflight(vq);
+ cmd->tvc_tag = v_req->tag;
+ cmd->tvc_task_attr = v_req->task_attr;
+ cmd->tvc_exp_data_len = exp_data_len;
+ cmd->tvc_data_direction = data_direction;
+ cmd->tvc_nexus = tv_nexus;
+ cmd->inflight = tcm_vhost_get_inflight(vq);
- return tv_cmd;
+ return cmd;
}
/*
@@ -730,8 +739,11 @@
*
* Returns the number of scatterlist entries used or -errno on error.
*/
-static int vhost_scsi_map_to_sgl(struct scatterlist *sgl,
- unsigned int sgl_count, struct iovec *iov, int write)
+static int
+vhost_scsi_map_to_sgl(struct scatterlist *sgl,
+ unsigned int sgl_count,
+ struct iovec *iov,
+ int write)
{
unsigned int npages = 0, pages_nr, offset, nbytes;
struct scatterlist *sg = sgl;
@@ -775,8 +787,11 @@
return ret;
}
-static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd,
- struct iovec *iov, unsigned int niov, int write)
+static int
+vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd,
+ struct iovec *iov,
+ unsigned int niov,
+ int write)
{
int ret;
unsigned int i;
@@ -792,25 +807,25 @@
/* TODO overflow checking */
- sg = kmalloc(sizeof(tv_cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC);
+ sg = kmalloc(sizeof(cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC);
if (!sg)
return -ENOMEM;
pr_debug("%s sg %p sgl_count %u is_err %d\n", __func__,
sg, sgl_count, !sg);
sg_init_table(sg, sgl_count);
- tv_cmd->tvc_sgl = sg;
- tv_cmd->tvc_sgl_count = sgl_count;
+ cmd->tvc_sgl = sg;
+ cmd->tvc_sgl_count = sgl_count;
pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count);
for (i = 0; i < niov; i++) {
ret = vhost_scsi_map_to_sgl(sg, sgl_count, &iov[i], write);
if (ret < 0) {
- for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
- put_page(sg_page(&tv_cmd->tvc_sgl[i]));
- kfree(tv_cmd->tvc_sgl);
- tv_cmd->tvc_sgl = NULL;
- tv_cmd->tvc_sgl_count = 0;
+ for (i = 0; i < cmd->tvc_sgl_count; i++)
+ put_page(sg_page(&cmd->tvc_sgl[i]));
+ kfree(cmd->tvc_sgl);
+ cmd->tvc_sgl = NULL;
+ cmd->tvc_sgl_count = 0;
return ret;
}
@@ -822,15 +837,15 @@
static void tcm_vhost_submission_work(struct work_struct *work)
{
- struct tcm_vhost_cmd *tv_cmd =
+ struct tcm_vhost_cmd *cmd =
container_of(work, struct tcm_vhost_cmd, work);
struct tcm_vhost_nexus *tv_nexus;
- struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
int rc, sg_no_bidi = 0;
- if (tv_cmd->tvc_sgl_count) {
- sg_ptr = tv_cmd->tvc_sgl;
+ if (cmd->tvc_sgl_count) {
+ sg_ptr = cmd->tvc_sgl;
/* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */
#if 0
if (se_cmd->se_cmd_flags & SCF_BIDI) {
@@ -841,13 +856,13 @@
} else {
sg_ptr = NULL;
}
- tv_nexus = tv_cmd->tvc_nexus;
+ tv_nexus = cmd->tvc_nexus;
rc = target_submit_cmd_map_sgls(se_cmd, tv_nexus->tvn_se_sess,
- tv_cmd->tvc_cdb, &tv_cmd->tvc_sense_buf[0],
- tv_cmd->tvc_lun, tv_cmd->tvc_exp_data_len,
- tv_cmd->tvc_task_attr, tv_cmd->tvc_data_direction,
- 0, sg_ptr, tv_cmd->tvc_sgl_count,
+ cmd->tvc_cdb, &cmd->tvc_sense_buf[0],
+ cmd->tvc_lun, cmd->tvc_exp_data_len,
+ cmd->tvc_task_attr, cmd->tvc_data_direction,
+ TARGET_SCF_ACK_KREF, sg_ptr, cmd->tvc_sgl_count,
sg_bidi_ptr, sg_no_bidi);
if (rc < 0) {
transport_send_check_condition_and_sense(se_cmd,
@@ -856,8 +871,10 @@
}
}
-static void vhost_scsi_send_bad_target(struct vhost_scsi *vs,
- struct vhost_virtqueue *vq, int head, unsigned out)
+static void
+vhost_scsi_send_bad_target(struct vhost_scsi *vs,
+ struct vhost_virtqueue *vq,
+ int head, unsigned out)
{
struct virtio_scsi_cmd_resp __user *resp;
struct virtio_scsi_cmd_resp rsp;
@@ -873,13 +890,13 @@
pr_err("Faulted on virtio_scsi_cmd_resp\n");
}
-static void vhost_scsi_handle_vq(struct vhost_scsi *vs,
- struct vhost_virtqueue *vq)
+static void
+vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
{
struct tcm_vhost_tpg **vs_tpg;
struct virtio_scsi_cmd_req v_req;
- struct tcm_vhost_tpg *tv_tpg;
- struct tcm_vhost_cmd *tv_cmd;
+ struct tcm_vhost_tpg *tpg;
+ struct tcm_vhost_cmd *cmd;
u32 exp_data_len, data_first, data_num, data_direction;
unsigned out, in, i;
int head, ret;
@@ -964,10 +981,10 @@
/* Extract the tpgt */
target = v_req.lun[1];
- tv_tpg = ACCESS_ONCE(vs_tpg[target]);
+ tpg = ACCESS_ONCE(vs_tpg[target]);
/* Target does not exist, fail the request */
- if (unlikely(!tv_tpg)) {
+ if (unlikely(!tpg)) {
vhost_scsi_send_bad_target(vs, vq, head, out);
continue;
}
@@ -976,46 +993,46 @@
for (i = 0; i < data_num; i++)
exp_data_len += vq->iov[data_first + i].iov_len;
- tv_cmd = vhost_scsi_allocate_cmd(vq, tv_tpg, &v_req,
+ cmd = vhost_scsi_allocate_cmd(vq, tpg, &v_req,
exp_data_len, data_direction);
- if (IS_ERR(tv_cmd)) {
+ if (IS_ERR(cmd)) {
vq_err(vq, "vhost_scsi_allocate_cmd failed %ld\n",
- PTR_ERR(tv_cmd));
+ PTR_ERR(cmd));
goto err_cmd;
}
pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction"
- ": %d\n", tv_cmd, exp_data_len, data_direction);
+ ": %d\n", cmd, exp_data_len, data_direction);
- tv_cmd->tvc_vhost = vs;
- tv_cmd->tvc_vq = vq;
- tv_cmd->tvc_resp = vq->iov[out].iov_base;
+ cmd->tvc_vhost = vs;
+ cmd->tvc_vq = vq;
+ cmd->tvc_resp = vq->iov[out].iov_base;
/*
- * Copy in the recieved CDB descriptor into tv_cmd->tvc_cdb
+ * Copy in the recieved CDB descriptor into cmd->tvc_cdb
* that will be used by tcm_vhost_new_cmd_map() and down into
* target_setup_cmd_from_cdb()
*/
- memcpy(tv_cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
+ memcpy(cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
/*
* Check that the recieved CDB size does not exceeded our
* hardcoded max for tcm_vhost
*/
/* TODO what if cdb was too small for varlen cdb header? */
- if (unlikely(scsi_command_size(tv_cmd->tvc_cdb) >
+ if (unlikely(scsi_command_size(cmd->tvc_cdb) >
TCM_VHOST_MAX_CDB_SIZE)) {
vq_err(vq, "Received SCSI CDB with command_size: %d that"
" exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
- scsi_command_size(tv_cmd->tvc_cdb),
+ scsi_command_size(cmd->tvc_cdb),
TCM_VHOST_MAX_CDB_SIZE);
goto err_free;
}
- tv_cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+ cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
- tv_cmd->tvc_cdb[0], tv_cmd->tvc_lun);
+ cmd->tvc_cdb[0], cmd->tvc_lun);
if (data_direction != DMA_NONE) {
- ret = vhost_scsi_map_iov_to_sgl(tv_cmd,
+ ret = vhost_scsi_map_iov_to_sgl(cmd,
&vq->iov[data_first], data_num,
data_direction == DMA_TO_DEVICE);
if (unlikely(ret)) {
@@ -1029,22 +1046,22 @@
* complete the virtio-scsi request in TCM callback context via
* tcm_vhost_queue_data_in() and tcm_vhost_queue_status()
*/
- tv_cmd->tvc_vq_desc = head;
+ cmd->tvc_vq_desc = head;
/*
* Dispatch tv_cmd descriptor for cmwq execution in process
* context provided by tcm_vhost_workqueue. This also ensures
* tv_cmd is executed on the same kworker CPU as this vhost
* thread to gain positive L2 cache locality effects..
*/
- INIT_WORK(&tv_cmd->work, tcm_vhost_submission_work);
- queue_work(tcm_vhost_workqueue, &tv_cmd->work);
+ INIT_WORK(&cmd->work, tcm_vhost_submission_work);
+ queue_work(tcm_vhost_workqueue, &cmd->work);
}
mutex_unlock(&vq->mutex);
return;
err_free:
- vhost_scsi_free_cmd(tv_cmd);
+ vhost_scsi_free_cmd(cmd);
err_cmd:
vhost_scsi_send_bad_target(vs, vq, head, out);
mutex_unlock(&vq->mutex);
@@ -1055,8 +1072,12 @@
pr_debug("%s: The handling func for control queue.\n", __func__);
}
-static void tcm_vhost_send_evt(struct vhost_scsi *vs, struct tcm_vhost_tpg *tpg,
- struct se_lun *lun, u32 event, u32 reason)
+static void
+tcm_vhost_send_evt(struct vhost_scsi *vs,
+ struct tcm_vhost_tpg *tpg,
+ struct se_lun *lun,
+ u32 event,
+ u32 reason)
{
struct tcm_vhost_evt *evt;
@@ -1146,12 +1167,12 @@
* The lock nesting rule is:
* tcm_vhost_mutex -> vs->dev.mutex -> tpg->tv_tpg_mutex -> vq->mutex
*/
-static int vhost_scsi_set_endpoint(
- struct vhost_scsi *vs,
- struct vhost_scsi_target *t)
+static int
+vhost_scsi_set_endpoint(struct vhost_scsi *vs,
+ struct vhost_scsi_target *t)
{
struct tcm_vhost_tport *tv_tport;
- struct tcm_vhost_tpg *tv_tpg;
+ struct tcm_vhost_tpg *tpg;
struct tcm_vhost_tpg **vs_tpg;
struct vhost_virtqueue *vq;
int index, ret, i, len;
@@ -1178,32 +1199,32 @@
if (vs->vs_tpg)
memcpy(vs_tpg, vs->vs_tpg, len);
- list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) {
- mutex_lock(&tv_tpg->tv_tpg_mutex);
- if (!tv_tpg->tpg_nexus) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ list_for_each_entry(tpg, &tcm_vhost_list, tv_tpg_list) {
+ mutex_lock(&tpg->tv_tpg_mutex);
+ if (!tpg->tpg_nexus) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
continue;
}
- if (tv_tpg->tv_tpg_vhost_count != 0) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ if (tpg->tv_tpg_vhost_count != 0) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
continue;
}
- tv_tport = tv_tpg->tport;
+ tv_tport = tpg->tport;
if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) {
- if (vs->vs_tpg && vs->vs_tpg[tv_tpg->tport_tpgt]) {
+ if (vs->vs_tpg && vs->vs_tpg[tpg->tport_tpgt]) {
kfree(vs_tpg);
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
ret = -EEXIST;
goto out;
}
- tv_tpg->tv_tpg_vhost_count++;
- tv_tpg->vhost_scsi = vs;
- vs_tpg[tv_tpg->tport_tpgt] = tv_tpg;
+ tpg->tv_tpg_vhost_count++;
+ tpg->vhost_scsi = vs;
+ vs_tpg[tpg->tport_tpgt] = tpg;
smp_mb__after_atomic_inc();
match = true;
}
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
}
if (match) {
@@ -1236,12 +1257,12 @@
return ret;
}
-static int vhost_scsi_clear_endpoint(
- struct vhost_scsi *vs,
- struct vhost_scsi_target *t)
+static int
+vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
+ struct vhost_scsi_target *t)
{
struct tcm_vhost_tport *tv_tport;
- struct tcm_vhost_tpg *tv_tpg;
+ struct tcm_vhost_tpg *tpg;
struct vhost_virtqueue *vq;
bool match = false;
int index, ret, i;
@@ -1264,30 +1285,30 @@
for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) {
target = i;
- tv_tpg = vs->vs_tpg[target];
- if (!tv_tpg)
+ tpg = vs->vs_tpg[target];
+ if (!tpg)
continue;
- mutex_lock(&tv_tpg->tv_tpg_mutex);
- tv_tport = tv_tpg->tport;
+ mutex_lock(&tpg->tv_tpg_mutex);
+ tv_tport = tpg->tport;
if (!tv_tport) {
ret = -ENODEV;
goto err_tpg;
}
if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) {
- pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu"
+ pr_warn("tv_tport->tport_name: %s, tpg->tport_tpgt: %hu"
" does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n",
- tv_tport->tport_name, tv_tpg->tport_tpgt,
+ tv_tport->tport_name, tpg->tport_tpgt,
t->vhost_wwpn, t->vhost_tpgt);
ret = -EINVAL;
goto err_tpg;
}
- tv_tpg->tv_tpg_vhost_count--;
- tv_tpg->vhost_scsi = NULL;
+ tpg->tv_tpg_vhost_count--;
+ tpg->vhost_scsi = NULL;
vs->vs_tpg[target] = NULL;
match = true;
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
}
if (match) {
for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
@@ -1311,7 +1332,7 @@
return 0;
err_tpg:
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
err_dev:
mutex_unlock(&vs->dev.mutex);
mutex_unlock(&tcm_vhost_mutex);
@@ -1338,68 +1359,70 @@
static int vhost_scsi_open(struct inode *inode, struct file *f)
{
- struct vhost_scsi *s;
+ struct vhost_scsi *vs;
struct vhost_virtqueue **vqs;
int r, i;
- s = kzalloc(sizeof(*s), GFP_KERNEL);
- if (!s)
+ vs = kzalloc(sizeof(*vs), GFP_KERNEL);
+ if (!vs)
return -ENOMEM;
vqs = kmalloc(VHOST_SCSI_MAX_VQ * sizeof(*vqs), GFP_KERNEL);
if (!vqs) {
- kfree(s);
+ kfree(vs);
return -ENOMEM;
}
- vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work);
- vhost_work_init(&s->vs_event_work, tcm_vhost_evt_work);
+ vhost_work_init(&vs->vs_completion_work, vhost_scsi_complete_cmd_work);
+ vhost_work_init(&vs->vs_event_work, tcm_vhost_evt_work);
- s->vs_events_nr = 0;
- s->vs_events_missed = false;
+ vs->vs_events_nr = 0;
+ vs->vs_events_missed = false;
- vqs[VHOST_SCSI_VQ_CTL] = &s->vqs[VHOST_SCSI_VQ_CTL].vq;
- vqs[VHOST_SCSI_VQ_EVT] = &s->vqs[VHOST_SCSI_VQ_EVT].vq;
- s->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick;
- s->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick;
+ vqs[VHOST_SCSI_VQ_CTL] = &vs->vqs[VHOST_SCSI_VQ_CTL].vq;
+ vqs[VHOST_SCSI_VQ_EVT] = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
+ vs->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick;
+ vs->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick;
for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) {
- vqs[i] = &s->vqs[i].vq;
- s->vqs[i].vq.handle_kick = vhost_scsi_handle_kick;
+ vqs[i] = &vs->vqs[i].vq;
+ vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick;
}
- r = vhost_dev_init(&s->dev, vqs, VHOST_SCSI_MAX_VQ);
+ r = vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ);
- tcm_vhost_init_inflight(s, NULL);
+ tcm_vhost_init_inflight(vs, NULL);
if (r < 0) {
kfree(vqs);
- kfree(s);
+ kfree(vs);
return r;
}
- f->private_data = s;
+ f->private_data = vs;
return 0;
}
static int vhost_scsi_release(struct inode *inode, struct file *f)
{
- struct vhost_scsi *s = f->private_data;
+ struct vhost_scsi *vs = f->private_data;
struct vhost_scsi_target t;
- mutex_lock(&s->dev.mutex);
- memcpy(t.vhost_wwpn, s->vs_vhost_wwpn, sizeof(t.vhost_wwpn));
- mutex_unlock(&s->dev.mutex);
- vhost_scsi_clear_endpoint(s, &t);
- vhost_dev_stop(&s->dev);
- vhost_dev_cleanup(&s->dev, false);
+ mutex_lock(&vs->dev.mutex);
+ memcpy(t.vhost_wwpn, vs->vs_vhost_wwpn, sizeof(t.vhost_wwpn));
+ mutex_unlock(&vs->dev.mutex);
+ vhost_scsi_clear_endpoint(vs, &t);
+ vhost_dev_stop(&vs->dev);
+ vhost_dev_cleanup(&vs->dev, false);
/* Jobs can re-queue themselves in evt kick handler. Do extra flush. */
- vhost_scsi_flush(s);
- kfree(s->dev.vqs);
- kfree(s);
+ vhost_scsi_flush(vs);
+ kfree(vs->dev.vqs);
+ kfree(vs);
return 0;
}
-static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,
- unsigned long arg)
+static long
+vhost_scsi_ioctl(struct file *f,
+ unsigned int ioctl,
+ unsigned long arg)
{
struct vhost_scsi *vs = f->private_data;
struct vhost_scsi_target backend;
@@ -1515,8 +1538,9 @@
return "Unknown";
}
-static void tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg,
- struct se_lun *lun, bool plug)
+static void
+tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg,
+ struct se_lun *lun, bool plug)
{
struct vhost_scsi *vs = tpg->vhost_scsi;
@@ -1556,18 +1580,18 @@
}
static int tcm_vhost_port_link(struct se_portal_group *se_tpg,
- struct se_lun *lun)
+ struct se_lun *lun)
{
- struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
mutex_lock(&tcm_vhost_mutex);
- mutex_lock(&tv_tpg->tv_tpg_mutex);
- tv_tpg->tv_tpg_port_count++;
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_lock(&tpg->tv_tpg_mutex);
+ tpg->tv_tpg_port_count++;
+ mutex_unlock(&tpg->tv_tpg_mutex);
- tcm_vhost_hotplug(tv_tpg, lun);
+ tcm_vhost_hotplug(tpg, lun);
mutex_unlock(&tcm_vhost_mutex);
@@ -1575,26 +1599,26 @@
}
static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg,
- struct se_lun *lun)
+ struct se_lun *lun)
{
- struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
mutex_lock(&tcm_vhost_mutex);
- mutex_lock(&tv_tpg->tv_tpg_mutex);
- tv_tpg->tv_tpg_port_count--;
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_lock(&tpg->tv_tpg_mutex);
+ tpg->tv_tpg_port_count--;
+ mutex_unlock(&tpg->tv_tpg_mutex);
- tcm_vhost_hotunplug(tv_tpg, lun);
+ tcm_vhost_hotunplug(tpg, lun);
mutex_unlock(&tcm_vhost_mutex);
}
-static struct se_node_acl *tcm_vhost_make_nodeacl(
- struct se_portal_group *se_tpg,
- struct config_group *group,
- const char *name)
+static struct se_node_acl *
+tcm_vhost_make_nodeacl(struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
{
struct se_node_acl *se_nacl, *se_nacl_new;
struct tcm_vhost_nacl *nacl;
@@ -1635,23 +1659,23 @@
kfree(nacl);
}
-static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg,
- const char *name)
+static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg,
+ const char *name)
{
struct se_portal_group *se_tpg;
struct tcm_vhost_nexus *tv_nexus;
- mutex_lock(&tv_tpg->tv_tpg_mutex);
- if (tv_tpg->tpg_nexus) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
- pr_debug("tv_tpg->tpg_nexus already exists\n");
+ mutex_lock(&tpg->tv_tpg_mutex);
+ if (tpg->tpg_nexus) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ pr_debug("tpg->tpg_nexus already exists\n");
return -EEXIST;
}
- se_tpg = &tv_tpg->se_tpg;
+ se_tpg = &tpg->se_tpg;
tv_nexus = kzalloc(sizeof(struct tcm_vhost_nexus), GFP_KERNEL);
if (!tv_nexus) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
pr_err("Unable to allocate struct tcm_vhost_nexus\n");
return -ENOMEM;
}
@@ -1660,7 +1684,7 @@
*/
tv_nexus->tvn_se_sess = transport_init_session();
if (IS_ERR(tv_nexus->tvn_se_sess)) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
kfree(tv_nexus);
return -ENOMEM;
}
@@ -1672,7 +1696,7 @@
tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
se_tpg, (unsigned char *)name);
if (!tv_nexus->tvn_se_sess->se_node_acl) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
pr_debug("core_tpg_check_initiator_node_acl() failed"
" for %s\n", name);
transport_free_session(tv_nexus->tvn_se_sess);
@@ -1685,9 +1709,9 @@
*/
__transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
tv_nexus->tvn_se_sess, tv_nexus);
- tv_tpg->tpg_nexus = tv_nexus;
+ tpg->tpg_nexus = tv_nexus;
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
return 0;
}
@@ -1740,40 +1764,40 @@
}
static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg,
- char *page)
+ char *page)
{
- struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
struct tcm_vhost_nexus *tv_nexus;
ssize_t ret;
- mutex_lock(&tv_tpg->tv_tpg_mutex);
- tv_nexus = tv_tpg->tpg_nexus;
+ mutex_lock(&tpg->tv_tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
if (!tv_nexus) {
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
return -ENODEV;
}
ret = snprintf(page, PAGE_SIZE, "%s\n",
tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
- mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tpg->tv_tpg_mutex);
return ret;
}
static ssize_t tcm_vhost_tpg_store_nexus(struct se_portal_group *se_tpg,
- const char *page,
- size_t count)
+ const char *page,
+ size_t count)
{
- struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg);
- struct tcm_vhost_tport *tport_wwn = tv_tpg->tport;
+ struct tcm_vhost_tport *tport_wwn = tpg->tport;
unsigned char i_port[TCM_VHOST_NAMELEN], *ptr, *port_ptr;
int ret;
/*
* Shutdown the active I_T nexus if 'NULL' is passed..
*/
if (!strncmp(page, "NULL", 4)) {
- ret = tcm_vhost_drop_nexus(tv_tpg);
+ ret = tcm_vhost_drop_nexus(tpg);
return (!ret) ? count : ret;
}
/*
@@ -1831,7 +1855,7 @@
if (i_port[strlen(i_port)-1] == '\n')
i_port[strlen(i_port)-1] = '\0';
- ret = tcm_vhost_make_nexus(tv_tpg, port_ptr);
+ ret = tcm_vhost_make_nexus(tpg, port_ptr);
if (ret < 0)
return ret;
@@ -1845,9 +1869,10 @@
NULL,
};
-static struct se_portal_group *tcm_vhost_make_tpg(struct se_wwn *wwn,
- struct config_group *group,
- const char *name)
+static struct se_portal_group *
+tcm_vhost_make_tpg(struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
{
struct tcm_vhost_tport *tport = container_of(wwn,
struct tcm_vhost_tport, tport_wwn);
@@ -1903,9 +1928,10 @@
kfree(tpg);
}
-static struct se_wwn *tcm_vhost_make_tport(struct target_fabric_configfs *tf,
- struct config_group *group,
- const char *name)
+static struct se_wwn *
+tcm_vhost_make_tport(struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
{
struct tcm_vhost_tport *tport;
char *ptr;
@@ -1975,9 +2001,9 @@
kfree(tport);
}
-static ssize_t tcm_vhost_wwn_show_attr_version(
- struct target_fabric_configfs *tf,
- char *page)
+static ssize_t
+tcm_vhost_wwn_show_attr_version(struct target_fabric_configfs *tf,
+ char *page)
{
return sprintf(page, "TCM_VHOST fabric module %s on %s/%s"
"on "UTS_RELEASE"\n", TCM_VHOST_VERSION, utsname()->sysname,
@@ -2008,6 +2034,7 @@
.tpg_release_fabric_acl = tcm_vhost_release_fabric_acl,
.tpg_get_inst_index = tcm_vhost_tpg_get_inst_index,
.release_cmd = tcm_vhost_release_cmd,
+ .check_stop_free = vhost_scsi_check_stop_free,
.shutdown_session = tcm_vhost_shutdown_session,
.close_session = tcm_vhost_close_session,
.sess_get_index = tcm_vhost_sess_get_index,
diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c
index 1ee45bc..a73ea21 100644
--- a/drivers/vhost/test.c
+++ b/drivers/vhost/test.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include "test.h"
-#include "vhost.c"
+#include "vhost.h"
/* Max number of bytes transferred before requeueing the job.
* Using this limit prevents one virtqueue from starving others. */
@@ -38,17 +38,19 @@
* read-size critical section for our kind of RCU. */
static void handle_vq(struct vhost_test *n)
{
- struct vhost_virtqueue *vq = &n->dev.vqs[VHOST_TEST_VQ];
+ struct vhost_virtqueue *vq = &n->vqs[VHOST_TEST_VQ];
unsigned out, in;
int head;
size_t len, total_len = 0;
void *private;
- private = rcu_dereference_check(vq->private_data, 1);
- if (!private)
- return;
-
mutex_lock(&vq->mutex);
+ private = vq->private_data;
+ if (!private) {
+ mutex_unlock(&vq->mutex);
+ return;
+ }
+
vhost_disable_notify(&n->dev, vq);
for (;;) {
@@ -102,15 +104,23 @@
{
struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL);
struct vhost_dev *dev;
+ struct vhost_virtqueue **vqs;
int r;
if (!n)
return -ENOMEM;
+ vqs = kmalloc(VHOST_TEST_VQ_MAX * sizeof(*vqs), GFP_KERNEL);
+ if (!vqs) {
+ kfree(n);
+ return -ENOMEM;
+ }
dev = &n->dev;
+ vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ];
n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick;
- r = vhost_dev_init(dev, n->vqs, VHOST_TEST_VQ_MAX);
+ r = vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX);
if (r < 0) {
+ kfree(vqs);
kfree(n);
return r;
}
@@ -126,9 +136,8 @@
void *private;
mutex_lock(&vq->mutex);
- private = rcu_dereference_protected(vq->private_data,
- lockdep_is_held(&vq->mutex));
- rcu_assign_pointer(vq->private_data, NULL);
+ private = vq->private_data;
+ vq->private_data = NULL;
mutex_unlock(&vq->mutex);
return private;
}
@@ -140,7 +149,7 @@
static void vhost_test_flush_vq(struct vhost_test *n, int index)
{
- vhost_poll_flush(&n->dev.vqs[index].poll);
+ vhost_poll_flush(&n->vqs[index].poll);
}
static void vhost_test_flush(struct vhost_test *n)
@@ -268,14 +277,14 @@
return -EFAULT;
return vhost_test_run(n, test);
case VHOST_GET_FEATURES:
- features = VHOST_NET_FEATURES;
+ features = VHOST_FEATURES;
if (copy_to_user(featurep, &features, sizeof features))
return -EFAULT;
return 0;
case VHOST_SET_FEATURES:
if (copy_from_user(&features, featurep, sizeof features))
return -EFAULT;
- if (features & ~VHOST_NET_FEATURES)
+ if (features & ~VHOST_FEATURES)
return -EOPNOTSUPP;
return vhost_test_set_features(n, features);
case VHOST_RESET_OWNER:
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 60aa5ad..e58cf00 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/cgroup.h>
+#include <linux/module.h>
#include "vhost.h"
@@ -66,6 +67,7 @@
work->flushing = 0;
work->queue_seq = work->done_seq = 0;
}
+EXPORT_SYMBOL_GPL(vhost_work_init);
/* Init poll structure */
void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,
@@ -79,6 +81,7 @@
vhost_work_init(&poll->work, fn);
}
+EXPORT_SYMBOL_GPL(vhost_poll_init);
/* Start polling a file. We add ourselves to file's wait queue. The caller must
* keep a reference to a file until after vhost_poll_stop is called. */
@@ -101,6 +104,7 @@
return ret;
}
+EXPORT_SYMBOL_GPL(vhost_poll_start);
/* Stop polling a file. After this function returns, it becomes safe to drop the
* file reference. You must also flush afterwards. */
@@ -111,6 +115,7 @@
poll->wqh = NULL;
}
}
+EXPORT_SYMBOL_GPL(vhost_poll_stop);
static bool vhost_work_seq_done(struct vhost_dev *dev, struct vhost_work *work,
unsigned seq)
@@ -123,7 +128,7 @@
return left <= 0;
}
-static void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work)
+void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work)
{
unsigned seq;
int flushing;
@@ -138,6 +143,7 @@
spin_unlock_irq(&dev->work_lock);
BUG_ON(flushing < 0);
}
+EXPORT_SYMBOL_GPL(vhost_work_flush);
/* Flush any work that has been scheduled. When calling this, don't hold any
* locks that are also used by the callback. */
@@ -145,6 +151,7 @@
{
vhost_work_flush(poll->dev, &poll->work);
}
+EXPORT_SYMBOL_GPL(vhost_poll_flush);
void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work)
{
@@ -158,11 +165,13 @@
}
spin_unlock_irqrestore(&dev->work_lock, flags);
}
+EXPORT_SYMBOL_GPL(vhost_work_queue);
void vhost_poll_queue(struct vhost_poll *poll)
{
vhost_work_queue(poll->dev, &poll->work);
}
+EXPORT_SYMBOL_GPL(vhost_poll_queue);
static void vhost_vq_reset(struct vhost_dev *dev,
struct vhost_virtqueue *vq)
@@ -251,17 +260,16 @@
/* Helper to allocate iovec buffers for all vqs. */
static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
{
+ struct vhost_virtqueue *vq;
int i;
for (i = 0; i < dev->nvqs; ++i) {
- dev->vqs[i]->indirect = kmalloc(sizeof *dev->vqs[i]->indirect *
- UIO_MAXIOV, GFP_KERNEL);
- dev->vqs[i]->log = kmalloc(sizeof *dev->vqs[i]->log * UIO_MAXIOV,
- GFP_KERNEL);
- dev->vqs[i]->heads = kmalloc(sizeof *dev->vqs[i]->heads *
- UIO_MAXIOV, GFP_KERNEL);
- if (!dev->vqs[i]->indirect || !dev->vqs[i]->log ||
- !dev->vqs[i]->heads)
+ vq = dev->vqs[i];
+ vq->indirect = kmalloc(sizeof *vq->indirect * UIO_MAXIOV,
+ GFP_KERNEL);
+ vq->log = kmalloc(sizeof *vq->log * UIO_MAXIOV, GFP_KERNEL);
+ vq->heads = kmalloc(sizeof *vq->heads * UIO_MAXIOV, GFP_KERNEL);
+ if (!vq->indirect || !vq->log || !vq->heads)
goto err_nomem;
}
return 0;
@@ -283,6 +291,7 @@
long vhost_dev_init(struct vhost_dev *dev,
struct vhost_virtqueue **vqs, int nvqs)
{
+ struct vhost_virtqueue *vq;
int i;
dev->vqs = vqs;
@@ -297,19 +306,21 @@
dev->worker = NULL;
for (i = 0; i < dev->nvqs; ++i) {
- dev->vqs[i]->log = NULL;
- dev->vqs[i]->indirect = NULL;
- dev->vqs[i]->heads = NULL;
- dev->vqs[i]->dev = dev;
- mutex_init(&dev->vqs[i]->mutex);
- vhost_vq_reset(dev, dev->vqs[i]);
- if (dev->vqs[i]->handle_kick)
- vhost_poll_init(&dev->vqs[i]->poll,
- dev->vqs[i]->handle_kick, POLLIN, dev);
+ vq = dev->vqs[i];
+ vq->log = NULL;
+ vq->indirect = NULL;
+ vq->heads = NULL;
+ vq->dev = dev;
+ mutex_init(&vq->mutex);
+ vhost_vq_reset(dev, vq);
+ if (vq->handle_kick)
+ vhost_poll_init(&vq->poll, vq->handle_kick,
+ POLLIN, dev);
}
return 0;
}
+EXPORT_SYMBOL_GPL(vhost_dev_init);
/* Caller should have device mutex */
long vhost_dev_check_owner(struct vhost_dev *dev)
@@ -317,6 +328,7 @@
/* Are you the owner? If not, I don't think you mean to do that */
return dev->mm == current->mm ? 0 : -EPERM;
}
+EXPORT_SYMBOL_GPL(vhost_dev_check_owner);
struct vhost_attach_cgroups_struct {
struct vhost_work work;
@@ -348,6 +360,7 @@
{
return dev->mm;
}
+EXPORT_SYMBOL_GPL(vhost_dev_has_owner);
/* Caller should have device mutex */
long vhost_dev_set_owner(struct vhost_dev *dev)
@@ -391,11 +404,13 @@
err_mm:
return err;
}
+EXPORT_SYMBOL_GPL(vhost_dev_set_owner);
struct vhost_memory *vhost_dev_reset_owner_prepare(void)
{
return kmalloc(offsetof(struct vhost_memory, regions), GFP_KERNEL);
}
+EXPORT_SYMBOL_GPL(vhost_dev_reset_owner_prepare);
/* Caller should have device mutex */
void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_memory *memory)
@@ -406,6 +421,7 @@
memory->nregions = 0;
RCU_INIT_POINTER(dev->memory, memory);
}
+EXPORT_SYMBOL_GPL(vhost_dev_reset_owner);
void vhost_dev_stop(struct vhost_dev *dev)
{
@@ -418,6 +434,7 @@
}
}
}
+EXPORT_SYMBOL_GPL(vhost_dev_stop);
/* Caller should have device mutex if and only if locked is set */
void vhost_dev_cleanup(struct vhost_dev *dev, bool locked)
@@ -458,6 +475,7 @@
mmput(dev->mm);
dev->mm = NULL;
}
+EXPORT_SYMBOL_GPL(vhost_dev_cleanup);
static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz)
{
@@ -543,6 +561,7 @@
lockdep_is_held(&dev->mutex));
return memory_access_ok(dev, mp, 1);
}
+EXPORT_SYMBOL_GPL(vhost_log_access_ok);
/* Verify access for write logging. */
/* Caller should have vq mutex and device mutex */
@@ -568,6 +587,7 @@
return vq_access_ok(vq->dev, vq->num, vq->desc, vq->avail, vq->used) &&
vq_log_access_ok(vq->dev, vq, vq->log_base);
}
+EXPORT_SYMBOL_GPL(vhost_vq_access_ok);
static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
{
@@ -797,6 +817,7 @@
vhost_poll_flush(&vq->poll);
return r;
}
+EXPORT_SYMBOL_GPL(vhost_vring_ioctl);
/* Caller must have device mutex */
long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp)
@@ -877,6 +898,7 @@
done:
return r;
}
+EXPORT_SYMBOL_GPL(vhost_dev_ioctl);
static const struct vhost_memory_region *find_region(struct vhost_memory *mem,
__u64 addr, __u32 len)
@@ -968,6 +990,7 @@
BUG();
return 0;
}
+EXPORT_SYMBOL_GPL(vhost_log_write);
static int vhost_update_used_flags(struct vhost_virtqueue *vq)
{
@@ -1019,6 +1042,7 @@
vq->signalled_used_valid = false;
return get_user(vq->last_used_idx, &vq->used->idx);
}
+EXPORT_SYMBOL_GPL(vhost_init_used);
static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len,
struct iovec iov[], int iov_size)
@@ -1295,12 +1319,14 @@
BUG_ON(!(vq->used_flags & VRING_USED_F_NO_NOTIFY));
return head;
}
+EXPORT_SYMBOL_GPL(vhost_get_vq_desc);
/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */
void vhost_discard_vq_desc(struct vhost_virtqueue *vq, int n)
{
vq->last_avail_idx -= n;
}
+EXPORT_SYMBOL_GPL(vhost_discard_vq_desc);
/* After we've used one of their buffers, we tell them about it. We'll then
* want to notify the guest, using eventfd. */
@@ -1349,6 +1375,7 @@
vq->signalled_used_valid = false;
return 0;
}
+EXPORT_SYMBOL_GPL(vhost_add_used);
static int __vhost_add_used_n(struct vhost_virtqueue *vq,
struct vring_used_elem *heads,
@@ -1418,6 +1445,7 @@
}
return r;
}
+EXPORT_SYMBOL_GPL(vhost_add_used_n);
static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
{
@@ -1462,6 +1490,7 @@
if (vq->call_ctx && vhost_notify(dev, vq))
eventfd_signal(vq->call_ctx, 1);
}
+EXPORT_SYMBOL_GPL(vhost_signal);
/* And here's the combo meal deal. Supersize me! */
void vhost_add_used_and_signal(struct vhost_dev *dev,
@@ -1471,6 +1500,7 @@
vhost_add_used(vq, head, len);
vhost_signal(dev, vq);
}
+EXPORT_SYMBOL_GPL(vhost_add_used_and_signal);
/* multi-buffer version of vhost_add_used_and_signal */
void vhost_add_used_and_signal_n(struct vhost_dev *dev,
@@ -1480,6 +1510,7 @@
vhost_add_used_n(vq, heads, count);
vhost_signal(dev, vq);
}
+EXPORT_SYMBOL_GPL(vhost_add_used_and_signal_n);
/* OK, now we need to know about added descriptors. */
bool vhost_enable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
@@ -1517,6 +1548,7 @@
return avail_idx != vq->avail_idx;
}
+EXPORT_SYMBOL_GPL(vhost_enable_notify);
/* We don't need to be notified again. */
void vhost_disable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq)
@@ -1533,3 +1565,21 @@
&vq->used->flags, r);
}
}
+EXPORT_SYMBOL_GPL(vhost_disable_notify);
+
+static int __init vhost_init(void)
+{
+ return 0;
+}
+
+static void __exit vhost_exit(void)
+{
+}
+
+module_init(vhost_init);
+module_exit(vhost_exit);
+
+MODULE_VERSION("0.0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Michael S. Tsirkin");
+MODULE_DESCRIPTION("Host kernel accelerator for virtio");
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index 64adcf9..42298cd 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -46,6 +46,8 @@
void vhost_poll_stop(struct vhost_poll *poll);
void vhost_poll_flush(struct vhost_poll *poll);
void vhost_poll_queue(struct vhost_poll *poll);
+void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work);
+long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp);
struct vhost_log {
u64 addr;
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 0098810..1f572c0 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -192,7 +192,8 @@
* virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST);
* is true, we *have* to do it in this order
*/
- tell_host(vb, vb->deflate_vq);
+ if (vb->num_pfns != 0)
+ tell_host(vb, vb->deflate_vq);
mutex_unlock(&vb->balloon_lock);
release_pages_by_pfn(vb->pfns, vb->num_pfns);
}
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index a7ce730..1aba255 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -289,9 +289,9 @@
pci_disable_msix(vp_dev->pci_dev);
vp_dev->msix_enabled = 0;
- vp_dev->msix_vectors = 0;
}
+ vp_dev->msix_vectors = 0;
vp_dev->msix_used_vectors = 0;
kfree(vp_dev->msix_names);
vp_dev->msix_names = NULL;
@@ -309,6 +309,8 @@
unsigned i, v;
int err = -ENOMEM;
+ vp_dev->msix_vectors = nvectors;
+
vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries,
GFP_KERNEL);
if (!vp_dev->msix_entries)
@@ -336,7 +338,6 @@
err = -ENOSPC;
if (err)
goto error;
- vp_dev->msix_vectors = nvectors;
vp_dev->msix_enabled = 1;
/* Set the vector used for configuration */
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7460d34..362085d 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -221,15 +221,6 @@
To compile this driver as a module, choose M here: the
module will be called dw_wdt.
-config MPCORE_WATCHDOG
- tristate "MPcore watchdog"
- depends on HAVE_ARM_TWD
- help
- Watchdog timer embedded into the MPcore system.
-
- To compile this driver as a module, choose M here: the
- module will be called mpcore_wdt.
-
config EP93XX_WATCHDOG
tristate "EP93xx Watchdog"
depends on ARCH_EP93XX
@@ -291,7 +282,7 @@
config ORION_WATCHDOG
tristate "Orion watchdog"
- depends on ARCH_ORION5X || ARCH_KIRKWOOD
+ depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE
select WATCHDOG_CORE
help
Say Y here if to include support for the watchdog timer
@@ -1083,7 +1074,7 @@
config OCTEON_WDT
tristate "Cavium OCTEON SOC family Watchdog Timer"
- depends on CPU_CAVIUM_OCTEON
+ depends on CAVIUM_OCTEON_SOC
default y
select EXPORT_UASM if OCTEON_WDT = m
help
@@ -1109,6 +1100,17 @@
To compile this driver as a loadable module, choose M here.
The module will be called bcm63xx_wdt.
+config BCM2835_WDT
+ tristate "Broadcom BCM2835 hardware watchdog"
+ depends on ARCH_BCM2835
+ select WATCHDOG_CORE
+ help
+ Watchdog driver for the built in watchdog hardware in Broadcom
+ BCM2835 SoC.
+
+ To compile this driver as a loadable module, choose M here.
+ The module will be called bcm2835_wdt.
+
config LANTIQ_WDT
tristate "Lantiq SoC watchdog"
depends on LANTIQ
@@ -1183,6 +1185,18 @@
The value can be overridden by the wdt_period command-line parameter.
+config MEN_A21_WDT
+ tristate "MEN A21 VME CPU Carrier Board Watchdog Timer"
+ select WATCHDOG_CORE
+ depends on GPIOLIB
+ help
+ Watchdog driver for MEN A21 VMEbus CPU Carrier Boards.
+
+ The driver can also be built as a module. If so, the module will be
+ called mena21_wdt.
+
+ If unsure select N here.
+
# PPC64 Architecture
config WATCHDOG_RTAS
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index ec26899..2f26a0b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -41,7 +41,6 @@
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
-obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
@@ -54,6 +53,7 @@
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
+obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -144,6 +144,7 @@
obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o
obj-$(CONFIG_PIKA_WDT) += pika_wdt.o
obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o
+obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o
# PPC64 Architecture
obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c
index 7a715e3..b178e71 100644
--- a/drivers/watchdog/at32ap700x_wdt.c
+++ b/drivers/watchdog/at32ap700x_wdt.c
@@ -321,13 +321,14 @@
return -ENXIO;
}
- wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL);
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x),
+ GFP_KERNEL);
if (!wdt) {
dev_dbg(&pdev->dev, "no memory for wdt structure\n");
return -ENOMEM;
}
- wdt->regs = ioremap(regs->start, resource_size(regs));
+ wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
if (!wdt->regs) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "could not map I/O memory\n");
@@ -342,7 +343,7 @@
dev_info(&pdev->dev, "CPU must be reset with external "
"reset or POR due to silicon errata.\n");
ret = -EIO;
- goto err_iounmap;
+ goto err_free;
} else {
wdt->users = 0;
}
@@ -364,7 +365,7 @@
ret = misc_register(&wdt->miscdev);
if (ret) {
dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
- goto err_register;
+ goto err_free;
}
dev_info(&pdev->dev,
@@ -373,12 +374,7 @@
return 0;
-err_register:
- platform_set_drvdata(pdev, NULL);
-err_iounmap:
- iounmap(wdt->regs);
err_free:
- kfree(wdt);
wdt = NULL;
return ret;
}
@@ -391,10 +387,7 @@
at32_wdt_stop();
misc_deregister(&wdt->miscdev);
- iounmap(wdt->regs);
- kfree(wdt);
wdt = NULL;
- platform_set_drvdata(pdev, NULL);
}
return 0;
}
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
new file mode 100644
index 0000000..61566fc
--- /dev/null
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -0,0 +1,189 @@
+/*
+ * Watchdog driver for Broadcom BCM2835
+ *
+ * "bcm2708_wdog" driver written by Luke Diamand that was obtained from
+ * branch "rpi-3.6.y" of git://github.com/raspberrypi/linux.git was used
+ * as a hardware reference for the Broadcom BCM2835 watchdog timer.
+ *
+ * Copyright (C) 2013 Lubomir Rintel <lkundrak@v3.sk>
+ *
+ * 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/types.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/miscdevice.h>
+
+#define PM_RSTC 0x1c
+#define PM_WDOG 0x24
+
+#define PM_PASSWORD 0x5a000000
+
+#define PM_WDOG_TIME_SET 0x000fffff
+#define PM_RSTC_WRCFG_CLR 0xffffffcf
+#define PM_RSTC_WRCFG_SET 0x00000030
+#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
+#define PM_RSTC_RESET 0x00000102
+
+#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
+#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
+
+struct bcm2835_wdt {
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+static unsigned int heartbeat;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+static int bcm2835_wdt_start(struct watchdog_device *wdog)
+{
+ struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
+ uint32_t cur;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wdt->lock, flags);
+
+ writel_relaxed(PM_PASSWORD | (SECS_TO_WDOG_TICKS(wdog->timeout) &
+ PM_WDOG_TIME_SET), wdt->base + PM_WDOG);
+ cur = readl_relaxed(wdt->base + PM_RSTC);
+ writel_relaxed(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
+ PM_RSTC_WRCFG_FULL_RESET, wdt->base + PM_RSTC);
+
+ spin_unlock_irqrestore(&wdt->lock, flags);
+
+ return 0;
+}
+
+static int bcm2835_wdt_stop(struct watchdog_device *wdog)
+{
+ struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ writel_relaxed(PM_PASSWORD | PM_RSTC_RESET, wdt->base + PM_RSTC);
+ dev_info(wdog->dev, "Watchdog timer stopped");
+ return 0;
+}
+
+static int bcm2835_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
+{
+ wdog->timeout = t;
+ return 0;
+}
+
+static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog)
+{
+ struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
+
+ uint32_t ret = readl_relaxed(wdt->base + PM_WDOG);
+ return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
+}
+
+static struct watchdog_ops bcm2835_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = bcm2835_wdt_start,
+ .stop = bcm2835_wdt_stop,
+ .set_timeout = bcm2835_wdt_set_timeout,
+ .get_timeleft = bcm2835_wdt_get_timeleft,
+};
+
+static struct watchdog_info bcm2835_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Broadcom BCM2835 Watchdog timer",
+};
+
+static struct watchdog_device bcm2835_wdt_wdd = {
+ .info = &bcm2835_wdt_info,
+ .ops = &bcm2835_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
+ .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
+};
+
+static int bcm2835_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct bcm2835_wdt *wdt;
+ int err;
+
+ wdt = devm_kzalloc(dev, sizeof(struct bcm2835_wdt), GFP_KERNEL);
+ if (!wdt) {
+ dev_err(dev, "Failed to allocate memory for watchdog device");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, wdt);
+
+ spin_lock_init(&wdt->lock);
+
+ wdt->base = of_iomap(np, 0);
+ if (!wdt->base) {
+ dev_err(dev, "Failed to remap watchdog regs");
+ return -ENODEV;
+ }
+
+ watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
+ watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
+ watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
+ err = watchdog_register_device(&bcm2835_wdt_wdd);
+ if (err) {
+ dev_err(dev, "Failed to register watchdog device");
+ iounmap(wdt->base);
+ return err;
+ }
+
+ dev_info(dev, "Broadcom BCM2835 watchdog timer");
+ return 0;
+}
+
+static int bcm2835_wdt_remove(struct platform_device *pdev)
+{
+ struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);
+
+ watchdog_unregister_device(&bcm2835_wdt_wdd);
+ iounmap(wdt->base);
+
+ return 0;
+}
+
+static void bcm2835_wdt_shutdown(struct platform_device *pdev)
+{
+ bcm2835_wdt_stop(&bcm2835_wdt_wdd);
+}
+
+static const struct of_device_id bcm2835_wdt_of_match[] = {
+ { .compatible = "brcm,bcm2835-pm-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match);
+
+static struct platform_driver bcm2835_wdt_driver = {
+ .probe = bcm2835_wdt_probe,
+ .remove = bcm2835_wdt_remove,
+ .shutdown = bcm2835_wdt_shutdown,
+ .driver = {
+ .name = "bcm2835-wdt",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_wdt_of_match,
+ },
+};
+module_platform_driver(bcm2835_wdt_driver);
+
+module_param(heartbeat, uint, 0);
+MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("Driver for Broadcom BCM2835 watchdog timer");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index b2b80d4..a14a58d 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -16,6 +16,7 @@
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -249,7 +250,8 @@
return -ENODEV;
}
- bcm63xx_wdt_device.regs = ioremap_nocache(r->start, resource_size(r));
+ bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
+ resource_size(r));
if (!bcm63xx_wdt_device.regs) {
dev_err(&pdev->dev, "failed to remap I/O resources\n");
return -ENXIO;
@@ -258,7 +260,7 @@
ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register wdt timer isr\n");
- goto unmap;
+ return ret;
}
if (bcm63xx_wdt_settimeout(wdt_time)) {
@@ -281,8 +283,6 @@
unregister_timer:
bcm63xx_timer_unregister(TIMER_WDT_ID);
-unmap:
- iounmap(bcm63xx_wdt_device.regs);
return ret;
}
@@ -293,7 +293,6 @@
misc_deregister(&bcm63xx_wdt_miscdev);
bcm63xx_timer_unregister(TIMER_WDT_ID);
- iounmap(bcm63xx_wdt_device.regs);
return 0;
}
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
index 7038758..213225e 100644
--- a/drivers/watchdog/cpwd.c
+++ b/drivers/watchdog/cpwd.c
@@ -621,7 +621,7 @@
WD_BADMODEL);
}
- dev_set_drvdata(&op->dev, p);
+ platform_set_drvdata(op, p);
cpwd_device = p;
err = 0;
@@ -642,7 +642,7 @@
static int cpwd_remove(struct platform_device *op)
{
- struct cpwd *p = dev_get_drvdata(&op->dev);
+ struct cpwd *p = platform_get_drvdata(op);
int i;
for (i = 0; i < WD_NUMDEVS; i++) {
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
index 3674450..f09c54e 100644
--- a/drivers/watchdog/da9052_wdt.c
+++ b/drivers/watchdog/da9052_wdt.c
@@ -215,14 +215,14 @@
goto err;
}
- dev_set_drvdata(&pdev->dev, driver_data);
+ platform_set_drvdata(pdev, driver_data);
err:
return ret;
}
static int da9052_wdt_remove(struct platform_device *pdev)
{
- struct da9052_wdt_data *driver_data = dev_get_drvdata(&pdev->dev);
+ struct da9052_wdt_data *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
kref_put(&driver_data->kref, da9052_wdt_release_resources);
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
index f5ad105..575f37a 100644
--- a/drivers/watchdog/da9055_wdt.c
+++ b/drivers/watchdog/da9055_wdt.c
@@ -174,7 +174,7 @@
goto err;
}
- dev_set_drvdata(&pdev->dev, driver_data);
+ platform_set_drvdata(pdev, driver_data);
ret = watchdog_register_device(&driver_data->wdt);
if (ret != 0)
@@ -187,7 +187,7 @@
static int da9055_wdt_remove(struct platform_device *pdev)
{
- struct da9055_wdt_data *driver_data = dev_get_drvdata(&pdev->dev);
+ struct da9055_wdt_data *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
kref_put(&driver_data->kref, da9055_wdt_release_resources);
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c
index 2037669..e621098 100644
--- a/drivers/watchdog/dw_wdt.c
+++ b/drivers/watchdog/dw_wdt.c
@@ -154,8 +154,8 @@
return nonseekable_open(inode, filp);
}
-ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len,
- loff_t *offset)
+static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
+ size_t len, loff_t *offset)
{
if (!len)
return 0;
@@ -305,13 +305,13 @@
if (IS_ERR(dw_wdt.regs))
return PTR_ERR(dw_wdt.regs);
- dw_wdt.clk = clk_get(&pdev->dev, NULL);
+ dw_wdt.clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dw_wdt.clk))
return PTR_ERR(dw_wdt.clk);
ret = clk_enable(dw_wdt.clk);
if (ret)
- goto out_put_clk;
+ return ret;
spin_lock_init(&dw_wdt.lock);
@@ -327,8 +327,6 @@
out_disable_clk:
clk_disable(dw_wdt.clk);
-out_put_clk:
- clk_put(dw_wdt.clk);
return ret;
}
@@ -338,7 +336,6 @@
misc_deregister(&dw_wdt_miscdev);
clk_disable(dw_wdt.clk);
- clk_put(dw_wdt.clk);
return 0;
}
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 11796b9..de7e4f4 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -39,7 +39,7 @@
#endif /* CONFIG_HPWDT_NMI_DECODING */
#include <asm/nmi.h>
-#define HPWDT_VERSION "1.3.1"
+#define HPWDT_VERSION "1.3.2"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
@@ -148,6 +148,7 @@
static unsigned int hpwdt_nmi_decoding;
static unsigned int allow_kdump = 1;
static unsigned int is_icru;
+static unsigned int is_uefi;
static DEFINE_SPINLOCK(rom_lock);
static void *cru_rom_addr;
static struct cmn_registers cmn_regs;
@@ -484,7 +485,7 @@
goto out;
spin_lock_irqsave(&rom_lock, rom_pl);
- if (!die_nmi_called && !is_icru)
+ if (!die_nmi_called && !is_icru && !is_uefi)
asminline_call(&cmn_regs, cru_rom_addr);
die_nmi_called = 1;
spin_unlock_irqrestore(&rom_lock, rom_pl);
@@ -492,7 +493,7 @@
if (allow_kdump)
hpwdt_stop();
- if (!is_icru) {
+ if (!is_icru && !is_uefi) {
if (cmn_regs.u1.ral == 0) {
panic("An NMI occurred, "
"but unable to determine source.\n");
@@ -679,6 +680,8 @@
smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
if (smbios_proliant_ptr->misc_features & 0x01)
is_icru = 1;
+ if (smbios_proliant_ptr->misc_features & 0x408)
+ is_uefi = 1;
}
}
@@ -697,7 +700,7 @@
* the old cru detect code.
*/
dmi_walk(dmi_find_icru, NULL);
- if (!is_icru) {
+ if (!is_icru && !is_uefi) {
/*
* We need to map the ROM to get the CRU service.
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 62946c2..693ac3f 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -261,7 +261,7 @@
if (IS_ERR(imx2_wdt.base))
return PTR_ERR(imx2_wdt.base);
- imx2_wdt.clk = clk_get(&pdev->dev, NULL);
+ imx2_wdt.clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(imx2_wdt.clk)) {
dev_err(&pdev->dev, "can't get Watchdog clock\n");
return PTR_ERR(imx2_wdt.clk);
@@ -286,7 +286,6 @@
fail:
imx2_wdt_miscdev.parent = NULL;
- clk_put(imx2_wdt.clk);
return ret;
}
@@ -299,8 +298,7 @@
dev_crit(imx2_wdt_miscdev.parent,
"Device removed: Expect reboot!\n");
- } else
- clk_put(imx2_wdt.clk);
+ }
imx2_wdt_miscdev.parent = NULL;
return 0;
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 1cb25f6..d1afdf6 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -177,7 +177,7 @@
goto err_out;
}
- drvdata->rtc_clk = clk_get(NULL, "rtc");
+ drvdata->rtc_clk = clk_get(&pdev->dev, "rtc");
if (IS_ERR(drvdata->rtc_clk)) {
dev_err(&pdev->dev, "cannot find RTC clock\n");
ret = PTR_ERR(drvdata->rtc_clk);
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
new file mode 100644
index 0000000..96dbba9
--- /dev/null
+++ b/drivers/watchdog/mena21_wdt.c
@@ -0,0 +1,270 @@
+/*
+ * Watchdog driver for the A21 VME CPU Boards
+ *
+ * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
+ *
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define NUM_GPIOS 6
+
+enum a21_wdt_gpios {
+ GPIO_WD_ENAB,
+ GPIO_WD_FAST,
+ GPIO_WD_TRIG,
+ GPIO_WD_RST0,
+ GPIO_WD_RST1,
+ GPIO_WD_RST2,
+};
+
+struct a21_wdt_drv {
+ struct watchdog_device wdt;
+ struct mutex lock;
+ unsigned gpios[NUM_GPIOS];
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
+{
+ int reset = 0;
+
+ reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
+ reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
+ reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
+
+ return reset;
+}
+
+static int a21_wdt_start(struct watchdog_device *wdt)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ mutex_lock(&drv->lock);
+
+ gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int a21_wdt_stop(struct watchdog_device *wdt)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ mutex_lock(&drv->lock);
+
+ gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int a21_wdt_ping(struct watchdog_device *wdt)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ mutex_lock(&drv->lock);
+
+ gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
+ ndelay(10);
+ gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int a21_wdt_set_timeout(struct watchdog_device *wdt,
+ unsigned int timeout)
+{
+ struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
+
+ if (timeout != 1 && timeout != 30) {
+ dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
+ return -EINVAL;
+ }
+
+ if (timeout == 30 && wdt->timeout == 1) {
+ dev_err(wdt->dev,
+ "Transition from fast to slow mode not allowed\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&drv->lock);
+
+ if (timeout == 1)
+ gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
+ else
+ gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
+
+ wdt->timeout = timeout;
+
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static const struct watchdog_info a21_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .identity = "MEN A21 Watchdog",
+};
+
+static const struct watchdog_ops a21_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = a21_wdt_start,
+ .stop = a21_wdt_stop,
+ .ping = a21_wdt_ping,
+ .set_timeout = a21_wdt_set_timeout,
+};
+
+static struct watchdog_device a21_wdt = {
+ .info = &a21_wdt_info,
+ .ops = &a21_wdt_ops,
+ .min_timeout = 1,
+ .max_timeout = 30,
+};
+
+static int a21_wdt_probe(struct platform_device *pdev)
+{
+ struct device_node *node;
+ struct a21_wdt_drv *drv;
+ unsigned int reset = 0;
+ int num_gpios;
+ int ret;
+ int i;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ /* Fill GPIO pin array */
+ node = pdev->dev.of_node;
+
+ num_gpios = of_gpio_count(node);
+ if (num_gpios != NUM_GPIOS) {
+ dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
+ num_gpios, NUM_GPIOS);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < num_gpios; i++) {
+ int val;
+
+ val = of_get_gpio(node, i);
+ if (val < 0)
+ return val;
+
+ drv->gpios[i] = val;
+ }
+
+ /* Request the used GPIOs */
+ for (i = 0; i < num_gpios; i++) {
+ ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
+ "MEN A21 Watchdog");
+ if (ret)
+ return ret;
+
+ if (i < GPIO_WD_RST0)
+ ret = gpio_direction_output(drv->gpios[i],
+ gpio_get_value(drv->gpios[i]));
+ else /* GPIO_WD_RST[0..2] are inputs */
+ ret = gpio_direction_input(drv->gpios[i]);
+ if (ret)
+ return ret;
+ }
+
+ mutex_init(&drv->lock);
+ watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
+ watchdog_set_nowayout(&a21_wdt, nowayout);
+ watchdog_set_drvdata(&a21_wdt, drv);
+
+ reset = a21_wdt_get_bootstatus(drv);
+ if (reset == 2)
+ a21_wdt.bootstatus |= WDIOF_EXTERN1;
+ else if (reset == 4)
+ a21_wdt.bootstatus |= WDIOF_CARDRESET;
+ else if (reset == 5)
+ a21_wdt.bootstatus |= WDIOF_POWERUNDER;
+ else if (reset == 7)
+ a21_wdt.bootstatus |= WDIOF_EXTERN2;
+
+ ret = watchdog_register_device(&a21_wdt);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot register watchdog device\n");
+ goto err_register_wd;
+ }
+
+ dev_set_drvdata(&pdev->dev, drv);
+
+ dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
+
+ return 0;
+
+err_register_wd:
+ mutex_destroy(&drv->lock);
+
+ return ret;
+}
+
+static int a21_wdt_remove(struct platform_device *pdev)
+{
+ struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+ dev_warn(&pdev->dev,
+ "Unregistering A21 watchdog driver, board may reboot\n");
+
+ watchdog_unregister_device(&drv->wdt);
+
+ mutex_destroy(&drv->lock);
+
+ return 0;
+}
+
+static void a21_wdt_shutdown(struct platform_device *pdev)
+{
+ struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
+
+ gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
+}
+
+static const struct of_device_id a21_wdt_ids[] = {
+ { .compatible = "men,a021-wdt" },
+ { },
+};
+
+static struct platform_driver a21_wdt_driver = {
+ .probe = a21_wdt_probe,
+ .remove = a21_wdt_remove,
+ .shutdown = a21_wdt_shutdown,
+ .driver = {
+ .name = "a21-watchdog",
+ .of_match_table = a21_wdt_ids,
+ },
+};
+
+module_platform_driver(a21_wdt_driver);
+
+MODULE_AUTHOR("MEN Mikro Elektronik");
+MODULE_DESCRIPTION("MEN A21 Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:a21-watchdog");
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
deleted file mode 100644
index 233cfad..0000000
--- a/drivers/watchdog/mpcore_wdt.c
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Watchdog driver for the mpcore watchdog timer
- *
- * (c) Copyright 2004 ARM Limited
- *
- * Based on the SoftDog driver:
- * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
- * 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
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
- *
- * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-
-#include <asm/smp_twd.h>
-
-struct mpcore_wdt {
- unsigned long timer_alive;
- struct device *dev;
- void __iomem *base;
- int irq;
- unsigned int perturb;
- char expect_close;
-};
-
-static struct platform_device *mpcore_wdt_pdev;
-static DEFINE_SPINLOCK(wdt_lock);
-
-#define TIMER_MARGIN 60
-static int mpcore_margin = TIMER_MARGIN;
-module_param(mpcore_margin, int, 0);
-MODULE_PARM_DESC(mpcore_margin,
- "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
- __MODULE_STRING(TIMER_MARGIN) ")");
-
-static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
- "Watchdog cannot be stopped once started (default="
- __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-
-#define ONLY_TESTING 0
-static int mpcore_noboot = ONLY_TESTING;
-module_param(mpcore_noboot, int, 0);
-MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
- "set to 1 to ignore reboots, 0 to reboot (default="
- __MODULE_STRING(ONLY_TESTING) ")");
-
-/*
- * This is the interrupt handler. Note that we only use this
- * in testing mode, so don't actually do a reboot here.
- */
-static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
-{
- struct mpcore_wdt *wdt = arg;
-
- /* Check it really was our interrupt */
- if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
- dev_crit(wdt->dev, "Triggered - Reboot ignored\n");
- /* Clear the interrupt on the watchdog */
- writel(1, wdt->base + TWD_WDOG_INTSTAT);
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
-}
-
-/*
- * mpcore_wdt_keepalive - reload the timer
- *
- * Note that the spec says a DIFFERENT value must be written to the reload
- * register each time. The "perturb" variable deals with this by adding 1
- * to the count every other time the function is called.
- */
-static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
-{
- unsigned long count;
-
- spin_lock(&wdt_lock);
- /* Assume prescale is set to 256 */
- count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
- count = (0xFFFFFFFFU - count) * (HZ / 5);
- count = (count / 256) * mpcore_margin;
-
- /* Reload the counter */
- writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
- wdt->perturb = wdt->perturb ? 0 : 1;
- spin_unlock(&wdt_lock);
-}
-
-static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
-{
- spin_lock(&wdt_lock);
- writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
- writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
- writel(0x0, wdt->base + TWD_WDOG_CONTROL);
- spin_unlock(&wdt_lock);
-}
-
-static void mpcore_wdt_start(struct mpcore_wdt *wdt)
-{
- dev_info(wdt->dev, "enabling watchdog\n");
-
- /* This loads the count register but does NOT start the count yet */
- mpcore_wdt_keepalive(wdt);
-
- if (mpcore_noboot) {
- /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
- writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
- } else {
- /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
- writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
- }
-}
-
-static int mpcore_wdt_set_heartbeat(int t)
-{
- if (t < 0x0001 || t > 0xFFFF)
- return -EINVAL;
-
- mpcore_margin = t;
- return 0;
-}
-
-/*
- * /dev/watchdog handling
- */
-static int mpcore_wdt_open(struct inode *inode, struct file *file)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
-
- if (test_and_set_bit(0, &wdt->timer_alive))
- return -EBUSY;
-
- if (nowayout)
- __module_get(THIS_MODULE);
-
- file->private_data = wdt;
-
- /*
- * Activate timer
- */
- mpcore_wdt_start(wdt);
-
- return nonseekable_open(inode, file);
-}
-
-static int mpcore_wdt_release(struct inode *inode, struct file *file)
-{
- struct mpcore_wdt *wdt = file->private_data;
-
- /*
- * Shut off the timer.
- * Lock it in if it's a module and we set nowayout
- */
- if (wdt->expect_close == 42)
- mpcore_wdt_stop(wdt);
- else {
- dev_crit(wdt->dev,
- "unexpected close, not stopping watchdog!\n");
- mpcore_wdt_keepalive(wdt);
- }
- clear_bit(0, &wdt->timer_alive);
- wdt->expect_close = 0;
- return 0;
-}
-
-static ssize_t mpcore_wdt_write(struct file *file, const char *data,
- size_t len, loff_t *ppos)
-{
- struct mpcore_wdt *wdt = file->private_data;
-
- /*
- * Refresh the timer.
- */
- if (len) {
- if (!nowayout) {
- size_t i;
-
- /* In case it was set long ago */
- wdt->expect_close = 0;
-
- for (i = 0; i != len; i++) {
- char c;
-
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- wdt->expect_close = 42;
- }
- }
- mpcore_wdt_keepalive(wdt);
- }
- return len;
-}
-
-static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
- WDIOF_KEEPALIVEPING |
- WDIOF_MAGICCLOSE,
- .identity = "MPcore Watchdog",
-};
-
-static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct mpcore_wdt *wdt = file->private_data;
- int ret;
- union {
- struct watchdog_info ident;
- int i;
- } uarg;
-
- if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
- return -ENOTTY;
-
- if (_IOC_DIR(cmd) & _IOC_WRITE) {
- ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
- if (ret)
- return -EFAULT;
- }
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- uarg.ident = ident;
- ret = 0;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- uarg.i = 0;
- ret = 0;
- break;
-
- case WDIOC_SETOPTIONS:
- ret = -EINVAL;
- if (uarg.i & WDIOS_DISABLECARD) {
- mpcore_wdt_stop(wdt);
- ret = 0;
- }
- if (uarg.i & WDIOS_ENABLECARD) {
- mpcore_wdt_start(wdt);
- ret = 0;
- }
- break;
-
- case WDIOC_KEEPALIVE:
- mpcore_wdt_keepalive(wdt);
- ret = 0;
- break;
-
- case WDIOC_SETTIMEOUT:
- ret = mpcore_wdt_set_heartbeat(uarg.i);
- if (ret)
- break;
-
- mpcore_wdt_keepalive(wdt);
- /* Fall */
- case WDIOC_GETTIMEOUT:
- uarg.i = mpcore_margin;
- ret = 0;
- break;
-
- default:
- return -ENOTTY;
- }
-
- if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
- ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
- if (ret)
- ret = -EFAULT;
- }
- return ret;
-}
-
-/*
- * System shutdown handler. Turn off the watchdog if we're
- * restarting or halting the system.
- */
-static void mpcore_wdt_shutdown(struct platform_device *pdev)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
-
- if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
- mpcore_wdt_stop(wdt);
-}
-
-/*
- * Kernel Interfaces
- */
-static const struct file_operations mpcore_wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = mpcore_wdt_write,
- .unlocked_ioctl = mpcore_wdt_ioctl,
- .open = mpcore_wdt_open,
- .release = mpcore_wdt_release,
-};
-
-static struct miscdevice mpcore_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &mpcore_wdt_fops,
-};
-
-static int mpcore_wdt_probe(struct platform_device *pdev)
-{
- struct mpcore_wdt *wdt;
- struct resource *res;
- int ret;
-
- /* We only accept one device, and it must have an id of -1 */
- if (pdev->id != -1)
- return -ENODEV;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
- if (!wdt)
- return -ENOMEM;
-
- wdt->dev = &pdev->dev;
- wdt->irq = platform_get_irq(pdev, 0);
- if (wdt->irq >= 0) {
- ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
- "mpcore_wdt", wdt);
- if (ret) {
- dev_err(wdt->dev,
- "cannot register IRQ%d for watchdog\n",
- wdt->irq);
- return ret;
- }
- }
-
- wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
- if (!wdt->base)
- return -ENOMEM;
-
- mpcore_wdt_miscdev.parent = &pdev->dev;
- ret = misc_register(&mpcore_wdt_miscdev);
- if (ret) {
- dev_err(wdt->dev,
- "cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- return ret;
- }
-
- mpcore_wdt_stop(wdt);
- platform_set_drvdata(pdev, wdt);
- mpcore_wdt_pdev = pdev;
-
- return 0;
-}
-
-static int mpcore_wdt_remove(struct platform_device *pdev)
-{
- platform_set_drvdata(pdev, NULL);
-
- misc_deregister(&mpcore_wdt_miscdev);
-
- mpcore_wdt_pdev = NULL;
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
- mpcore_wdt_stop(wdt); /* Turn the WDT off */
- return 0;
-}
-
-static int mpcore_wdt_resume(struct platform_device *pdev)
-{
- struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
- /* re-activate timer */
- if (test_bit(0, &wdt->timer_alive))
- mpcore_wdt_start(wdt);
- return 0;
-}
-#else
-#define mpcore_wdt_suspend NULL
-#define mpcore_wdt_resume NULL
-#endif
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:mpcore_wdt");
-
-static struct platform_driver mpcore_wdt_driver = {
- .probe = mpcore_wdt_probe,
- .remove = mpcore_wdt_remove,
- .suspend = mpcore_wdt_suspend,
- .resume = mpcore_wdt_resume,
- .shutdown = mpcore_wdt_shutdown,
- .driver = {
- .owner = THIS_MODULE,
- .name = "mpcore_wdt",
- },
-};
-
-static int __init mpcore_wdt_init(void)
-{
- /*
- * Check that the margin value is within it's range;
- * if not reset to the default
- */
- if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
- mpcore_wdt_set_heartbeat(TIMER_MARGIN);
- pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
- TIMER_MARGIN);
- }
-
- pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
- mpcore_noboot, mpcore_margin, nowayout);
-
- return platform_driver_register(&mpcore_wdt_driver);
-}
-
-static void __exit mpcore_wdt_exit(void)
-{
- platform_driver_unregister(&mpcore_wdt_driver);
-}
-
-module_init(mpcore_wdt_init);
-module_exit(mpcore_wdt_exit);
-
-MODULE_AUTHOR("ARM Limited");
-MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
index 14dab6f..b434111 100644
--- a/drivers/watchdog/mtx-1_wdt.c
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -209,7 +209,7 @@
int ret;
mtx1_wdt_device.gpio = pdev->resource[0].start;
- ret = gpio_request_one(mtx1_wdt_device.gpio,
+ ret = devm_gpio_request_one(&pdev->dev, mtx1_wdt_device.gpio,
GPIOF_OUT_INIT_HIGH, "mtx1-wdt");
if (ret < 0) {
dev_err(&pdev->dev, "failed to request gpio");
@@ -241,7 +241,6 @@
wait_for_completion(&mtx1_wdt_device.stop);
}
- gpio_free(mtx1_wdt_device.gpio);
misc_deregister(&mtx1_wdt_misc);
return 0;
}
diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c
index c7fb878..e4cf980 100644
--- a/drivers/watchdog/mv64x60_wdt.c
+++ b/drivers/watchdog/mv64x60_wdt.c
@@ -276,7 +276,7 @@
if (!r)
return -ENODEV;
- mv64x60_wdt_regs = ioremap(r->start, resource_size(r));
+ mv64x60_wdt_regs = devm_ioremap(&dev->dev, r->start, resource_size(r));
if (mv64x60_wdt_regs == NULL)
return -ENOMEM;
@@ -293,8 +293,6 @@
mv64x60_wdt_handler_disable();
- iounmap(mv64x60_wdt_regs);
-
return 0;
}
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
index 04c45a1..e2b6d2c 100644
--- a/drivers/watchdog/nuc900_wdt.c
+++ b/drivers/watchdog/nuc900_wdt.c
@@ -61,7 +61,6 @@
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct nuc900_wdt {
- struct resource *res;
struct clk *wdt_clock;
struct platform_device *pdev;
void __iomem *wdt_base;
@@ -244,9 +243,11 @@
static int nuc900wdt_probe(struct platform_device *pdev)
{
+ struct resource *res;
int ret = 0;
- nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
+ nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt),
+ GFP_KERNEL);
if (!nuc900_wdt)
return -ENOMEM;
@@ -254,33 +255,20 @@
spin_lock_init(&nuc900_wdt->wdt_lock);
- nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (nuc900_wdt->res == NULL) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
dev_err(&pdev->dev, "no memory resource specified\n");
- ret = -ENOENT;
- goto err_get;
+ return -ENOENT;
}
- if (!request_mem_region(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res), pdev->name)) {
- dev_err(&pdev->dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto err_get;
- }
+ nuc900_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nuc900_wdt->wdt_base))
+ return PTR_ERR(nuc900_wdt->wdt_base);
- nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res));
- if (nuc900_wdt->wdt_base == NULL) {
- dev_err(&pdev->dev, "failed to ioremap() region\n");
- ret = -EINVAL;
- goto err_req;
- }
-
- nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
+ nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_wdt->wdt_clock)) {
dev_err(&pdev->dev, "failed to find watchdog clock source\n");
- ret = PTR_ERR(nuc900_wdt->wdt_clock);
- goto err_map;
+ return PTR_ERR(nuc900_wdt->wdt_clock);
}
clk_enable(nuc900_wdt->wdt_clock);
@@ -298,14 +286,6 @@
err_clk:
clk_disable(nuc900_wdt->wdt_clock);
- clk_put(nuc900_wdt->wdt_clock);
-err_map:
- iounmap(nuc900_wdt->wdt_base);
-err_req:
- release_mem_region(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res));
-err_get:
- kfree(nuc900_wdt);
return ret;
}
@@ -314,14 +294,6 @@
misc_deregister(&nuc900wdt_miscdev);
clk_disable(nuc900_wdt->wdt_clock);
- clk_put(nuc900_wdt->wdt_clock);
-
- iounmap(nuc900_wdt->wdt_base);
-
- release_mem_region(nuc900_wdt->res->start,
- resource_size(nuc900_wdt->res));
-
- kfree(nuc900_wdt);
return 0;
}
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c
index 2761ddb..4dd281f 100644
--- a/drivers/watchdog/of_xilinx_wdt.c
+++ b/drivers/watchdog/of_xilinx_wdt.c
@@ -1,23 +1,13 @@
/*
-* of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt
-*
-* (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.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.
-*
-* -----------------------
-* 30-May-2011 Alejandro Cabrera <aldaya@gmail.com>
-* - If "xlnx,wdt-enable-once" wasn't found on device tree the
-* module will use CONFIG_WATCHDOG_NOWAYOUT
-* - If the device tree parameters ("clock-frequency" and
-* "xlnx,wdt-interval") wasn't found the driver won't
-* know the wdt reset interval
-*/
+ * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
+ *
+ * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.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.
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -394,6 +384,7 @@
/* Match table for of_platform binding */
static struct of_device_id xwdt_of_match[] = {
+ { .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
{},
};
@@ -413,5 +404,5 @@
MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
MODULE_DESCRIPTION("Xilinx Watchdog driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index da57798..4ea5fcc 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -38,6 +38,9 @@
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
+#define WDT_RESET_OUT_EN BIT(1)
+#define WDT_INT_REQ BIT(3)
+
static bool nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = -1; /* module parameter (seconds) */
static unsigned int wdt_max_duration; /* (seconds) */
@@ -67,9 +70,7 @@
writel(wdt_tclk * wdt_dev->timeout, wdt_reg + WDT_VAL);
/* Clear watchdog timer interrupt */
- reg = readl(BRIDGE_CAUSE);
- reg &= ~WDT_INT_REQ;
- writel(reg, BRIDGE_CAUSE);
+ writel(~WDT_INT_REQ, BRIDGE_CAUSE);
/* Enable watchdog timer */
reg = readl(wdt_reg + TIMER_CTRL);
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index a3684a3..b30bd43 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -159,13 +159,13 @@
if (IS_ERR(wdt_base))
return PTR_ERR(wdt_base);
- wdt_clk = clk_get(&pdev->dev, NULL);
+ wdt_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt_clk))
return PTR_ERR(wdt_clk);
ret = clk_enable(wdt_clk);
if (ret)
- goto out;
+ return ret;
pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
WDIOF_CARDRESET : 0;
@@ -186,8 +186,6 @@
disable_clk:
clk_disable(wdt_clk);
-out:
- clk_put(wdt_clk);
return ret;
}
@@ -196,7 +194,6 @@
watchdog_unregister_device(&pnx4008_wdd);
clk_disable(wdt_clk);
- clk_put(wdt_clk);
return 0;
}
diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c
index f78bc00..9cf6bc7 100644
--- a/drivers/watchdog/rc32434_wdt.c
+++ b/drivers/watchdog/rc32434_wdt.c
@@ -32,6 +32,7 @@
#include <linux/platform_device.h> /* For platform_driver framework */
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
+#include <linux/io.h> /* For devm_ioremap_nocache */
#include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */
@@ -271,7 +272,7 @@
return -ENODEV;
}
- wdt_reg = ioremap_nocache(r->start, resource_size(r));
+ wdt_reg = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
if (!wdt_reg) {
pr_err("failed to remap I/O resources\n");
return -ENXIO;
@@ -293,23 +294,18 @@
ret = misc_register(&rc32434_wdt_miscdev);
if (ret < 0) {
pr_err("failed to register watchdog device\n");
- goto unmap;
+ return ret;
}
pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n",
timeout);
return 0;
-
-unmap:
- iounmap(wdt_reg);
- return ret;
}
static int rc32434_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&rc32434_wdt_miscdev);
- iounmap(wdt_reg);
return 0;
}
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
index 0040451..3dd8ed2 100644
--- a/drivers/watchdog/riowd.c
+++ b/drivers/watchdog/riowd.c
@@ -183,7 +183,7 @@
goto out;
err = -ENOMEM;
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
if (!p)
goto out;
@@ -192,7 +192,7 @@
p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
if (!p->regs) {
pr_err("Cannot map registers\n");
- goto out_free;
+ goto out;
}
/* Make miscdev useable right away */
riowd_device = p;
@@ -206,27 +206,23 @@
pr_info("Hardware watchdog [%i minutes], regs at %p\n",
riowd_timeout, p->regs);
- dev_set_drvdata(&op->dev, p);
+ platform_set_drvdata(op, p);
return 0;
out_iounmap:
riowd_device = NULL;
of_iounmap(&op->resource[0], p->regs, 2);
-out_free:
- kfree(p);
-
out:
return err;
}
static int riowd_remove(struct platform_device *op)
{
- struct riowd *p = dev_get_drvdata(&op->dev);
+ struct riowd *p = platform_get_drvdata(op);
misc_deregister(&riowd_miscdev);
of_iounmap(&op->resource[0], p->regs, 2);
- kfree(p);
return 0;
}
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 3a9f696..6a22cf5 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -358,7 +358,7 @@
ret = s3c2410wdt_cpufreq_register();
if (ret < 0) {
- pr_err("failed to register cpufreq\n");
+ dev_err(dev, "failed to register cpufreq\n");
goto err_clk;
}
@@ -448,12 +448,12 @@
s3c2410wdt_stop(&s3c2410_wdd);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static unsigned long wtcon_save;
static unsigned long wtdat_save;
-static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
+static int s3c2410wdt_suspend(struct device *dev)
{
/* Save watchdog state, and turn it off. */
wtcon_save = readl(wdt_base + S3C2410_WTCON);
@@ -465,7 +465,7 @@
return 0;
}
-static int s3c2410wdt_resume(struct platform_device *dev)
+static int s3c2410wdt_resume(struct device *dev)
{
/* Restore watchdog state. */
@@ -473,16 +473,15 @@
writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */
writel(wtcon_save, wdt_base + S3C2410_WTCON);
- pr_info("watchdog %sabled\n",
+ dev_info(dev, "watchdog %sabled\n",
(wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
return 0;
}
+#endif
-#else
-#define s3c2410wdt_suspend NULL
-#define s3c2410wdt_resume NULL
-#endif /* CONFIG_PM */
+static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend,
+ s3c2410wdt_resume);
#ifdef CONFIG_OF
static const struct of_device_id s3c2410_wdt_match[] = {
@@ -496,11 +495,10 @@
.probe = s3c2410wdt_probe,
.remove = s3c2410wdt_remove,
.shutdown = s3c2410wdt_shutdown,
- .suspend = s3c2410wdt_suspend,
- .resume = s3c2410wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",
+ .pm = &s3c2410wdt_pm_ops,
.of_match_table = of_match_ptr(s3c2410_wdt_match),
},
};
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
index 25c7a3f..ea5d84a 100644
--- a/drivers/watchdog/sb_wdog.c
+++ b/drivers/watchdog/sb_wdog.c
@@ -208,7 +208,7 @@
* get the remaining count from the ... count register
* which is 1*8 before the config register
*/
- ret = put_user(__raw_readq(user_dog - 8) / 1000000, p);
+ ret = put_user((u32)__raw_readq(user_dog - 8) / 1000000, p);
break;
}
return ret;
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 6185af2b..5bca794 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -241,7 +241,7 @@
wdt->dev = &pdev->dev;
- wdt->clk = clk_get(&pdev->dev, NULL);
+ wdt->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(wdt->clk)) {
/*
* Clock framework support is optional, continue on
@@ -251,10 +251,8 @@
}
wdt->base = devm_ioremap_resource(wdt->dev, res);
- if (IS_ERR(wdt->base)) {
- rc = PTR_ERR(wdt->base);
- goto err;
- }
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
watchdog_set_nowayout(&sh_wdt_dev, nowayout);
watchdog_set_drvdata(&sh_wdt_dev, wdt);
@@ -277,7 +275,7 @@
rc = watchdog_register_device(&sh_wdt_dev);
if (unlikely(rc)) {
dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc);
- goto err;
+ return rc;
}
init_timer(&wdt->timer);
@@ -292,23 +290,15 @@
pm_runtime_enable(&pdev->dev);
return 0;
-
-err:
- clk_put(wdt->clk);
-
- return rc;
}
static int sh_wdt_remove(struct platform_device *pdev)
{
struct sh_wdt *wdt = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
-
watchdog_unregister_device(&sh_wdt_dev);
pm_runtime_disable(&pdev->dev);
- clk_put(wdt->clk);
return 0;
}
diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c
index fe83beb8..b68b1e5 100644
--- a/drivers/watchdog/softdog.c
+++ b/drivers/watchdog/softdog.c
@@ -152,7 +152,6 @@
.owner = THIS_MODULE,
.start = softdog_ping,
.stop = softdog_stop,
- .ping = softdog_ping,
.set_timeout = softdog_set_timeout,
};
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 8872642..58df98a 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -231,7 +231,7 @@
goto err;
}
- wdt->clk = clk_get(&adev->dev, NULL);
+ wdt->clk = devm_clk_get(&adev->dev, NULL);
if (IS_ERR(wdt->clk)) {
dev_warn(&adev->dev, "Clock not found\n");
ret = PTR_ERR(wdt->clk);
@@ -251,15 +251,13 @@
if (ret) {
dev_err(&adev->dev, "watchdog_register_device() failed: %d\n",
ret);
- goto err_register;
+ goto err;
}
amba_set_drvdata(adev, wdt);
dev_info(&adev->dev, "registration successful\n");
return 0;
-err_register:
- clk_put(wdt->clk);
err:
dev_err(&adev->dev, "Probe Failed!!!\n");
return ret;
@@ -272,7 +270,6 @@
watchdog_unregister_device(&wdt->wdd);
amba_set_drvdata(adev, NULL);
watchdog_set_drvdata(&wdt->wdd, NULL);
- clk_put(wdt->clk);
return 0;
}
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c
index b8a9245..4da59b4 100644
--- a/drivers/watchdog/ts72xx_wdt.c
+++ b/drivers/watchdog/ts72xx_wdt.c
@@ -396,7 +396,7 @@
struct resource *r1, *r2;
int error = 0;
- wdt = kzalloc(sizeof(struct ts72xx_wdt), GFP_KERNEL);
+ wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL);
if (!wdt) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
@@ -405,44 +405,22 @@
r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r1) {
dev_err(&pdev->dev, "failed to get memory resource\n");
- error = -ENODEV;
- goto fail;
+ return -ENODEV;
}
- r1 = request_mem_region(r1->start, resource_size(r1), pdev->name);
- if (!r1) {
- dev_err(&pdev->dev, "cannot request memory region\n");
- error = -EBUSY;
- goto fail;
- }
-
- wdt->control_reg = ioremap(r1->start, resource_size(r1));
- if (!wdt->control_reg) {
- dev_err(&pdev->dev, "failed to map memory\n");
- error = -ENODEV;
- goto fail_free_control;
- }
+ wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1);
+ if (IS_ERR(wdt->control_reg))
+ return PTR_ERR(wdt->control_reg);
r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!r2) {
dev_err(&pdev->dev, "failed to get memory resource\n");
- error = -ENODEV;
- goto fail_unmap_control;
+ return -ENODEV;
}
- r2 = request_mem_region(r2->start, resource_size(r2), pdev->name);
- if (!r2) {
- dev_err(&pdev->dev, "cannot request memory region\n");
- error = -EBUSY;
- goto fail_unmap_control;
- }
-
- wdt->feed_reg = ioremap(r2->start, resource_size(r2));
- if (!wdt->feed_reg) {
- dev_err(&pdev->dev, "failed to map memory\n");
- error = -ENODEV;
- goto fail_free_feed;
- }
+ wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2);
+ if (IS_ERR(wdt->feed_reg))
+ return PTR_ERR(wdt->feed_reg);
platform_set_drvdata(pdev, wdt);
ts72xx_wdt_pdev = pdev;
@@ -455,45 +433,20 @@
error = misc_register(&ts72xx_wdt_miscdev);
if (error) {
dev_err(&pdev->dev, "failed to register miscdev\n");
- goto fail_unmap_feed;
+ return error;
}
dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
return 0;
-
-fail_unmap_feed:
- platform_set_drvdata(pdev, NULL);
- iounmap(wdt->feed_reg);
-fail_free_feed:
- release_mem_region(r2->start, resource_size(r2));
-fail_unmap_control:
- iounmap(wdt->control_reg);
-fail_free_control:
- release_mem_region(r1->start, resource_size(r1));
-fail:
- kfree(wdt);
- return error;
}
static int ts72xx_wdt_remove(struct platform_device *pdev)
{
- struct ts72xx_wdt *wdt = platform_get_drvdata(pdev);
- struct resource *res;
int error;
error = misc_deregister(&ts72xx_wdt_miscdev);
- platform_set_drvdata(pdev, NULL);
- iounmap(wdt->feed_reg);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- release_mem_region(res->start, resource_size(res));
-
- iounmap(wdt->control_reg);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- kfree(wdt);
return error;
}
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index 0f03106..2d4535d 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -90,10 +90,8 @@
twl4030_wdt_stop(wdt);
ret = watchdog_register_device(wdt);
- if (ret) {
- platform_set_drvdata(pdev, NULL);
+ if (ret)
return ret;
- }
return 0;
}
@@ -103,7 +101,6 @@
struct watchdog_device *wdt = platform_get_drvdata(pdev);
watchdog_unregister_device(wdt);
- platform_set_drvdata(pdev, NULL);
return 0;
}
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index faf4e18..6aaefba 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -469,8 +469,10 @@
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
* watchdog_stop will fail.
*/
- if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
- !(wdd->info->options & WDIOF_MAGICCLOSE))
+ if (!test_bit(WDOG_ACTIVE, &wdd->status))
+ err = 0;
+ else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
+ !(wdd->info->options & WDIOF_MAGICCLOSE))
err = watchdog_stop(wdd);
/* If the watchdog was not stopped, send a keepalive ping */
diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
index 0a77655..3045deb 100644
--- a/drivers/watchdog/wdrtas.c
+++ b/drivers/watchdog/wdrtas.c
@@ -162,31 +162,6 @@
}
/**
- * wdrtas_log_scanned_event - logs an event we received during keepalive
- *
- * wdrtas_log_scanned_event prints a message to the log buffer dumping
- * the results of the last event-scan call
- */
-static void wdrtas_log_scanned_event(void)
-{
- int i;
-
- for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
- pr_info("dumping event (line %i/%i), data = "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
- wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
- wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
- wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
- wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
- wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
- wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
- wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
- wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
-}
-
-/**
* wdrtas_timer_keepalive - resets watchdog timer to keep system alive
*
* wdrtas_timer_keepalive restarts the watchdog timer by calling the
@@ -205,7 +180,9 @@
if (result < 0)
pr_err("event-scan failed: %li\n", result);
if (result == 0)
- wdrtas_log_scanned_event();
+ print_hex_dump(KERN_INFO, "dumping event, data: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ wdrtas_logbuffer, WDRTAS_LOGBUFFER_LEN, false);
} while (result == 0);
}
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index 9dcb6d0..d4e47ed 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -247,9 +247,10 @@
reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
if (pdata->update_gpio) {
- ret = gpio_request_one(pdata->update_gpio,
- GPIOF_DIR_OUT | GPIOF_INIT_LOW,
- "Watchdog update");
+ ret = devm_gpio_request_one(&pdev->dev,
+ pdata->update_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "Watchdog update");
if (ret < 0) {
dev_err(wm831x->dev,
"Failed to request update GPIO: %d\n",
@@ -270,7 +271,7 @@
} else {
dev_err(wm831x->dev,
"Failed to unlock security key: %d\n", ret);
- goto err_gpio;
+ goto err;
}
}
@@ -278,29 +279,23 @@
if (ret != 0) {
dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n",
ret);
- goto err_gpio;
+ goto err;
}
- dev_set_drvdata(&pdev->dev, driver_data);
+ platform_set_drvdata(pdev, driver_data);
return 0;
-err_gpio:
- if (driver_data->update_gpio)
- gpio_free(driver_data->update_gpio);
err:
return ret;
}
static int wm831x_wdt_remove(struct platform_device *pdev)
{
- struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev);
+ struct wm831x_wdt_drvdata *driver_data = platform_get_drvdata(pdev);
watchdog_unregister_device(&driver_data->wdt);
- if (driver_data->update_gpio)
- gpio_free(driver_data->update_gpio);
-
return 0;
}
diff --git a/fs/9p/Kconfig b/fs/9p/Kconfig
index 55abfd6..6489e1f 100644
--- a/fs/9p/Kconfig
+++ b/fs/9p/Kconfig
@@ -31,3 +31,16 @@
If you don't know what Access Control Lists are, say N
endif
+
+
+config 9P_FS_SECURITY
+ bool "9P Security Labels"
+ depends on 9P_FS
+ help
+ Security labels support alternative access control models
+ implemented by security modules like SELinux. This option
+ enables an extended attribute handler for file security
+ labels in the 9P filesystem.
+
+ If you are not using a security module that requires using
+ extended attributes for file security labels, say N.
diff --git a/fs/9p/Makefile b/fs/9p/Makefile
index ab8c127..ff7be98 100644
--- a/fs/9p/Makefile
+++ b/fs/9p/Makefile
@@ -11,7 +11,9 @@
v9fs.o \
fid.o \
xattr.o \
- xattr_user.o
+ xattr_user.o \
+ xattr_trusted.o
9p-$(CONFIG_9P_FSCACHE) += cache.o
9p-$(CONFIG_9P_FS_POSIX_ACL) += acl.o
+9p-$(CONFIG_9P_FS_SECURITY) += xattr_security.o
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index d86edc8..25b018e 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1054,13 +1054,11 @@
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
- int err;
struct v9fs_session_info *v9ses;
struct p9_fid *fid;
struct p9_wstat *st;
p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
- err = -EPERM;
v9ses = v9fs_dentry2v9ses(dentry);
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
generic_fillattr(dentry->d_inode, stat);
diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c
index c45e016..3c28cdf 100644
--- a/fs/9p/xattr.c
+++ b/fs/9p/xattr.c
@@ -167,9 +167,13 @@
const struct xattr_handler *v9fs_xattr_handlers[] = {
&v9fs_xattr_user_handler,
+ &v9fs_xattr_trusted_handler,
#ifdef CONFIG_9P_FS_POSIX_ACL
&v9fs_xattr_acl_access_handler,
&v9fs_xattr_acl_default_handler,
#endif
+#ifdef CONFIG_9P_FS_SECURITY
+ &v9fs_xattr_security_handler,
+#endif
NULL
};
diff --git a/fs/9p/xattr.h b/fs/9p/xattr.h
index eec348a..d3e2ea3 100644
--- a/fs/9p/xattr.h
+++ b/fs/9p/xattr.h
@@ -20,6 +20,8 @@
extern const struct xattr_handler *v9fs_xattr_handlers[];
extern struct xattr_handler v9fs_xattr_user_handler;
+extern struct xattr_handler v9fs_xattr_trusted_handler;
+extern struct xattr_handler v9fs_xattr_security_handler;
extern const struct xattr_handler v9fs_xattr_acl_access_handler;
extern const struct xattr_handler v9fs_xattr_acl_default_handler;
diff --git a/fs/9p/xattr_security.c b/fs/9p/xattr_security.c
new file mode 100644
index 0000000..cb247a1
--- /dev/null
+++ b/fs/9p/xattr_security.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include "xattr.h"
+
+static int v9fs_xattr_security_get(struct dentry *dentry, const char *name,
+ void *buffer, size_t size, int type)
+{
+ int retval;
+ char *full_name;
+ size_t name_len;
+ size_t prefix_len = XATTR_SECURITY_PREFIX_LEN;
+
+ if (name == NULL)
+ return -EINVAL;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+
+ name_len = strlen(name);
+ full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
+ if (!full_name)
+ return -ENOMEM;
+ memcpy(full_name, XATTR_SECURITY_PREFIX, prefix_len);
+ memcpy(full_name+prefix_len, name, name_len);
+ full_name[prefix_len + name_len] = '\0';
+
+ retval = v9fs_xattr_get(dentry, full_name, buffer, size);
+ kfree(full_name);
+ return retval;
+}
+
+static int v9fs_xattr_security_set(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags, int type)
+{
+ int retval;
+ char *full_name;
+ size_t name_len;
+ size_t prefix_len = XATTR_SECURITY_PREFIX_LEN;
+
+ if (name == NULL)
+ return -EINVAL;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+
+ name_len = strlen(name);
+ full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
+ if (!full_name)
+ return -ENOMEM;
+ memcpy(full_name, XATTR_SECURITY_PREFIX, prefix_len);
+ memcpy(full_name + prefix_len, name, name_len);
+ full_name[prefix_len + name_len] = '\0';
+
+ retval = v9fs_xattr_set(dentry, full_name, value, size, flags);
+ kfree(full_name);
+ return retval;
+}
+
+struct xattr_handler v9fs_xattr_security_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .get = v9fs_xattr_security_get,
+ .set = v9fs_xattr_security_set,
+};
diff --git a/fs/9p/xattr_trusted.c b/fs/9p/xattr_trusted.c
new file mode 100644
index 0000000..e30d33b
--- /dev/null
+++ b/fs/9p/xattr_trusted.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include "xattr.h"
+
+static int v9fs_xattr_trusted_get(struct dentry *dentry, const char *name,
+ void *buffer, size_t size, int type)
+{
+ int retval;
+ char *full_name;
+ size_t name_len;
+ size_t prefix_len = XATTR_TRUSTED_PREFIX_LEN;
+
+ if (name == NULL)
+ return -EINVAL;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+
+ name_len = strlen(name);
+ full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
+ if (!full_name)
+ return -ENOMEM;
+ memcpy(full_name, XATTR_TRUSTED_PREFIX, prefix_len);
+ memcpy(full_name+prefix_len, name, name_len);
+ full_name[prefix_len + name_len] = '\0';
+
+ retval = v9fs_xattr_get(dentry, full_name, buffer, size);
+ kfree(full_name);
+ return retval;
+}
+
+static int v9fs_xattr_trusted_set(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags, int type)
+{
+ int retval;
+ char *full_name;
+ size_t name_len;
+ size_t prefix_len = XATTR_TRUSTED_PREFIX_LEN;
+
+ if (name == NULL)
+ return -EINVAL;
+
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+
+ name_len = strlen(name);
+ full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL);
+ if (!full_name)
+ return -ENOMEM;
+ memcpy(full_name, XATTR_TRUSTED_PREFIX, prefix_len);
+ memcpy(full_name + prefix_len, name, name_len);
+ full_name[prefix_len + name_len] = '\0';
+
+ retval = v9fs_xattr_set(dentry, full_name, value, size, flags);
+ kfree(full_name);
+ return retval;
+}
+
+struct xattr_handler v9fs_xattr_trusted_handler = {
+ .prefix = XATTR_TRUSTED_PREFIX,
+ .get = v9fs_xattr_trusted_get,
+ .set = v9fs_xattr_trusted_set,
+};
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index bce8769..89dec7f 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -255,8 +255,6 @@
(current->mm->start_data = N_DATADDR(ex));
current->mm->brk = ex.a_bss +
(current->mm->start_brk = N_BSSADDR(ex));
- current->mm->free_area_cache = current->mm->mmap_base;
- current->mm->cached_hole_size = 0;
retval = setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
if (retval < 0) {
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index f8a0b0e..100edcc 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -738,8 +738,6 @@
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
- current->mm->free_area_cache = current->mm->mmap_base;
- current->mm->cached_hole_size = 0;
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
executable_stack);
if (retval < 0) {
diff --git a/fs/block_dev.c b/fs/block_dev.c
index bb43ce0..c7bda5c 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -58,17 +58,24 @@
struct backing_dev_info *dst)
{
struct backing_dev_info *old = inode->i_data.backing_dev_info;
+ bool wakeup_bdi = false;
if (unlikely(dst == old)) /* deadlock avoidance */
return;
bdi_lock_two(&old->wb, &dst->wb);
spin_lock(&inode->i_lock);
inode->i_data.backing_dev_info = dst;
- if (inode->i_state & I_DIRTY)
+ if (inode->i_state & I_DIRTY) {
+ if (bdi_cap_writeback_dirty(dst) && !wb_has_dirty_io(&dst->wb))
+ wakeup_bdi = true;
list_move(&inode->i_wb_list, &dst->wb.b_dirty);
+ }
spin_unlock(&inode->i_lock);
spin_unlock(&old->wb.list_lock);
spin_unlock(&dst->wb.list_lock);
+
+ if (wakeup_bdi)
+ bdi_wakeup_thread_delayed(dst);
}
/* Kill _all_ buffers and pagecache , dirty or not.. */
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 3d8bf94..45e57cc 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -1,7 +1,7 @@
/*
* fs/cifs/cifsencrypt.c
*
- * Copyright (C) International Business Machines Corp., 2005,2006
+ * Copyright (C) International Business Machines Corp., 2005,2013
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
@@ -31,6 +31,36 @@
#include <linux/random.h>
#include <linux/highmem.h>
+static int
+cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server)
+{
+ int rc;
+ unsigned int size;
+
+ if (server->secmech.sdescmd5 != NULL)
+ return 0; /* already allocated */
+
+ server->secmech.md5 = crypto_alloc_shash("md5", 0, 0);
+ if (IS_ERR(server->secmech.md5)) {
+ cifs_dbg(VFS, "could not allocate crypto md5\n");
+ return PTR_ERR(server->secmech.md5);
+ }
+
+ size = sizeof(struct shash_desc) +
+ crypto_shash_descsize(server->secmech.md5);
+ server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
+ if (!server->secmech.sdescmd5) {
+ rc = -ENOMEM;
+ crypto_free_shash(server->secmech.md5);
+ server->secmech.md5 = NULL;
+ return rc;
+ }
+ server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
+ server->secmech.sdescmd5->shash.flags = 0x0;
+
+ return 0;
+}
+
/*
* Calculate and return the CIFS signature based on the mac key and SMB PDU.
* The 16 byte signature must be allocated by the caller. Note we only use the
@@ -50,8 +80,11 @@
return -EINVAL;
if (!server->secmech.sdescmd5) {
- cifs_dbg(VFS, "%s: Can't generate signature\n", __func__);
- return -1;
+ rc = cifs_crypto_shash_md5_allocate(server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__);
+ return -1;
+ }
}
rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
@@ -556,6 +589,33 @@
return rc;
}
+static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server)
+{
+ unsigned int size;
+
+ /* check if already allocated */
+ if (server->secmech.sdeschmacmd5)
+ return 0;
+
+ server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0);
+ if (IS_ERR(server->secmech.hmacmd5)) {
+ cifs_dbg(VFS, "could not allocate crypto hmacmd5\n");
+ return PTR_ERR(server->secmech.hmacmd5);
+ }
+
+ size = sizeof(struct shash_desc) +
+ crypto_shash_descsize(server->secmech.hmacmd5);
+ server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
+ if (!server->secmech.sdeschmacmd5) {
+ crypto_free_shash(server->secmech.hmacmd5);
+ server->secmech.hmacmd5 = NULL;
+ return -ENOMEM;
+ }
+ server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
+ server->secmech.sdeschmacmd5->shash.flags = 0x0;
+
+ return 0;
+}
int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
@@ -606,6 +666,12 @@
memcpy(ses->auth_key.response + baselen, tiblob, tilen);
+ rc = crypto_hmacmd5_alloc(ses->server);
+ if (rc) {
+ cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
+ goto setup_ntlmv2_rsp_ret;
+ }
+
/* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) {
@@ -705,123 +771,32 @@
void
cifs_crypto_shash_release(struct TCP_Server_Info *server)
{
- if (server->secmech.cmacaes)
+ if (server->secmech.cmacaes) {
crypto_free_shash(server->secmech.cmacaes);
+ server->secmech.cmacaes = NULL;
+ }
- if (server->secmech.hmacsha256)
+ if (server->secmech.hmacsha256) {
crypto_free_shash(server->secmech.hmacsha256);
+ server->secmech.hmacsha256 = NULL;
+ }
- if (server->secmech.md5)
+ if (server->secmech.md5) {
crypto_free_shash(server->secmech.md5);
+ server->secmech.md5 = NULL;
+ }
- if (server->secmech.hmacmd5)
+ if (server->secmech.hmacmd5) {
crypto_free_shash(server->secmech.hmacmd5);
+ server->secmech.hmacmd5 = NULL;
+ }
kfree(server->secmech.sdesccmacaes);
-
+ server->secmech.sdesccmacaes = NULL;
kfree(server->secmech.sdeschmacsha256);
-
+ server->secmech.sdeschmacsha256 = NULL;
kfree(server->secmech.sdeschmacmd5);
-
+ server->secmech.sdeschmacmd5 = NULL;
kfree(server->secmech.sdescmd5);
-}
-
-int
-cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
-{
- int rc;
- unsigned int size;
-
- server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0);
- if (IS_ERR(server->secmech.hmacmd5)) {
- cifs_dbg(VFS, "could not allocate crypto hmacmd5\n");
- return PTR_ERR(server->secmech.hmacmd5);
- }
-
- server->secmech.md5 = crypto_alloc_shash("md5", 0, 0);
- if (IS_ERR(server->secmech.md5)) {
- cifs_dbg(VFS, "could not allocate crypto md5\n");
- rc = PTR_ERR(server->secmech.md5);
- goto crypto_allocate_md5_fail;
- }
-
- server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
- if (IS_ERR(server->secmech.hmacsha256)) {
- cifs_dbg(VFS, "could not allocate crypto hmacsha256\n");
- rc = PTR_ERR(server->secmech.hmacsha256);
- goto crypto_allocate_hmacsha256_fail;
- }
-
- server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
- if (IS_ERR(server->secmech.cmacaes)) {
- cifs_dbg(VFS, "could not allocate crypto cmac-aes");
- rc = PTR_ERR(server->secmech.cmacaes);
- goto crypto_allocate_cmacaes_fail;
- }
-
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.hmacmd5);
- server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdeschmacmd5) {
- rc = -ENOMEM;
- goto crypto_allocate_hmacmd5_sdesc_fail;
- }
- server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
- server->secmech.sdeschmacmd5->shash.flags = 0x0;
-
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.md5);
- server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdescmd5) {
- rc = -ENOMEM;
- goto crypto_allocate_md5_sdesc_fail;
- }
- server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
- server->secmech.sdescmd5->shash.flags = 0x0;
-
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.hmacsha256);
- server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdeschmacsha256) {
- rc = -ENOMEM;
- goto crypto_allocate_hmacsha256_sdesc_fail;
- }
- server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
- server->secmech.sdeschmacsha256->shash.flags = 0x0;
-
- size = sizeof(struct shash_desc) +
- crypto_shash_descsize(server->secmech.cmacaes);
- server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
- if (!server->secmech.sdesccmacaes) {
- cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
- rc = -ENOMEM;
- goto crypto_allocate_cmacaes_sdesc_fail;
- }
- server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
- server->secmech.sdesccmacaes->shash.flags = 0x0;
-
- return 0;
-
-crypto_allocate_cmacaes_sdesc_fail:
- kfree(server->secmech.sdeschmacsha256);
-
-crypto_allocate_hmacsha256_sdesc_fail:
- kfree(server->secmech.sdescmd5);
-
-crypto_allocate_md5_sdesc_fail:
- kfree(server->secmech.sdeschmacmd5);
-
-crypto_allocate_hmacmd5_sdesc_fail:
- crypto_free_shash(server->secmech.cmacaes);
-
-crypto_allocate_cmacaes_fail:
- crypto_free_shash(server->secmech.hmacsha256);
-
-crypto_allocate_hmacsha256_fail:
- crypto_free_shash(server->secmech.md5);
-
-crypto_allocate_md5_fail:
- crypto_free_shash(server->secmech.hmacmd5);
-
- return rc;
+ server->secmech.sdescmd5 = NULL;
}
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index e66b088..1fdc370 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -194,6 +194,7 @@
struct cifs_io_parms;
struct cifs_search_info;
struct cifsInodeInfo;
+struct cifs_open_parms;
struct smb_version_operations {
int (*send_cancel)(struct TCP_Server_Info *, void *,
@@ -307,9 +308,8 @@
const char *, const char *,
struct cifs_sb_info *);
/* open a file for non-posix mounts */
- int (*open)(const unsigned int, struct cifs_tcon *, const char *, int,
- int, int, struct cifs_fid *, __u32 *, FILE_ALL_INFO *,
- struct cifs_sb_info *);
+ int (*open)(const unsigned int, struct cifs_open_parms *,
+ __u32 *, FILE_ALL_INFO *);
/* set fid protocol-specific info */
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
/* close a file */
@@ -912,6 +912,17 @@
bool smallBuf:1; /* so we know which buf_release function to call */
};
+struct cifs_open_parms {
+ struct cifs_tcon *tcon;
+ struct cifs_sb_info *cifs_sb;
+ int disposition;
+ int desired_access;
+ int create_options;
+ const char *path;
+ struct cifs_fid *fid;
+ bool reconnect:1;
+};
+
struct cifs_fid {
__u16 netfid;
#ifdef CONFIG_CIFS_SMB2
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index c8ff018..f7e584d 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -433,7 +433,6 @@
const struct nls_table *);
extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
-extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *);
extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
extern int calc_seckey(struct cifs_ses *);
extern void generate_smb3signingkey(struct TCP_Server_Info *);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index afcb8a1..fa68813 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -2108,12 +2108,6 @@
goto out_err;
}
- rc = cifs_crypto_shash_allocate(tcp_ses);
- if (rc) {
- cifs_dbg(VFS, "could not setup hash structures rc %d\n", rc);
- goto out_err;
- }
-
tcp_ses->ops = volume_info->ops;
tcp_ses->vals = volume_info->vals;
cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 5175aeb..d62ce0d 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -204,6 +204,7 @@
struct inode *newinode = NULL;
int disposition;
struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
*oplock = 0;
if (tcon->ses->server->oplocks)
@@ -319,9 +320,16 @@
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- rc = server->ops->open(xid, tcon, full_path, disposition,
- desired_access, create_options, fid, oplock,
- buf, cifs_sb);
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = desired_access;
+ oparms.create_options = create_options;
+ oparms.disposition = disposition;
+ oparms.path = full_path;
+ oparms.fid = fid;
+ oparms.reconnect = false;
+
+ rc = server->ops->open(xid, &oparms, oplock, buf);
if (rc) {
cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
goto out;
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 91d8629..1e57f36 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -183,6 +183,7 @@
int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf;
struct TCP_Server_Info *server = tcon->ses->server;
+ struct cifs_open_parms oparms;
if (!server->ops->open)
return -ENOSYS;
@@ -224,9 +225,16 @@
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- rc = server->ops->open(xid, tcon, full_path, disposition,
- desired_access, create_options, fid, oplock, buf,
- cifs_sb);
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = desired_access;
+ oparms.create_options = create_options;
+ oparms.disposition = disposition;
+ oparms.path = full_path;
+ oparms.fid = fid;
+ oparms.reconnect = false;
+
+ rc = server->ops->open(xid, &oparms, oplock, buf);
if (rc)
goto out;
@@ -553,11 +561,10 @@
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
- /* we are going to update can_cache_brlcks here - need a write access */
- down_write(&cinode->lock_sem);
+ down_read(&cinode->lock_sem);
if (cinode->can_cache_brlcks) {
- /* can cache locks - no need to push them */
- up_write(&cinode->lock_sem);
+ /* can cache locks - no need to relock */
+ up_read(&cinode->lock_sem);
return rc;
}
@@ -568,7 +575,7 @@
else
rc = tcon->ses->server->ops->push_mand_locks(cfile);
- up_write(&cinode->lock_sem);
+ up_read(&cinode->lock_sem);
return rc;
}
@@ -587,7 +594,7 @@
int desired_access;
int disposition = FILE_OPEN;
int create_options = CREATE_NOT_DIR;
- struct cifs_fid fid;
+ struct cifs_open_parms oparms;
xid = get_xid();
mutex_lock(&cfile->fh_mutex);
@@ -637,7 +644,7 @@
rc = cifs_posix_open(full_path, NULL, inode->i_sb,
cifs_sb->mnt_file_mode /* ignored */,
- oflags, &oplock, &fid.netfid, xid);
+ oflags, &oplock, &cfile->fid.netfid, xid);
if (rc == 0) {
cifs_dbg(FYI, "posix reopen succeeded\n");
goto reopen_success;
@@ -654,7 +661,16 @@
create_options |= CREATE_OPEN_BACKUP_INTENT;
if (server->ops->get_lease_key)
- server->ops->get_lease_key(inode, &fid);
+ server->ops->get_lease_key(inode, &cfile->fid);
+
+ oparms.tcon = tcon;
+ oparms.cifs_sb = cifs_sb;
+ oparms.desired_access = desired_access;
+ oparms.create_options = create_options;
+ oparms.disposition = disposition;
+ oparms.path = full_path;
+ oparms.fid = &cfile->fid;
+ oparms.reconnect = true;
/*
* Can not refresh inode by passing in file_info buf to be returned by
@@ -663,9 +679,14 @@
* version of file size can be stale. If we knew for sure that inode was
* not dirty locally we could do this.
*/
- rc = server->ops->open(xid, tcon, full_path, disposition,
- desired_access, create_options, &fid, &oplock,
- NULL, cifs_sb);
+ rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ if (rc == -ENOENT && oparms.reconnect == false) {
+ /* durable handle timeout is expired - open the file again */
+ rc = server->ops->open(xid, &oparms, &oplock, NULL);
+ /* indicate that we need to relock the file */
+ oparms.reconnect = true;
+ }
+
if (rc) {
mutex_unlock(&cfile->fh_mutex);
cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc);
@@ -696,8 +717,9 @@
* to the server to get the new inode info.
*/
- server->ops->set_fid(cfile, &fid, oplock);
- cifs_relock_file(cfile);
+ server->ops->set_fid(cfile, &cfile->fid, oplock);
+ if (oparms.reconnect)
+ cifs_relock_file(cfile);
reopen_error_exit:
kfree(full_path);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 20efd81..449b6cf 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -558,6 +558,11 @@
fattr->cf_mode &= ~(S_IWUGO);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
+ if (fattr->cf_nlink < 1) {
+ cifs_dbg(1, "replacing bogus file nlink value %u\n",
+ fattr->cf_nlink);
+ fattr->cf_nlink = 1;
+ }
}
fattr->cf_uid = cifs_sb->mnt_uid;
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index e813f04..6457690 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -674,20 +674,23 @@
}
static int
-cifs_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
- int disposition, int desired_access, int create_options,
- struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
- struct cifs_sb_info *cifs_sb)
+cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
+ __u32 *oplock, FILE_ALL_INFO *buf)
{
- if (!(tcon->ses->capabilities & CAP_NT_SMBS))
- return SMBLegacyOpen(xid, tcon, path, disposition,
- desired_access, create_options,
- &fid->netfid, oplock, buf,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+ if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
+ return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
+ oparms->disposition,
+ oparms->desired_access,
+ oparms->create_options,
+ &oparms->fid->netfid, oplock, buf,
+ oparms->cifs_sb->local_nls,
+ oparms->cifs_sb->mnt_cifs_flags
& CIFS_MOUNT_MAP_SPECIAL_CHR);
- return CIFSSMBOpen(xid, tcon, path, disposition, desired_access,
- create_options, &fid->netfid, oplock, buf,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ return CIFSSMBOpen(xid, oparms->tcon, oparms->path,
+ oparms->disposition, oparms->desired_access,
+ oparms->create_options, &oparms->fid->netfid, oplock,
+ buf, oparms->cifs_sb->local_nls,
+ oparms->cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index 5da1b55..04a81a4 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -40,7 +40,8 @@
oplock &= 0xFF;
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
return;
- if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+ if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE ||
+ oplock == SMB2_OPLOCK_LEVEL_BATCH) {
cinode->clientCanCacheAll = true;
cinode->clientCanCacheRead = true;
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
@@ -57,17 +58,16 @@
}
int
-smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
- int disposition, int desired_access, int create_options,
- struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
- struct cifs_sb_info *cifs_sb)
+smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
+ __u32 *oplock, FILE_ALL_INFO *buf)
{
int rc;
__le16 *smb2_path;
struct smb2_file_all_info *smb2_data = NULL;
__u8 smb2_oplock[17];
+ struct cifs_fid *fid = oparms->fid;
- smb2_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL) {
rc = -ENOMEM;
goto out;
@@ -80,21 +80,19 @@
goto out;
}
- desired_access |= FILE_READ_ATTRIBUTES;
- *smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ oparms->desired_access |= FILE_READ_ATTRIBUTES;
+ *smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
- if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
+ if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
- rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid,
- &fid->volatile_fid, desired_access, disposition,
- 0, 0, smb2_oplock, smb2_data);
+ rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data);
if (rc)
goto out;
if (buf) {
/* open response does not have IndexNumber field - get it */
- rc = SMB2_get_srv_num(xid, tcon, fid->persistent_fid,
+ rc = SMB2_get_srv_num(xid, oparms->tcon, fid->persistent_fid,
fid->volatile_fid,
&smb2_data->IndexNumber);
if (rc) {
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index fff6dfb..c6ec163 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -41,21 +41,26 @@
smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path,
__u32 desired_access, __u32 create_disposition,
- __u32 file_attributes, __u32 create_options,
- void *data, int command)
+ __u32 create_options, void *data, int command)
{
int rc, tmprc = 0;
- u64 persistent_fid, volatile_fid;
__le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
- rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
- desired_access, create_disposition, file_attributes,
- create_options, &oplock, NULL);
+ oparms.tcon = tcon;
+ oparms.desired_access = desired_access;
+ oparms.disposition = create_disposition;
+ oparms.create_options = create_options;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
if (rc) {
kfree(utf16_path);
return rc;
@@ -65,8 +70,8 @@
case SMB2_OP_DELETE:
break;
case SMB2_OP_QUERY_INFO:
- tmprc = SMB2_query_info(xid, tcon, persistent_fid,
- volatile_fid,
+ tmprc = SMB2_query_info(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid,
(struct smb2_file_all_info *)data);
break;
case SMB2_OP_MKDIR:
@@ -76,19 +81,21 @@
*/
break;
case SMB2_OP_RENAME:
- tmprc = SMB2_rename(xid, tcon, persistent_fid, volatile_fid,
- (__le16 *)data);
+ tmprc = SMB2_rename(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, (__le16 *)data);
break;
case SMB2_OP_HARDLINK:
- tmprc = SMB2_set_hardlink(xid, tcon, persistent_fid,
- volatile_fid, (__le16 *)data);
+ tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, (__le16 *)data);
break;
case SMB2_OP_SET_EOF:
- tmprc = SMB2_set_eof(xid, tcon, persistent_fid, volatile_fid,
- current->tgid, (__le64 *)data);
+ tmprc = SMB2_set_eof(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid, current->tgid,
+ (__le64 *)data);
break;
case SMB2_OP_SET_INFO:
- tmprc = SMB2_set_info(xid, tcon, persistent_fid, volatile_fid,
+ tmprc = SMB2_set_info(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid,
(FILE_BASIC_INFO *)data);
break;
default:
@@ -96,7 +103,7 @@
break;
}
- rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
if (tmprc)
rc = tmprc;
kfree(utf16_path);
@@ -129,8 +136,8 @@
return -ENOMEM;
rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
- FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0,
- smb2_data, SMB2_OP_QUERY_INFO);
+ FILE_READ_ATTRIBUTES, FILE_OPEN, 0, smb2_data,
+ SMB2_OP_QUERY_INFO);
if (rc)
goto out;
@@ -145,7 +152,7 @@
struct cifs_sb_info *cifs_sb)
{
return smb2_open_op_close(xid, tcon, cifs_sb, name,
- FILE_WRITE_ATTRIBUTES, FILE_CREATE, 0,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR);
}
@@ -164,7 +171,7 @@
dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
data.Attributes = cpu_to_le32(dosattrs);
tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name,
- FILE_WRITE_ATTRIBUTES, FILE_CREATE, 0,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO);
if (tmprc == 0)
cifs_i->cifsAttrs = dosattrs;
@@ -175,7 +182,7 @@
struct cifs_sb_info *cifs_sb)
{
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
- 0, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
+ CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
NULL, SMB2_OP_DELETE);
}
@@ -184,7 +191,7 @@
struct cifs_sb_info *cifs_sb)
{
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
- 0, CREATE_DELETE_ON_CLOSE, NULL,
+ CREATE_DELETE_ON_CLOSE, NULL,
SMB2_OP_DELETE);
}
@@ -203,7 +210,7 @@
}
rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access,
- FILE_OPEN, 0, 0, smb2_to_name, command);
+ FILE_OPEN, 0, smb2_to_name, command);
smb2_rename_path:
kfree(smb2_to_name);
return rc;
@@ -234,7 +241,7 @@
{
__le64 eof = cpu_to_le64(size);
return smb2_open_op_close(xid, tcon, cifs_sb, full_path,
- FILE_WRITE_DATA, FILE_OPEN, 0, 0, &eof,
+ FILE_WRITE_DATA, FILE_OPEN, 0, &eof,
SMB2_OP_SET_EOF);
}
@@ -250,7 +257,7 @@
if (IS_ERR(tlink))
return PTR_ERR(tlink);
rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
- FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, 0, buf,
+ FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
SMB2_OP_SET_INFO);
cifs_put_tlink(tlink);
return rc;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 6d15cab..f259e6c 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -213,22 +213,29 @@
struct cifs_sb_info *cifs_sb, const char *full_path)
{
int rc;
- __u64 persistent_fid, volatile_fid;
__le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
- rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
- FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
if (rc) {
kfree(utf16_path);
return rc;
}
- rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
kfree(utf16_path);
return rc;
}
@@ -443,15 +450,20 @@
__le16 *utf16_path;
int rc;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
- __u64 persistent_fid, volatile_fid;
+ struct cifs_open_parms oparms;
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
- rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
- FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0,
- &oplock, NULL);
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
kfree(utf16_path);
if (rc) {
cifs_dbg(VFS, "open dir failed\n");
@@ -460,14 +472,12 @@
srch_inf->entries_in_buffer = 0;
srch_inf->index_of_last_entry = 0;
- fid->persistent_fid = persistent_fid;
- fid->volatile_fid = volatile_fid;
- rc = SMB2_query_directory(xid, tcon, persistent_fid, volatile_fid, 0,
- srch_inf);
+ rc = SMB2_query_directory(xid, tcon, fid->persistent_fid,
+ fid->volatile_fid, 0, srch_inf);
if (rc) {
cifs_dbg(VFS, "query directory failed\n");
- SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
}
return rc;
}
@@ -528,17 +538,25 @@
struct kstatfs *buf)
{
int rc;
- u64 persistent_fid, volatile_fid;
__le16 srch_path = 0; /* Null - open root of share */
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ struct cifs_open_parms oparms;
+ struct cifs_fid fid;
- rc = SMB2_open(xid, tcon, &srch_path, &persistent_fid, &volatile_fid,
- FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
+ oparms.tcon = tcon;
+ oparms.desired_access = FILE_READ_ATTRIBUTES;
+ oparms.disposition = FILE_OPEN;
+ oparms.create_options = 0;
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+ rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL);
if (rc)
return rc;
buf->f_type = SMB2_MAGIC_NUMBER;
- rc = SMB2_QFS_info(xid, tcon, persistent_fid, volatile_fid, buf);
- SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ rc = SMB2_QFS_info(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+ buf);
+ SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
return rc;
}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 2b312e4..abc9c28 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -847,29 +847,76 @@
return buf;
}
+static struct create_durable *
+create_durable_buf(void)
+{
+ struct create_durable *buf;
+
+ buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_durable, Data));
+ buf->ccontext.DataLength = cpu_to_le32(16);
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_durable, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ buf->Name[0] = 'D';
+ buf->Name[1] = 'H';
+ buf->Name[2] = 'n';
+ buf->Name[3] = 'Q';
+ return buf;
+}
+
+static struct create_durable *
+create_reconnect_durable_buf(struct cifs_fid *fid)
+{
+ struct create_durable *buf;
+
+ buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_durable, Data));
+ buf->ccontext.DataLength = cpu_to_le32(16);
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_durable, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ buf->Data.Fid.PersistentFileId = fid->persistent_fid;
+ buf->Data.Fid.VolatileFileId = fid->volatile_fid;
+ buf->Name[0] = 'D';
+ buf->Name[1] = 'H';
+ buf->Name[2] = 'n';
+ buf->Name[3] = 'C';
+ return buf;
+}
+
static __u8
parse_lease_state(struct smb2_create_rsp *rsp)
{
char *data_offset;
struct create_lease *lc;
bool found = false;
+ unsigned int next = 0;
+ char *name;
- data_offset = (char *)rsp;
- data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset);
+ data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
lc = (struct create_lease *)data_offset;
do {
- char *name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
+ lc = (struct create_lease *)((char *)lc + next);
+ name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
if (le16_to_cpu(lc->ccontext.NameLength) != 4 ||
strncmp(name, "RqLs", 4)) {
- lc = (struct create_lease *)((char *)lc
- + le32_to_cpu(lc->ccontext.Next));
+ next = le32_to_cpu(lc->ccontext.Next);
continue;
}
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
return SMB2_OPLOCK_LEVEL_NOCHANGE;
found = true;
break;
- } while (le32_to_cpu(lc->ccontext.Next) != 0);
+ } while (next != 0);
if (!found)
return 0;
@@ -877,23 +924,74 @@
return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
}
+static int
+add_lease_context(struct kvec *iov, unsigned int *num_iovec, __u8 *oplock)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ iov[num].iov_base = create_lease_buf(oplock+1, *oplock);
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct create_lease);
+ req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset = cpu_to_le32(
+ sizeof(struct smb2_create_req) - 4 +
+ iov[num - 1].iov_len);
+ req->CreateContextsLength = cpu_to_le32(
+ le32_to_cpu(req->CreateContextsLength) +
+ sizeof(struct create_lease));
+ inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
+ *num_iovec = num + 1;
+ return 0;
+}
+
+static int
+add_durable_context(struct kvec *iov, unsigned int *num_iovec,
+ struct cifs_open_parms *oparms)
+{
+ struct smb2_create_req *req = iov[0].iov_base;
+ unsigned int num = *num_iovec;
+
+ if (oparms->reconnect) {
+ iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
+ /* indicate that we don't need to relock the file */
+ oparms->reconnect = false;
+ } else
+ iov[num].iov_base = create_durable_buf();
+ if (iov[num].iov_base == NULL)
+ return -ENOMEM;
+ iov[num].iov_len = sizeof(struct create_durable);
+ if (!req->CreateContextsOffset)
+ req->CreateContextsOffset =
+ cpu_to_le32(sizeof(struct smb2_create_req) - 4 +
+ iov[1].iov_len);
+ req->CreateContextsLength =
+ cpu_to_le32(le32_to_cpu(req->CreateContextsLength) +
+ sizeof(struct create_durable));
+ inc_rfc1001_len(&req->hdr, sizeof(struct create_durable));
+ *num_iovec = num + 1;
+ return 0;
+}
+
int
-SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
- u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
- __u32 create_disposition, __u32 file_attributes, __u32 create_options,
+SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
__u8 *oplock, struct smb2_file_all_info *buf)
{
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
+ struct cifs_tcon *tcon = oparms->tcon;
struct cifs_ses *ses = tcon->ses;
- struct kvec iov[3];
+ struct kvec iov[4];
int resp_buftype;
int uni_path_len;
__le16 *copy_path = NULL;
int copy_size;
int rc = 0;
- int num_iovecs = 2;
+ unsigned int num_iovecs = 2;
+ __u32 file_attributes = 0;
cifs_dbg(FYI, "create/open\n");
@@ -906,55 +1004,47 @@
if (rc)
return rc;
+ if (oparms->create_options & CREATE_OPTION_READONLY)
+ file_attributes |= ATTR_READONLY;
+
req->ImpersonationLevel = IL_IMPERSONATION;
- req->DesiredAccess = cpu_to_le32(desired_access);
+ req->DesiredAccess = cpu_to_le32(oparms->desired_access);
/* File attributes ignored on open (used in create though) */
req->FileAttributes = cpu_to_le32(file_attributes);
req->ShareAccess = FILE_SHARE_ALL_LE;
- req->CreateDisposition = cpu_to_le32(create_disposition);
- req->CreateOptions = cpu_to_le32(create_options);
+ req->CreateDisposition = cpu_to_le32(oparms->disposition);
+ req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
- req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)
- - 8 /* pad */ - 4 /* do not count rfc1001 len field */);
+ /* do not count rfc1001 len field */
+ req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
/* MUST set path len (NameLength) to 0 opening root of share */
- if (uni_path_len >= 4) {
- req->NameLength = cpu_to_le16(uni_path_len - 2);
- /* -1 since last byte is buf[0] which is sent below (path) */
- iov[0].iov_len--;
- if (uni_path_len % 8 != 0) {
- copy_size = uni_path_len / 8 * 8;
- if (copy_size < uni_path_len)
- copy_size += 8;
+ req->NameLength = cpu_to_le16(uni_path_len - 2);
+ /* -1 since last byte is buf[0] which is sent below (path) */
+ iov[0].iov_len--;
+ if (uni_path_len % 8 != 0) {
+ copy_size = uni_path_len / 8 * 8;
+ if (copy_size < uni_path_len)
+ copy_size += 8;
- copy_path = kzalloc(copy_size, GFP_KERNEL);
- if (!copy_path)
- return -ENOMEM;
- memcpy((char *)copy_path, (const char *)path,
- uni_path_len);
- uni_path_len = copy_size;
- path = copy_path;
- }
-
- iov[1].iov_len = uni_path_len;
- iov[1].iov_base = path;
- /*
- * -1 since last byte is buf[0] which was counted in
- * smb2_buf_len.
- */
- inc_rfc1001_len(req, uni_path_len - 1);
- } else {
- iov[0].iov_len += 7;
- req->hdr.smb2_buf_length = cpu_to_be32(be32_to_cpu(
- req->hdr.smb2_buf_length) + 8 - 1);
- num_iovecs = 1;
- req->NameLength = 0;
+ copy_path = kzalloc(copy_size, GFP_KERNEL);
+ if (!copy_path)
+ return -ENOMEM;
+ memcpy((char *)copy_path, (const char *)path,
+ uni_path_len);
+ uni_path_len = copy_size;
+ path = copy_path;
}
+ iov[1].iov_len = uni_path_len;
+ iov[1].iov_base = path;
+ /* -1 since last byte is buf[0] which was counted in smb2_buf_len */
+ inc_rfc1001_len(req, uni_path_len - 1);
+
if (!server->oplocks)
*oplock = SMB2_OPLOCK_LEVEL_NONE;
@@ -962,21 +1052,29 @@
*oplock == SMB2_OPLOCK_LEVEL_NONE)
req->RequestedOplockLevel = *oplock;
else {
- iov[num_iovecs].iov_base = create_lease_buf(oplock+1, *oplock);
- if (iov[num_iovecs].iov_base == NULL) {
+ rc = add_lease_context(iov, &num_iovecs, oplock);
+ if (rc) {
cifs_small_buf_release(req);
kfree(copy_path);
- return -ENOMEM;
+ return rc;
}
- iov[num_iovecs].iov_len = sizeof(struct create_lease);
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
- req->CreateContextsOffset = cpu_to_le32(
- sizeof(struct smb2_create_req) - 4 - 8 +
- iov[num_iovecs-1].iov_len);
- req->CreateContextsLength = cpu_to_le32(
- sizeof(struct create_lease));
- inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
- num_iovecs++;
+ }
+
+ if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
+ /* need to set Next field of lease context if we request it */
+ if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
+ struct create_context *ccontext =
+ (struct create_context *)iov[num_iovecs-1].iov_base;
+ ccontext->Next =
+ cpu_to_le32(sizeof(struct create_lease));
+ }
+ rc = add_durable_context(iov, &num_iovecs, oparms);
+ if (rc) {
+ cifs_small_buf_release(req);
+ kfree(copy_path);
+ kfree(iov[num_iovecs-1].iov_base);
+ return rc;
+ }
}
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
@@ -987,8 +1085,8 @@
goto creat_exit;
}
- *persistent_fid = rsp->PersistentFileId;
- *volatile_fid = rsp->VolatileFileId;
+ oparms->fid->persistent_fid = rsp->PersistentFileId;
+ oparms->fid->volatile_fid = rsp->VolatileFileId;
if (buf) {
memcpy(buf, &rsp->CreationTime, 32);
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index f31043b..36b0d37 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -428,7 +428,7 @@
__le16 NameLength;
__le32 CreateContextsOffset;
__le32 CreateContextsLength;
- __u8 Buffer[8];
+ __u8 Buffer[0];
} __packed;
struct smb2_create_rsp {
@@ -485,6 +485,18 @@
struct lease_context lcontext;
} __packed;
+struct create_durable {
+ struct create_context ccontext;
+ __u8 Name[8];
+ union {
+ __u8 Reserved[16];
+ struct {
+ __u64 PersistentFileId;
+ __u64 VolatileFileId;
+ } Fid;
+ } Data;
+} __packed;
+
/* this goes in the ioctl buffer when doing a copychunk request */
struct copychunk_ioctl {
char SourceKey[24];
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index d4e1eb8..1a5ecbe 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -84,11 +84,9 @@
const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb);
-extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon,
- const char *full_path, int disposition,
- int desired_access, int create_options,
- struct cifs_fid *fid, __u32 *oplock,
- FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb);
+extern int smb2_open_file(const unsigned int xid,
+ struct cifs_open_parms *oparms,
+ __u32 *oplock, FILE_ALL_INFO *buf);
extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid);
@@ -106,11 +104,9 @@
const char *tree, struct cifs_tcon *tcon,
const struct nls_table *);
extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
-extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon,
- __le16 *path, u64 *persistent_fid, u64 *volatile_fid,
- __u32 desired_access, __u32 create_disposition,
- __u32 file_attributes, __u32 create_options,
- __u8 *oplock, struct smb2_file_all_info *buf);
+extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
+ __le16 *path, __u8 *oplock,
+ struct smb2_file_all_info *buf);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen,
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 09b4fba..301b191 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -39,6 +39,77 @@
#include "smb2status.h"
#include "smb2glob.h"
+static int
+smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
+{
+ unsigned int size;
+
+ if (server->secmech.sdeschmacsha256 != NULL)
+ return 0; /* already allocated */
+
+ server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
+ if (IS_ERR(server->secmech.hmacsha256)) {
+ cifs_dbg(VFS, "could not allocate crypto hmacsha256\n");
+ return PTR_ERR(server->secmech.hmacsha256);
+ }
+
+ size = sizeof(struct shash_desc) +
+ crypto_shash_descsize(server->secmech.hmacsha256);
+ server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
+ if (!server->secmech.sdeschmacsha256) {
+ crypto_free_shash(server->secmech.hmacsha256);
+ server->secmech.hmacsha256 = NULL;
+ return -ENOMEM;
+ }
+ server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
+ server->secmech.sdeschmacsha256->shash.flags = 0x0;
+
+ return 0;
+}
+
+static int
+smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
+{
+ unsigned int size;
+ int rc;
+
+ if (server->secmech.sdesccmacaes != NULL)
+ return 0; /* already allocated */
+
+ rc = smb2_crypto_shash_allocate(server);
+ if (rc)
+ return rc;
+
+ server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
+ if (IS_ERR(server->secmech.cmacaes)) {
+ cifs_dbg(VFS, "could not allocate crypto cmac-aes");
+ kfree(server->secmech.sdeschmacsha256);
+ server->secmech.sdeschmacsha256 = NULL;
+ crypto_free_shash(server->secmech.hmacsha256);
+ server->secmech.hmacsha256 = NULL;
+ return PTR_ERR(server->secmech.cmacaes);
+ }
+
+ size = sizeof(struct shash_desc) +
+ crypto_shash_descsize(server->secmech.cmacaes);
+ server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
+ if (!server->secmech.sdesccmacaes) {
+ cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
+ kfree(server->secmech.sdeschmacsha256);
+ server->secmech.sdeschmacsha256 = NULL;
+ crypto_free_shash(server->secmech.hmacsha256);
+ crypto_free_shash(server->secmech.cmacaes);
+ server->secmech.hmacsha256 = NULL;
+ server->secmech.cmacaes = NULL;
+ return -ENOMEM;
+ }
+ server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
+ server->secmech.sdesccmacaes->shash.flags = 0x0;
+
+ return 0;
+}
+
+
int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
@@ -52,6 +123,12 @@
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+ rc = smb2_crypto_shash_allocate(server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: shah256 alloc failed\n", __func__);
+ return rc;
+ }
+
rc = crypto_shash_setkey(server->secmech.hmacsha256,
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) {
@@ -61,7 +138,7 @@
rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
if (rc) {
- cifs_dbg(VFS, "%s: Could not init md5\n", __func__);
+ cifs_dbg(VFS, "%s: Could not init sha256", __func__);
return rc;
}
@@ -129,6 +206,12 @@
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
+ rc = smb3_crypto_shash_allocate(server);
+ if (rc) {
+ cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
+ goto smb3signkey_ret;
+ }
+
rc = crypto_shash_setkey(server->secmech.hmacsha256,
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) {
@@ -210,6 +293,11 @@
return rc;
}
+ /*
+ * we already allocate sdesccmacaes when we init smb3 signing key,
+ * so unlike smb2 case we do not have to check here if secmech are
+ * initialized
+ */
rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash);
if (rc) {
cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index cfa109a..d107576 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -37,16 +37,8 @@
#include <asm/unaligned.h>
#include "ecryptfs_kernel.h"
-static int
-ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
- struct page *dst_page, int dst_offset,
- struct page *src_page, int src_offset, int size,
- unsigned char *iv);
-static int
-ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
- struct page *dst_page, int dst_offset,
- struct page *src_page, int src_offset, int size,
- unsigned char *iv);
+#define DECRYPT 0
+#define ENCRYPT 1
/**
* ecryptfs_to_hex
@@ -336,19 +328,20 @@
}
/**
- * encrypt_scatterlist
+ * crypt_scatterlist
* @crypt_stat: Pointer to the crypt_stat struct to initialize.
- * @dest_sg: Destination of encrypted data
- * @src_sg: Data to be encrypted
- * @size: Length of data to be encrypted
- * @iv: iv to use during encryption
+ * @dst_sg: Destination of the data after performing the crypto operation
+ * @src_sg: Data to be encrypted or decrypted
+ * @size: Length of data
+ * @iv: IV to use
+ * @op: ENCRYPT or DECRYPT to indicate the desired operation
*
- * Returns the number of bytes encrypted; negative value on error
+ * Returns the number of bytes encrypted or decrypted; negative value on error
*/
-static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
- struct scatterlist *dest_sg,
- struct scatterlist *src_sg, int size,
- unsigned char *iv)
+static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
+ struct scatterlist *dst_sg,
+ struct scatterlist *src_sg, int size,
+ unsigned char *iv, int op)
{
struct ablkcipher_request *req = NULL;
struct extent_crypt_result ecr;
@@ -391,9 +384,9 @@
crypt_stat->flags |= ECRYPTFS_KEY_SET;
}
mutex_unlock(&crypt_stat->cs_tfm_mutex);
- ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size);
- ablkcipher_request_set_crypt(req, src_sg, dest_sg, size, iv);
- rc = crypto_ablkcipher_encrypt(req);
+ ablkcipher_request_set_crypt(req, src_sg, dst_sg, size, iv);
+ rc = op == ENCRYPT ? crypto_ablkcipher_encrypt(req) :
+ crypto_ablkcipher_decrypt(req);
if (rc == -EINPROGRESS || rc == -EBUSY) {
struct extent_crypt_result *ecr = req->base.data;
@@ -407,41 +400,43 @@
}
/**
- * ecryptfs_lower_offset_for_extent
+ * lower_offset_for_page
*
* Convert an eCryptfs page index into a lower byte offset
*/
-static void ecryptfs_lower_offset_for_extent(loff_t *offset, loff_t extent_num,
- struct ecryptfs_crypt_stat *crypt_stat)
+static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat,
+ struct page *page)
{
- (*offset) = ecryptfs_lower_header_size(crypt_stat)
- + (crypt_stat->extent_size * extent_num);
+ return ecryptfs_lower_header_size(crypt_stat) +
+ (page->index << PAGE_CACHE_SHIFT);
}
/**
- * ecryptfs_encrypt_extent
- * @enc_extent_page: Allocated page into which to encrypt the data in
- * @page
+ * crypt_extent
* @crypt_stat: crypt_stat containing cryptographic context for the
* encryption operation
- * @page: Page containing plaintext data extent to encrypt
+ * @dst_page: The page to write the result into
+ * @src_page: The page to read from
* @extent_offset: Page extent offset for use in generating IV
+ * @op: ENCRYPT or DECRYPT to indicate the desired operation
*
- * Encrypts one extent of data.
+ * Encrypts or decrypts one extent of data.
*
* Return zero on success; non-zero otherwise
*/
-static int ecryptfs_encrypt_extent(struct page *enc_extent_page,
- struct ecryptfs_crypt_stat *crypt_stat,
- struct page *page,
- unsigned long extent_offset)
+static int crypt_extent(struct ecryptfs_crypt_stat *crypt_stat,
+ struct page *dst_page,
+ struct page *src_page,
+ unsigned long extent_offset, int op)
{
+ pgoff_t page_index = op == ENCRYPT ? src_page->index : dst_page->index;
loff_t extent_base;
char extent_iv[ECRYPTFS_MAX_IV_BYTES];
+ struct scatterlist src_sg, dst_sg;
+ size_t extent_size = crypt_stat->extent_size;
int rc;
- extent_base = (((loff_t)page->index)
- * (PAGE_CACHE_SIZE / crypt_stat->extent_size));
+ extent_base = (((loff_t)page_index) * (PAGE_CACHE_SIZE / extent_size));
rc = ecryptfs_derive_iv(extent_iv, crypt_stat,
(extent_base + extent_offset));
if (rc) {
@@ -450,15 +445,21 @@
(unsigned long long)(extent_base + extent_offset), rc);
goto out;
}
- rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0,
- page, (extent_offset
- * crypt_stat->extent_size),
- crypt_stat->extent_size, extent_iv);
+
+ sg_init_table(&src_sg, 1);
+ sg_init_table(&dst_sg, 1);
+
+ sg_set_page(&src_sg, src_page, extent_size,
+ extent_offset * extent_size);
+ sg_set_page(&dst_sg, dst_page, extent_size,
+ extent_offset * extent_size);
+
+ rc = crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, extent_size,
+ extent_iv, op);
if (rc < 0) {
- printk(KERN_ERR "%s: Error attempting to encrypt page with "
- "page->index = [%ld], extent_offset = [%ld]; "
- "rc = [%d]\n", __func__, page->index, extent_offset,
- rc);
+ printk(KERN_ERR "%s: Error attempting to crypt page with "
+ "page_index = [%ld], extent_offset = [%ld]; "
+ "rc = [%d]\n", __func__, page_index, extent_offset, rc);
goto out;
}
rc = 0;
@@ -489,6 +490,7 @@
char *enc_extent_virt;
struct page *enc_extent_page = NULL;
loff_t extent_offset;
+ loff_t lower_offset;
int rc = 0;
ecryptfs_inode = page->mapping->host;
@@ -502,78 +504,38 @@
"encrypted extent\n");
goto out;
}
- enc_extent_virt = kmap(enc_extent_page);
+
for (extent_offset = 0;
extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size);
extent_offset++) {
- loff_t offset;
-
- rc = ecryptfs_encrypt_extent(enc_extent_page, crypt_stat, page,
- extent_offset);
+ rc = crypt_extent(crypt_stat, enc_extent_page, page,
+ extent_offset, ENCRYPT);
if (rc) {
printk(KERN_ERR "%s: Error encrypting extent; "
"rc = [%d]\n", __func__, rc);
goto out;
}
- ecryptfs_lower_offset_for_extent(
- &offset, ((((loff_t)page->index)
- * (PAGE_CACHE_SIZE
- / crypt_stat->extent_size))
- + extent_offset), crypt_stat);
- rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt,
- offset, crypt_stat->extent_size);
- if (rc < 0) {
- ecryptfs_printk(KERN_ERR, "Error attempting "
- "to write lower page; rc = [%d]"
- "\n", rc);
- goto out;
- }
+ }
+
+ lower_offset = lower_offset_for_page(crypt_stat, page);
+ enc_extent_virt = kmap(enc_extent_page);
+ rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt, lower_offset,
+ PAGE_CACHE_SIZE);
+ kunmap(enc_extent_page);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR,
+ "Error attempting to write lower page; rc = [%d]\n",
+ rc);
+ goto out;
}
rc = 0;
out:
if (enc_extent_page) {
- kunmap(enc_extent_page);
__free_page(enc_extent_page);
}
return rc;
}
-static int ecryptfs_decrypt_extent(struct page *page,
- struct ecryptfs_crypt_stat *crypt_stat,
- struct page *enc_extent_page,
- unsigned long extent_offset)
-{
- loff_t extent_base;
- char extent_iv[ECRYPTFS_MAX_IV_BYTES];
- int rc;
-
- extent_base = (((loff_t)page->index)
- * (PAGE_CACHE_SIZE / crypt_stat->extent_size));
- rc = ecryptfs_derive_iv(extent_iv, crypt_stat,
- (extent_base + extent_offset));
- if (rc) {
- ecryptfs_printk(KERN_ERR, "Error attempting to derive IV for "
- "extent [0x%.16llx]; rc = [%d]\n",
- (unsigned long long)(extent_base + extent_offset), rc);
- goto out;
- }
- rc = ecryptfs_decrypt_page_offset(crypt_stat, page,
- (extent_offset
- * crypt_stat->extent_size),
- enc_extent_page, 0,
- crypt_stat->extent_size, extent_iv);
- if (rc < 0) {
- printk(KERN_ERR "%s: Error attempting to decrypt to page with "
- "page->index = [%ld], extent_offset = [%ld]; "
- "rc = [%d]\n", __func__, page->index, extent_offset,
- rc);
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
/**
* ecryptfs_decrypt_page
* @page: Page mapped from the eCryptfs inode for the file; data read
@@ -594,43 +556,33 @@
{
struct inode *ecryptfs_inode;
struct ecryptfs_crypt_stat *crypt_stat;
- char *enc_extent_virt;
- struct page *enc_extent_page = NULL;
+ char *page_virt;
unsigned long extent_offset;
+ loff_t lower_offset;
int rc = 0;
ecryptfs_inode = page->mapping->host;
crypt_stat =
&(ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat);
BUG_ON(!(crypt_stat->flags & ECRYPTFS_ENCRYPTED));
- enc_extent_page = alloc_page(GFP_USER);
- if (!enc_extent_page) {
- rc = -ENOMEM;
- ecryptfs_printk(KERN_ERR, "Error allocating memory for "
- "encrypted extent\n");
+
+ lower_offset = lower_offset_for_page(crypt_stat, page);
+ page_virt = kmap(page);
+ rc = ecryptfs_read_lower(page_virt, lower_offset, PAGE_CACHE_SIZE,
+ ecryptfs_inode);
+ kunmap(page);
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR,
+ "Error attempting to read lower page; rc = [%d]\n",
+ rc);
goto out;
}
- enc_extent_virt = kmap(enc_extent_page);
+
for (extent_offset = 0;
extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size);
extent_offset++) {
- loff_t offset;
-
- ecryptfs_lower_offset_for_extent(
- &offset, ((page->index * (PAGE_CACHE_SIZE
- / crypt_stat->extent_size))
- + extent_offset), crypt_stat);
- rc = ecryptfs_read_lower(enc_extent_virt, offset,
- crypt_stat->extent_size,
- ecryptfs_inode);
- if (rc < 0) {
- ecryptfs_printk(KERN_ERR, "Error attempting "
- "to read lower page; rc = [%d]"
- "\n", rc);
- goto out;
- }
- rc = ecryptfs_decrypt_extent(page, crypt_stat, enc_extent_page,
- extent_offset);
+ rc = crypt_extent(crypt_stat, page, page,
+ extent_offset, DECRYPT);
if (rc) {
printk(KERN_ERR "%s: Error encrypting extent; "
"rc = [%d]\n", __func__, rc);
@@ -638,142 +590,9 @@
}
}
out:
- if (enc_extent_page) {
- kunmap(enc_extent_page);
- __free_page(enc_extent_page);
- }
return rc;
}
-/**
- * decrypt_scatterlist
- * @crypt_stat: Cryptographic context
- * @dest_sg: The destination scatterlist to decrypt into
- * @src_sg: The source scatterlist to decrypt from
- * @size: The number of bytes to decrypt
- * @iv: The initialization vector to use for the decryption
- *
- * Returns the number of bytes decrypted; negative value on error
- */
-static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
- struct scatterlist *dest_sg,
- struct scatterlist *src_sg, int size,
- unsigned char *iv)
-{
- struct ablkcipher_request *req = NULL;
- struct extent_crypt_result ecr;
- int rc = 0;
-
- BUG_ON(!crypt_stat || !crypt_stat->tfm
- || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED));
- if (unlikely(ecryptfs_verbosity > 0)) {
- ecryptfs_printk(KERN_DEBUG, "Key size [%zd]; key:\n",
- crypt_stat->key_size);
- ecryptfs_dump_hex(crypt_stat->key,
- crypt_stat->key_size);
- }
-
- init_completion(&ecr.completion);
-
- mutex_lock(&crypt_stat->cs_tfm_mutex);
- req = ablkcipher_request_alloc(crypt_stat->tfm, GFP_NOFS);
- if (!req) {
- mutex_unlock(&crypt_stat->cs_tfm_mutex);
- rc = -ENOMEM;
- goto out;
- }
-
- ablkcipher_request_set_callback(req,
- CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
- extent_crypt_complete, &ecr);
- /* Consider doing this once, when the file is opened */
- if (!(crypt_stat->flags & ECRYPTFS_KEY_SET)) {
- rc = crypto_ablkcipher_setkey(crypt_stat->tfm, crypt_stat->key,
- crypt_stat->key_size);
- if (rc) {
- ecryptfs_printk(KERN_ERR,
- "Error setting key; rc = [%d]\n",
- rc);
- mutex_unlock(&crypt_stat->cs_tfm_mutex);
- rc = -EINVAL;
- goto out;
- }
- crypt_stat->flags |= ECRYPTFS_KEY_SET;
- }
- mutex_unlock(&crypt_stat->cs_tfm_mutex);
- ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size);
- ablkcipher_request_set_crypt(req, src_sg, dest_sg, size, iv);
- rc = crypto_ablkcipher_decrypt(req);
- if (rc == -EINPROGRESS || rc == -EBUSY) {
- struct extent_crypt_result *ecr = req->base.data;
-
- wait_for_completion(&ecr->completion);
- rc = ecr->rc;
- INIT_COMPLETION(ecr->completion);
- }
-out:
- ablkcipher_request_free(req);
- return rc;
-
-}
-
-/**
- * ecryptfs_encrypt_page_offset
- * @crypt_stat: The cryptographic context
- * @dst_page: The page to encrypt into
- * @dst_offset: The offset in the page to encrypt into
- * @src_page: The page to encrypt from
- * @src_offset: The offset in the page to encrypt from
- * @size: The number of bytes to encrypt
- * @iv: The initialization vector to use for the encryption
- *
- * Returns the number of bytes encrypted
- */
-static int
-ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
- struct page *dst_page, int dst_offset,
- struct page *src_page, int src_offset, int size,
- unsigned char *iv)
-{
- struct scatterlist src_sg, dst_sg;
-
- sg_init_table(&src_sg, 1);
- sg_init_table(&dst_sg, 1);
-
- sg_set_page(&src_sg, src_page, size, src_offset);
- sg_set_page(&dst_sg, dst_page, size, dst_offset);
- return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv);
-}
-
-/**
- * ecryptfs_decrypt_page_offset
- * @crypt_stat: The cryptographic context
- * @dst_page: The page to decrypt into
- * @dst_offset: The offset in the page to decrypt into
- * @src_page: The page to decrypt from
- * @src_offset: The offset in the page to decrypt from
- * @size: The number of bytes to decrypt
- * @iv: The initialization vector to use for the decryption
- *
- * Returns the number of bytes decrypted
- */
-static int
-ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat,
- struct page *dst_page, int dst_offset,
- struct page *src_page, int src_offset, int size,
- unsigned char *iv)
-{
- struct scatterlist src_sg, dst_sg;
-
- sg_init_table(&src_sg, 1);
- sg_set_page(&src_sg, src_page, size, src_offset);
-
- sg_init_table(&dst_sg, 1);
- sg_set_page(&dst_sg, dst_page, size, dst_offset);
-
- return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv);
-}
-
#define ECRYPTFS_MAX_SCATTERLIST_LEN 4
/**
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 24f1105..992cf95 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -49,7 +49,7 @@
unsigned long nr_segs, loff_t pos)
{
ssize_t rc;
- struct path lower;
+ struct path *path;
struct file *file = iocb->ki_filp;
rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
@@ -60,9 +60,8 @@
if (-EIOCBQUEUED == rc)
rc = wait_on_sync_kiocb(iocb);
if (rc >= 0) {
- lower.dentry = ecryptfs_dentry_to_lower(file->f_path.dentry);
- lower.mnt = ecryptfs_dentry_to_lower_mnt(file->f_path.dentry);
- touch_atime(&lower);
+ path = ecryptfs_dentry_to_lower_path(file->f_path.dentry);
+ touch_atime(path);
}
return rc;
}
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index e924cf4..eb1c597 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -120,16 +120,15 @@
struct file **lower_file)
{
const struct cred *cred = current_cred();
- struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
- struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
+ struct path *path = ecryptfs_dentry_to_lower_path(dentry);
int rc;
- rc = ecryptfs_privileged_open(lower_file, lower_dentry, lower_mnt,
+ rc = ecryptfs_privileged_open(lower_file, path->dentry, path->mnt,
cred);
if (rc) {
printk(KERN_ERR "Error opening lower file "
"for lower_dentry [0x%p] and lower_mnt [0x%p]; "
- "rc = [%d]\n", lower_dentry, lower_mnt, rc);
+ "rc = [%d]\n", path->dentry, path->mnt, rc);
(*lower_file) = NULL;
}
return rc;
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
index 49ff8ea0..e57380e 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -247,14 +247,13 @@
goto unlock;
}
msg_size = (sizeof(*msg) + msg->data_len);
- msg_ctx->msg = kmalloc(msg_size, GFP_KERNEL);
+ msg_ctx->msg = kmemdup(msg, msg_size, GFP_KERNEL);
if (!msg_ctx->msg) {
rc = -ENOMEM;
printk(KERN_ERR "%s: Failed to allocate [%zd] bytes of "
"GFP_KERNEL memory\n", __func__, msg_size);
goto unlock;
}
- memcpy(msg_ctx->msg, msg, msg_size);
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE;
wake_up_process(msg_ctx->task);
rc = 0;
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 9a55f53..370d7b6 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -346,8 +346,7 @@
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
- jfs_error(ip->i_sb,
- "dbFree: block to be freed is outside the map");
+ jfs_error(ip->i_sb, "block to be freed is outside the map\n");
return -EIO;
}
@@ -384,7 +383,7 @@
/* free the blocks. */
if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
- jfs_error(ip->i_sb, "dbFree: error in block map\n");
+ jfs_error(ip->i_sb, "error in block map\n");
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (rc);
@@ -441,8 +440,7 @@
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
- jfs_error(ipbmap->i_sb,
- "dbUpdatePMap: blocks are outside the map");
+ jfs_error(ipbmap->i_sb, "blocks are outside the map\n");
return -EIO;
}
@@ -726,7 +724,7 @@
/* the hint should be within the map */
if (hint >= mapSize) {
- jfs_error(ip->i_sb, "dbAlloc: the hint is outside the map");
+ jfs_error(ip->i_sb, "the hint is outside the map\n");
return -EIO;
}
@@ -1057,8 +1055,7 @@
bmp = sbi->bmap;
if (lastblkno < 0 || lastblkno >= bmp->db_mapsize) {
IREAD_UNLOCK(ipbmap);
- jfs_error(ip->i_sb,
- "dbExtend: the block is outside the filesystem");
+ jfs_error(ip->i_sb, "the block is outside the filesystem\n");
return -EIO;
}
@@ -1134,8 +1131,7 @@
u32 mask;
if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocNext: Corrupt dmap page");
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -1265,8 +1261,7 @@
s8 *leaf;
if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocNear: Corrupt dmap page");
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -1381,8 +1376,7 @@
*/
if (l2nb > bmp->db_agl2size) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAG: allocation request is larger than the "
- "allocation group size");
+ "allocation request is larger than the allocation group size\n");
return -EIO;
}
@@ -1417,7 +1411,7 @@
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAG: dbAllocCtl failed in free AG");
+ "dbAllocCtl failed in free AG\n");
}
return (rc);
}
@@ -1433,8 +1427,7 @@
budmin = dcp->budmin;
if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAG: Corrupt dmapctl page");
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
}
@@ -1475,7 +1468,7 @@
}
if (n == 4) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAG: failed descending stree");
+ "failed descending stree\n");
release_metapage(mp);
return -EIO;
}
@@ -1515,8 +1508,7 @@
&blkno))) {
if (rc == -ENOSPC) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAG: control page "
- "inconsistent");
+ "control page inconsistent\n");
return -EIO;
}
return (rc);
@@ -1528,7 +1520,7 @@
rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
if (rc == -ENOSPC) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAG: unable to allocate blocks");
+ "unable to allocate blocks\n");
rc = -EIO;
}
return (rc);
@@ -1587,8 +1579,7 @@
*/
rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
if (rc == -ENOSPC) {
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocAny: unable to allocate blocks");
+ jfs_error(bmp->db_ipbmap->i_sb, "unable to allocate blocks\n");
return -EIO;
}
return (rc);
@@ -1652,8 +1643,7 @@
range_cnt = min_t(u64, max_ranges + 1, 32 * 1024);
totrim = kmalloc(sizeof(struct range2trim) * range_cnt, GFP_NOFS);
if (totrim == NULL) {
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbDiscardAG: no memory for trim array");
+ jfs_error(bmp->db_ipbmap->i_sb, "no memory for trim array\n");
IWRITE_UNLOCK(ipbmap);
return 0;
}
@@ -1682,8 +1672,7 @@
nblocks = 1 << l2nb;
} else {
/* Trim any already allocated blocks */
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbDiscardAG: -EIO");
+ jfs_error(bmp->db_ipbmap->i_sb, "-EIO\n");
break;
}
@@ -1761,7 +1750,7 @@
if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbFindCtl: Corrupt dmapctl page");
+ "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
}
@@ -1782,7 +1771,7 @@
if (rc) {
if (lev != level) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbFindCtl: dmap inconsistent");
+ "dmap inconsistent\n");
return -EIO;
}
return -ENOSPC;
@@ -1906,7 +1895,7 @@
if (dp->tree.stree[ROOT] != L2BPERDMAP) {
release_metapage(mp);
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocCtl: the dmap is not all free");
+ "the dmap is not all free\n");
rc = -EIO;
goto backout;
}
@@ -1953,7 +1942,7 @@
* to indicate that we have leaked blocks.
*/
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocCtl: I/O Error: Block Leakage.");
+ "I/O Error: Block Leakage\n");
continue;
}
dp = (struct dmap *) mp->data;
@@ -1965,8 +1954,7 @@
* to indicate that we have leaked blocks.
*/
release_metapage(mp);
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocCtl: Block Leakage.");
+ jfs_error(bmp->db_ipbmap->i_sb, "Block Leakage\n");
continue;
}
@@ -2263,8 +2251,7 @@
for (; nwords > 0; nwords -= nw) {
if (leaf[word] < BUDMIN) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAllocBits: leaf page "
- "corrupt");
+ "leaf page corrupt\n");
break;
}
@@ -2536,8 +2523,7 @@
dcp = (struct dmapctl *) mp->data;
if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
- jfs_error(bmp->db_ipbmap->i_sb,
- "dbAdjCtl: Corrupt dmapctl page");
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
}
@@ -2638,8 +2624,7 @@
assert(level == bmp->db_maxlevel);
if (bmp->db_maxfreebud != oldroot) {
jfs_error(bmp->db_ipbmap->i_sb,
- "dbAdjCtl: the maximum free buddy is "
- "not the old root");
+ "the maximum free buddy is not the old root\n");
}
bmp->db_maxfreebud = dcp->stree[ROOT];
}
@@ -3481,7 +3466,7 @@
p = BMAPBLKNO + nbperpage; /* L2 page */
l2mp = read_metapage(ipbmap, p, PSIZE, 0);
if (!l2mp) {
- jfs_error(ipbmap->i_sb, "dbExtendFS: L2 page could not be read");
+ jfs_error(ipbmap->i_sb, "L2 page could not be read\n");
return -EIO;
}
l2dcp = (struct dmapctl *) l2mp->data;
@@ -3646,8 +3631,7 @@
}
} /* for each L1 in a L2 */
- jfs_error(ipbmap->i_sb,
- "dbExtendFS: function has not returned as expected");
+ jfs_error(ipbmap->i_sb, "function has not returned as expected\n");
errout:
if (l0mp)
release_metapage(l0mp);
@@ -3717,7 +3701,7 @@
}
if (bmp->db_agpref >= bmp->db_numag) {
jfs_error(ipbmap->i_sb,
- "cannot find ag with average freespace");
+ "cannot find ag with average freespace\n");
}
}
diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index 9f4ed13..8743ba9 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -124,21 +124,21 @@
#define DT_PAGE(IP, MP) BT_PAGE(IP, MP, dtpage_t, i_dtroot)
/* get page buffer for specified block address */
-#define DT_GETPAGE(IP, BN, MP, SIZE, P, RC)\
-{\
- BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot)\
- if (!(RC))\
- {\
- if (((P)->header.nextindex > (((BN)==0)?DTROOTMAXSLOT:(P)->header.maxslot)) ||\
- ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT)))\
- {\
- BT_PUTPAGE(MP);\
- jfs_error((IP)->i_sb, "DT_GETPAGE: dtree page corrupt");\
- MP = NULL;\
- RC = -EIO;\
- }\
- }\
-}
+#define DT_GETPAGE(IP, BN, MP, SIZE, P, RC) \
+do { \
+ BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot); \
+ if (!(RC)) { \
+ if (((P)->header.nextindex > \
+ (((BN) == 0) ? DTROOTMAXSLOT : (P)->header.maxslot)) || \
+ ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT))) { \
+ BT_PUTPAGE(MP); \
+ jfs_error((IP)->i_sb, \
+ "DT_GETPAGE: dtree page corrupt\n"); \
+ MP = NULL; \
+ RC = -EIO; \
+ } \
+ } \
+} while (0)
/* for consistency */
#define DT_PUTPAGE(MP) BT_PUTPAGE(MP)
@@ -776,7 +776,7 @@
/* Something's corrupted, mark filesystem dirty so
* chkdsk will fix it.
*/
- jfs_error(sb, "stack overrun in dtSearch!");
+ jfs_error(sb, "stack overrun!\n");
BT_STACK_DUMP(btstack);
rc = -EIO;
goto out;
@@ -3247,8 +3247,7 @@
/* Sanity Check */
if (d_namleft == 0) {
jfs_error(ip->i_sb,
- "JFS:Dtree error: ino = "
- "%ld, bn=%Ld, index = %d",
+ "JFS:Dtree error: ino = %ld, bn=%lld, index = %d\n",
(long)ip->i_ino,
(long long)bn,
i);
@@ -3368,7 +3367,7 @@
*/
if (BT_STACK_FULL(btstack)) {
DT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "dtReadFirst: btstack overrun");
+ jfs_error(ip->i_sb, "btstack overrun\n");
BT_STACK_DUMP(btstack);
return -EIO;
}
diff --git a/fs/jfs/jfs_extent.c b/fs/jfs/jfs_extent.c
index e5fe850..2ae7d59a 100644
--- a/fs/jfs/jfs_extent.c
+++ b/fs/jfs/jfs_extent.c
@@ -388,7 +388,7 @@
if ((rc == 0) && xlen) {
if (xlen != nbperpage) {
- jfs_error(ip->i_sb, "extHint: corrupt xtree");
+ jfs_error(ip->i_sb, "corrupt xtree\n");
rc = -EIO;
}
XADaddress(xp, xaddr);
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index f7e042b..f321986 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -386,7 +386,7 @@
dp += rel_inode;
if (ip->i_ino != le32_to_cpu(dp->di_number)) {
- jfs_error(ip->i_sb, "diRead: i_ino != di_number");
+ jfs_error(ip->i_sb, "i_ino != di_number\n");
rc = -EIO;
} else if (le32_to_cpu(dp->di_nlink) == 0)
rc = -ESTALE;
@@ -625,7 +625,7 @@
if (!addressPXD(&(jfs_ip->ixpxd)) ||
(lengthPXD(&(jfs_ip->ixpxd)) !=
JFS_IP(ipimap)->i_imap->im_nbperiext)) {
- jfs_error(ip->i_sb, "diWrite: ixpxd invalid");
+ jfs_error(ip->i_sb, "ixpxd invalid\n");
return -EIO;
}
@@ -893,8 +893,7 @@
if (iagno >= imap->im_nextiag) {
print_hex_dump(KERN_ERR, "imap: ", DUMP_PREFIX_ADDRESS, 16, 4,
imap, 32, 0);
- jfs_error(ip->i_sb,
- "diFree: inum = %d, iagno = %d, nextiag = %d",
+ jfs_error(ip->i_sb, "inum = %d, iagno = %d, nextiag = %d\n",
(uint) inum, iagno, imap->im_nextiag);
return -EIO;
}
@@ -930,15 +929,14 @@
mask = HIGHORDER >> bitno;
if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
- jfs_error(ip->i_sb,
- "diFree: wmap shows inode already free");
+ jfs_error(ip->i_sb, "wmap shows inode already free\n");
}
if (!addressPXD(&iagp->inoext[extno])) {
release_metapage(mp);
IREAD_UNLOCK(ipimap);
AG_UNLOCK(imap, agno);
- jfs_error(ip->i_sb, "diFree: invalid inoext");
+ jfs_error(ip->i_sb, "invalid inoext\n");
return -EIO;
}
@@ -950,7 +948,7 @@
release_metapage(mp);
IREAD_UNLOCK(ipimap);
AG_UNLOCK(imap, agno);
- jfs_error(ip->i_sb, "diFree: numfree > numinos");
+ jfs_error(ip->i_sb, "numfree > numinos\n");
return -EIO;
}
/*
@@ -1199,7 +1197,7 @@
* for the inode being freed.
*/
if (iagp->pmap[extno] != 0) {
- jfs_error(ip->i_sb, "diFree: the pmap does not show inode free");
+ jfs_error(ip->i_sb, "the pmap does not show inode free\n");
}
iagp->wmap[extno] = 0;
PXDlength(&iagp->inoext[extno], 0);
@@ -1518,8 +1516,7 @@
release_metapage(mp);
AG_UNLOCK(imap, agno);
jfs_error(ip->i_sb,
- "diAlloc: can't find free bit "
- "in wmap");
+ "can't find free bit in wmap\n");
return -EIO;
}
@@ -1660,7 +1657,7 @@
numinos = imap->im_agctl[agno].numinos;
if (numfree > numinos) {
- jfs_error(ip->i_sb, "diAllocAG: numfree > numinos");
+ jfs_error(ip->i_sb, "numfree > numinos\n");
return -EIO;
}
@@ -1811,8 +1808,7 @@
if (!iagp->nfreeinos) {
IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
- jfs_error(ip->i_sb,
- "diAllocIno: nfreeinos = 0, but iag on freelist");
+ jfs_error(ip->i_sb, "nfreeinos = 0, but iag on freelist\n");
return -EIO;
}
@@ -1824,7 +1820,7 @@
IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
jfs_error(ip->i_sb,
- "diAllocIno: free inode not found in summary map");
+ "free inode not found in summary map\n");
return -EIO;
}
@@ -1839,7 +1835,7 @@
if (rem >= EXTSPERSUM) {
IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
- jfs_error(ip->i_sb, "diAllocIno: no free extent found");
+ jfs_error(ip->i_sb, "no free extent found\n");
return -EIO;
}
extno = (sword << L2EXTSPERSUM) + rem;
@@ -1850,7 +1846,7 @@
if (rem >= INOSPEREXT) {
IREAD_UNLOCK(imap->im_ipimap);
release_metapage(mp);
- jfs_error(ip->i_sb, "diAllocIno: free inode not found");
+ jfs_error(ip->i_sb, "free inode not found\n");
return -EIO;
}
@@ -1936,7 +1932,7 @@
IREAD_LOCK(imap->im_ipimap, RDWRLOCK_IMAP);
if ((rc = diIAGRead(imap, iagno, &mp))) {
IREAD_UNLOCK(imap->im_ipimap);
- jfs_error(ip->i_sb, "diAllocExt: error reading iag");
+ jfs_error(ip->i_sb, "error reading iag\n");
return rc;
}
iagp = (struct iag *) mp->data;
@@ -1948,8 +1944,7 @@
if (sword >= SMAPSZ) {
release_metapage(mp);
IREAD_UNLOCK(imap->im_ipimap);
- jfs_error(ip->i_sb,
- "diAllocExt: free ext summary map not found");
+ jfs_error(ip->i_sb, "free ext summary map not found\n");
return -EIO;
}
if (~iagp->extsmap[sword])
@@ -1962,7 +1957,7 @@
if (rem >= EXTSPERSUM) {
release_metapage(mp);
IREAD_UNLOCK(imap->im_ipimap);
- jfs_error(ip->i_sb, "diAllocExt: free extent not found");
+ jfs_error(ip->i_sb, "free extent not found\n");
return -EIO;
}
extno = (sword << L2EXTSPERSUM) + rem;
@@ -2081,8 +2076,7 @@
if (bmp)
release_metapage(bmp);
- jfs_error(imap->im_ipimap->i_sb,
- "diAllocBit: iag inconsistent");
+ jfs_error(imap->im_ipimap->i_sb, "iag inconsistent\n");
return -EIO;
}
@@ -2189,7 +2183,7 @@
/* better have free extents.
*/
if (!iagp->nfreeexts) {
- jfs_error(imap->im_ipimap->i_sb, "diNewExt: no free extents");
+ jfs_error(imap->im_ipimap->i_sb, "no free extents\n");
return -EIO;
}
@@ -2261,7 +2255,7 @@
}
if (ciagp == NULL) {
jfs_error(imap->im_ipimap->i_sb,
- "diNewExt: ciagp == NULL");
+ "ciagp == NULL\n");
rc = -EIO;
goto error_out;
}
@@ -2498,7 +2492,7 @@
IWRITE_UNLOCK(ipimap);
IAGFREE_UNLOCK(imap);
jfs_error(imap->im_ipimap->i_sb,
- "diNewIAG: ipimap->i_size is wrong");
+ "ipimap->i_size is wrong\n");
return -EIO;
}
@@ -2758,8 +2752,7 @@
iagno = INOTOIAG(inum);
/* make sure that the iag is contained within the map */
if (iagno >= imap->im_nextiag) {
- jfs_error(ipimap->i_sb,
- "diUpdatePMap: the iag is outside the map");
+ jfs_error(ipimap->i_sb, "the iag is outside the map\n");
return -EIO;
}
/* read the iag */
@@ -2788,13 +2781,13 @@
*/
if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
jfs_error(ipimap->i_sb,
- "diUpdatePMap: inode %ld not marked as "
- "allocated in wmap!", inum);
+ "inode %ld not marked as allocated in wmap!\n",
+ inum);
}
if (!(le32_to_cpu(iagp->pmap[extno]) & mask)) {
jfs_error(ipimap->i_sb,
- "diUpdatePMap: inode %ld not marked as "
- "allocated in pmap!", inum);
+ "inode %ld not marked as allocated in pmap!\n",
+ inum);
}
/* update the bitmap for the extent of the freed inode */
iagp->pmap[extno] &= cpu_to_le32(~mask);
@@ -2809,15 +2802,13 @@
if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
release_metapage(mp);
jfs_error(ipimap->i_sb,
- "diUpdatePMap: the inode is not allocated in "
- "the working map");
+ "the inode is not allocated in the working map\n");
return -EIO;
}
if ((le32_to_cpu(iagp->pmap[extno]) & mask) != 0) {
release_metapage(mp);
jfs_error(ipimap->i_sb,
- "diUpdatePMap: the inode is not free in the "
- "persistent map");
+ "the inode is not free in the persistent map\n");
return -EIO;
}
/* update the bitmap for the extent of the allocated inode */
@@ -2909,8 +2900,7 @@
iagp = (struct iag *) bp->data;
if (le32_to_cpu(iagp->iagnum) != i) {
release_metapage(bp);
- jfs_error(ipimap->i_sb,
- "diExtendFs: unexpected value of iagnum");
+ jfs_error(ipimap->i_sb, "unexpected value of iagnum\n");
return -EIO;
}
@@ -2986,8 +2976,7 @@
if (xnuminos != atomic_read(&imap->im_numinos) ||
xnumfree != atomic_read(&imap->im_numfree)) {
- jfs_error(ipimap->i_sb,
- "diExtendFs: numinos or numfree incorrect");
+ jfs_error(ipimap->i_sb, "numinos or numfree incorrect\n");
return -EIO;
}
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index 9e3aaff..d165cde 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -647,7 +647,7 @@
if (mp) {
if (mp->logical_size != size) {
jfs_error(inode->i_sb,
- "__get_metapage: mp->logical_size != size");
+ "get_mp->logical_size != size\n");
jfs_err("logical_size = %d, size = %d",
mp->logical_size, size);
dump_stack();
@@ -658,8 +658,7 @@
if (test_bit(META_discard, &mp->flag)) {
if (!new) {
jfs_error(inode->i_sb,
- "__get_metapage: using a "
- "discarded metapage");
+ "using a discarded metapage\n");
discard_metapage(mp);
goto unlock;
}
diff --git a/fs/jfs/jfs_superblock.h b/fs/jfs/jfs_superblock.h
index 884fc21..04847b8 100644
--- a/fs/jfs/jfs_superblock.h
+++ b/fs/jfs/jfs_superblock.h
@@ -108,6 +108,7 @@
extern int readSuper(struct super_block *, struct buffer_head **);
extern int updateSuper(struct super_block *, uint);
+__printf(2, 3)
extern void jfs_error(struct super_block *, const char *, ...);
extern int jfs_mount(struct super_block *);
extern int jfs_mount_rw(struct super_block *, int);
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index 5fcc02e..564c4f2 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -2684,7 +2684,7 @@
* mark filesystem dirty
*/
if (dirty)
- jfs_error(tblk->sb, "txAbort");
+ jfs_error(tblk->sb, "\n");
return;
}
diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c
index 6c50871..5ad7748 100644
--- a/fs/jfs/jfs_xtree.c
+++ b/fs/jfs/jfs_xtree.c
@@ -64,22 +64,23 @@
/* get page buffer for specified block address */
/* ToDo: Replace this ugly macro with a function */
-#define XT_GETPAGE(IP, BN, MP, SIZE, P, RC)\
-{\
- BT_GETPAGE(IP, BN, MP, xtpage_t, SIZE, P, RC, i_xtroot)\
- if (!(RC))\
- {\
- if ((le16_to_cpu((P)->header.nextindex) < XTENTRYSTART) ||\
- (le16_to_cpu((P)->header.nextindex) > le16_to_cpu((P)->header.maxentry)) ||\
- (le16_to_cpu((P)->header.maxentry) > (((BN)==0)?XTROOTMAXSLOT:PSIZE>>L2XTSLOTSIZE)))\
- {\
- jfs_error((IP)->i_sb, "XT_GETPAGE: xtree page corrupt");\
- BT_PUTPAGE(MP);\
- MP = NULL;\
- RC = -EIO;\
- }\
- }\
-}
+#define XT_GETPAGE(IP, BN, MP, SIZE, P, RC) \
+do { \
+ BT_GETPAGE(IP, BN, MP, xtpage_t, SIZE, P, RC, i_xtroot); \
+ if (!(RC)) { \
+ if ((le16_to_cpu((P)->header.nextindex) < XTENTRYSTART) || \
+ (le16_to_cpu((P)->header.nextindex) > \
+ le16_to_cpu((P)->header.maxentry)) || \
+ (le16_to_cpu((P)->header.maxentry) > \
+ (((BN) == 0) ? XTROOTMAXSLOT : PSIZE >> L2XTSLOTSIZE))) { \
+ jfs_error((IP)->i_sb, \
+ "XT_GETPAGE: xtree page corrupt\n"); \
+ BT_PUTPAGE(MP); \
+ MP = NULL; \
+ RC = -EIO; \
+ } \
+ } \
+} while (0)
/* for consistency */
#define XT_PUTPAGE(MP) BT_PUTPAGE(MP)
@@ -499,7 +500,7 @@
/* push (bn, index) of the parent page/entry */
if (BT_STACK_FULL(btstack)) {
- jfs_error(ip->i_sb, "stack overrun in xtSearch!");
+ jfs_error(ip->i_sb, "stack overrun!\n");
XT_PUTPAGE(mp);
return -EIO;
}
@@ -1385,7 +1386,7 @@
if (cmp != 0) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "xtExtend: xtSearch did not find extent");
+ jfs_error(ip->i_sb, "xtSearch did not find extent\n");
return -EIO;
}
@@ -1393,7 +1394,7 @@
xad = &p->xad[index];
if ((offsetXAD(xad) + lengthXAD(xad)) != xoff) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "xtExtend: extension is not contiguous");
+ jfs_error(ip->i_sb, "extension is not contiguous\n");
return -EIO;
}
@@ -1552,7 +1553,7 @@
if (cmp != 0) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "xtTailgate: couldn't find extent");
+ jfs_error(ip->i_sb, "couldn't find extent\n");
return -EIO;
}
@@ -1560,8 +1561,7 @@
nextindex = le16_to_cpu(p->header.nextindex);
if (index != nextindex - 1) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb,
- "xtTailgate: the entry found is not the last entry");
+ jfs_error(ip->i_sb, "the entry found is not the last entry\n");
return -EIO;
}
@@ -1734,7 +1734,7 @@
if (cmp != 0) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "xtUpdate: Could not find extent");
+ jfs_error(ip->i_sb, "Could not find extent\n");
return -EIO;
}
@@ -1758,7 +1758,7 @@
(nxoff + nxlen > xoff + xlen)) {
XT_PUTPAGE(mp);
jfs_error(ip->i_sb,
- "xtUpdate: nXAD in not completely contained within XAD");
+ "nXAD in not completely contained within XAD\n");
return -EIO;
}
@@ -1907,7 +1907,7 @@
if (xoff >= nxoff) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "xtUpdate: xoff >= nxoff");
+ jfs_error(ip->i_sb, "xoff >= nxoff\n");
return -EIO;
}
/* #endif _JFS_WIP_COALESCE */
@@ -2048,14 +2048,13 @@
if (cmp != 0) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb, "xtUpdate: xtSearch failed");
+ jfs_error(ip->i_sb, "xtSearch failed\n");
return -EIO;
}
if (index0 != index) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb,
- "xtUpdate: unexpected value of index");
+ jfs_error(ip->i_sb, "unexpected value of index\n");
return -EIO;
}
}
@@ -3650,7 +3649,7 @@
getChild:
/* save current parent entry for the child page */
if (BT_STACK_FULL(&btstack)) {
- jfs_error(ip->i_sb, "stack overrun in xtTruncate!");
+ jfs_error(ip->i_sb, "stack overrun!\n");
XT_PUTPAGE(mp);
return -EIO;
}
@@ -3751,8 +3750,7 @@
if (cmp != 0) {
XT_PUTPAGE(mp);
- jfs_error(ip->i_sb,
- "xtTruncate_pmap: did not find extent");
+ jfs_error(ip->i_sb, "did not find extent\n");
return -EIO;
}
} else {
@@ -3851,7 +3849,7 @@
getChild:
/* save current parent entry for the child page */
if (BT_STACK_FULL(&btstack)) {
- jfs_error(ip->i_sb, "stack overrun in xtTruncate_pmap!");
+ jfs_error(ip->i_sb, "stack overrun!\n");
XT_PUTPAGE(mp);
return -EIO;
}
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 8b19027..aa8a3370 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -1176,7 +1176,7 @@
if (!S_ISDIR(old_ip->i_mode) && new_ip)
IWRITE_UNLOCK(new_ip);
jfs_error(new_ip->i_sb,
- "jfs_rename: new_ip->i_nlink != 0");
+ "new_ip->i_nlink != 0\n");
return -EIO;
}
tblk = tid_to_tblock(tid);
diff --git a/fs/jfs/resize.c b/fs/jfs/resize.c
index 8d0c1c7..90b3bc2 100644
--- a/fs/jfs/resize.c
+++ b/fs/jfs/resize.c
@@ -530,7 +530,7 @@
goto resume;
error_out:
- jfs_error(sb, "jfs_extendfs");
+ jfs_error(sb, "\n");
resume:
/*
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 788e0a9..6669aa2 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -92,16 +92,20 @@
/* nothing is done for continue beyond marking the superblock dirty */
}
-void jfs_error(struct super_block *sb, const char * function, ...)
+void jfs_error(struct super_block *sb, const char *fmt, ...)
{
- static char error_buf[256];
+ struct va_format vaf;
va_list args;
- va_start(args, function);
- vsnprintf(error_buf, sizeof(error_buf), function, args);
- va_end(args);
+ va_start(args, fmt);
- pr_err("ERROR: (device %s): %s\n", sb->s_id, error_buf);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ pr_err("ERROR: (device %s): %pf: %pV\n",
+ sb->s_id, __builtin_return_address(0), &vaf);
+
+ va_end(args);
jfs_handle_error(sb);
}
@@ -617,7 +621,7 @@
txQuiesce(sb);
rc = lmLogShutdown(log);
if (rc) {
- jfs_error(sb, "jfs_freeze: lmLogShutdown failed");
+ jfs_error(sb, "lmLogShutdown failed\n");
/* let operations fail rather than hang */
txResume(sb);
@@ -646,12 +650,12 @@
if (!(sb->s_flags & MS_RDONLY)) {
rc = updateSuper(sb, FM_MOUNT);
if (rc) {
- jfs_error(sb, "jfs_unfreeze: updateSuper failed");
+ jfs_error(sb, "updateSuper failed\n");
goto out;
}
rc = lmLogInit(log);
if (rc)
- jfs_error(sb, "jfs_unfreeze: lmLogInit failed");
+ jfs_error(sb, "lmLogInit failed\n");
out:
txResume(sb);
}
diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c
index 42d67f9..d3472f4 100644
--- a/fs/jfs/xattr.c
+++ b/fs/jfs/xattr.c
@@ -382,7 +382,7 @@
nbytes = sizeDXD(&ji->ea);
if (!nbytes) {
- jfs_error(sb, "ea_read: nbytes is 0");
+ jfs_error(sb, "nbytes is 0\n");
return -EIO;
}
@@ -482,7 +482,7 @@
current_blocks = 0;
} else {
if (!(ji->ea.flag & DXD_EXTENT)) {
- jfs_error(sb, "ea_get: invalid ea.flag)");
+ jfs_error(sb, "invalid ea.flag\n");
return -EIO;
}
current_blocks = (ea_size + sb->s_blocksize - 1) >>
@@ -1089,8 +1089,8 @@
}
#ifdef CONFIG_JFS_SECURITY
-int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
- void *fs_info)
+static int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+ void *fs_info)
{
const struct xattr *xattr;
tid_t *tid = fs_info;
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 0fac2cb..e474ca2b 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -450,6 +450,7 @@
dentry = d_lookup(parent, &filename);
if (dentry != NULL) {
if (nfs_same_file(dentry, entry)) {
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
status = nfs_refresh_inode(dentry->d_inode, entry->fattr);
if (!status)
nfs_setsecurity(dentry->d_inode, entry->fattr, entry->label);
@@ -817,7 +818,7 @@
nfs_readdir_descriptor_t my_desc,
*desc = &my_desc;
struct nfs_open_dir_context *dir_ctx = file->private_data;
- int res;
+ int res = 0;
dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -839,7 +840,8 @@
desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
nfs_block_sillyrename(dentry);
- res = nfs_revalidate_mapping(inode, file->f_mapping);
+ if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
+ res = nfs_revalidate_mapping(inode, file->f_mapping);
if (res < 0)
goto out;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index c93639e..af6e806 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -936,7 +936,7 @@
return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
}
-static int nfs_attribute_cache_expired(struct inode *inode)
+int nfs_attribute_cache_expired(struct inode *inode)
{
if (nfs_have_delegated_attributes(inode))
return 0;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index a2c7c28..f1bdb72 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -888,6 +888,28 @@
return PageUptodate(page) != 0;
}
+/* If we know the page is up to date, and we're not using byte range locks (or
+ * if we have the whole file locked for writing), it may be more efficient to
+ * extend the write to cover the entire page in order to avoid fragmentation
+ * inefficiencies.
+ *
+ * If the file is opened for synchronous writes or if we have a write delegation
+ * from the server then we can just skip the rest of the checks.
+ */
+static int nfs_can_extend_write(struct file *file, struct page *page, struct inode *inode)
+{
+ if (file->f_flags & O_DSYNC)
+ return 0;
+ if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
+ return 1;
+ if (nfs_write_pageuptodate(page, inode) && (inode->i_flock == NULL ||
+ (inode->i_flock->fl_start == 0 &&
+ inode->i_flock->fl_end == OFFSET_MAX &&
+ inode->i_flock->fl_type != F_RDLCK)))
+ return 1;
+ return 0;
+}
+
/*
* Update and possibly write a cached page of an NFS file.
*
@@ -908,14 +930,7 @@
file->f_path.dentry->d_name.name, count,
(long long)(page_file_offset(page) + offset));
- /* If we're not using byte range locks, and we know the page
- * is up to date, it may be more efficient to extend the write
- * to cover the entire page in order to avoid fragmentation
- * inefficiencies.
- */
- if (nfs_write_pageuptodate(page, inode) &&
- inode->i_flock == NULL &&
- !(file->f_flags & O_DSYNC)) {
+ if (nfs_can_extend_write(file, page, inode)) {
count = max(count + offset, nfs_page_length(page));
offset = 0;
}
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index 430b687..dc8f1ef 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -81,6 +81,22 @@
If unsure, say N.
+config NFSD_V4_SECURITY_LABEL
+ bool "Provide Security Label support for NFSv4 server"
+ depends on NFSD_V4 && SECURITY
+ help
+
+ Say Y here if you want enable fine-grained security label attribute
+ support for NFS version 4. Security labels allow security modules like
+ SELinux and Smack to label files to facilitate enforcement of their policies.
+ Without this an NFSv4 mount will have the same label on each file.
+
+ If you do not wish to enable fine-grained security labels SELinux or
+ Smack policies on NFSv4 files, say N.
+
+ WARNING: there is still a chance of backwards-incompatible protocol changes.
+ For now we recommend "Y" only for developers and testers."
+
config NFSD_FAULT_INJECTION
bool "NFS server manual fault injection"
depends on NFSD_V4 && DEBUG_KERNEL
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 27d74a2..a7cee86 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -42,6 +42,36 @@
#include "current_stateid.h"
#include "netns.h"
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+
+static inline void
+nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
+{
+ struct inode *inode = resfh->fh_dentry->d_inode;
+ int status;
+
+ mutex_lock(&inode->i_mutex);
+ status = security_inode_setsecctx(resfh->fh_dentry,
+ label->data, label->len);
+ mutex_unlock(&inode->i_mutex);
+
+ if (status)
+ /*
+ * XXX: We should really fail the whole open, but we may
+ * already have created a new file, so it may be too
+ * late. For now this seems the least of evils:
+ */
+ bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+
+ return;
+}
+#else
+static inline void
+nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
+{ }
+#endif
+
#define NFSDDBG_FACILITY NFSDDBG_PROC
static u32 nfsd_attrmask[] = {
@@ -239,6 +269,9 @@
(u32 *)open->op_verf.data,
&open->op_truncate, &open->op_created);
+ if (!status && open->op_label.len)
+ nfsd4_security_inode_setsecctx(resfh, &open->op_label, open->op_bmval);
+
/*
* Following rfc 3530 14.2.16, use the returned bitmask
* to indicate which attributes we used to store the
@@ -263,7 +296,8 @@
nfsd4_set_open_owner_reply_cache(cstate, open, resfh);
accmode = NFSD_MAY_NOP;
- if (open->op_created)
+ if (open->op_created ||
+ open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR)
accmode |= NFSD_MAY_OWNER_OVERRIDE;
status = do_open_permission(rqstp, resfh, open, accmode);
set_change_info(&open->op_cinfo, current_fh);
@@ -637,6 +671,9 @@
if (status)
goto out;
+ if (create->cr_label.len)
+ nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval);
+
if (create->cr_acl != NULL)
do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
create->cr_bmval);
@@ -916,6 +953,11 @@
setattr->sa_acl);
if (status)
goto out;
+ if (setattr->sa_label.len)
+ status = nfsd4_set_nfs4_label(rqstp, &cstate->current_fh,
+ &setattr->sa_label);
+ if (status)
+ goto out;
status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
0, (time_t)0);
out:
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f170518..280acef 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -97,19 +97,20 @@
static void free_session(struct nfsd4_session *);
-void nfsd4_put_session(struct nfsd4_session *ses)
-{
- atomic_dec(&ses->se_ref);
-}
-
static bool is_session_dead(struct nfsd4_session *ses)
{
return ses->se_flags & NFS4_SESSION_DEAD;
}
-static __be32 mark_session_dead_locked(struct nfsd4_session *ses)
+void nfsd4_put_session(struct nfsd4_session *ses)
{
- if (atomic_read(&ses->se_ref))
+ if (atomic_dec_and_test(&ses->se_ref) && is_session_dead(ses))
+ free_session(ses);
+}
+
+static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me)
+{
+ if (atomic_read(&ses->se_ref) > ref_held_by_me)
return nfserr_jukebox;
ses->se_flags |= NFS4_SESSION_DEAD;
return nfs_ok;
@@ -364,19 +365,12 @@
}
static struct nfs4_delegation *
-alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type)
+alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh)
{
struct nfs4_delegation *dp;
struct nfs4_file *fp = stp->st_file;
dprintk("NFSD alloc_init_deleg\n");
- /*
- * Major work on the lease subsystem (for example, to support
- * calbacks on stat) will be required before we can support
- * write delegations properly.
- */
- if (type != NFS4_OPEN_DELEGATE_READ)
- return NULL;
if (fp->fi_had_conflict)
return NULL;
if (num_delegations > max_delegations)
@@ -397,7 +391,7 @@
INIT_LIST_HEAD(&dp->dl_recall_lru);
get_nfs4_file(fp);
dp->dl_file = fp;
- dp->dl_type = type;
+ dp->dl_type = NFS4_OPEN_DELEGATE_READ;
fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle);
dp->dl_time = 0;
atomic_set(&dp->dl_count, 1);
@@ -1188,6 +1182,9 @@
target->cr_gid = source->cr_gid;
target->cr_group_info = source->cr_group_info;
get_group_info(target->cr_group_info);
+ target->cr_gss_mech = source->cr_gss_mech;
+ if (source->cr_gss_mech)
+ gss_mech_get(source->cr_gss_mech);
return 0;
}
@@ -1262,6 +1259,31 @@
return 0 == strcmp(cr1->cr_principal, cr2->cr_principal);
}
+static bool svc_rqst_integrity_protected(struct svc_rqst *rqstp)
+{
+ struct svc_cred *cr = &rqstp->rq_cred;
+ u32 service;
+
+ service = gss_pseudoflavor_to_service(cr->cr_gss_mech, cr->cr_flavor);
+ return service == RPC_GSS_SVC_INTEGRITY ||
+ service == RPC_GSS_SVC_PRIVACY;
+}
+
+static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)
+{
+ struct svc_cred *cr = &rqstp->rq_cred;
+
+ if (!cl->cl_mach_cred)
+ return true;
+ if (cl->cl_cred.cr_gss_mech != cr->cr_gss_mech)
+ return false;
+ if (!svc_rqst_integrity_protected(rqstp))
+ return false;
+ if (!cr->cr_principal)
+ return false;
+ return 0 == strcmp(cl->cl_cred.cr_principal, cr->cr_principal);
+}
+
static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn)
{
static u32 current_clientid = 1;
@@ -1639,16 +1661,16 @@
if (exid->flags & ~EXCHGID4_FLAG_MASK_A)
return nfserr_inval;
- /* Currently only support SP4_NONE */
switch (exid->spa_how) {
+ case SP4_MACH_CRED:
+ if (!svc_rqst_integrity_protected(rqstp))
+ return nfserr_inval;
case SP4_NONE:
break;
default: /* checked by xdr code */
WARN_ON_ONCE(1);
case SP4_SSV:
return nfserr_encr_alg_unsupp;
- case SP4_MACH_CRED:
- return nfserr_serverfault; /* no excuse :-/ */
}
/* Cases below refer to rfc 5661 section 18.35.4: */
@@ -1663,6 +1685,10 @@
status = nfserr_inval;
goto out;
}
+ if (!mach_creds_match(conf, rqstp)) {
+ status = nfserr_wrong_cred;
+ goto out;
+ }
if (!creds_match) { /* case 9 */
status = nfserr_perm;
goto out;
@@ -1709,7 +1735,8 @@
status = nfserr_jukebox;
goto out;
}
- new->cl_minorversion = 1;
+ new->cl_minorversion = cstate->minorversion;
+ new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);
gen_clid(new, nn);
add_to_unconfirmed(new);
@@ -1839,6 +1866,24 @@
return nfs_ok;
}
+static __be32 nfsd4_check_cb_sec(struct nfsd4_cb_sec *cbs)
+{
+ switch (cbs->flavor) {
+ case RPC_AUTH_NULL:
+ case RPC_AUTH_UNIX:
+ return nfs_ok;
+ default:
+ /*
+ * GSS case: the spec doesn't allow us to return this
+ * error. But it also doesn't allow us not to support
+ * GSS.
+ * I'd rather this fail hard than return some error the
+ * client might think it can already handle:
+ */
+ return nfserr_encr_alg_unsupp;
+ }
+}
+
__be32
nfsd4_create_session(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
@@ -1854,6 +1899,9 @@
if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
return nfserr_inval;
+ status = nfsd4_check_cb_sec(&cr_ses->cb_sec);
+ if (status)
+ return status;
status = check_forechannel_attrs(&cr_ses->fore_channel, nn);
if (status)
return status;
@@ -1874,6 +1922,9 @@
WARN_ON_ONCE(conf && unconf);
if (conf) {
+ status = nfserr_wrong_cred;
+ if (!mach_creds_match(conf, rqstp))
+ goto out_free_conn;
cs_slot = &conf->cl_cs_slot;
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
if (status == nfserr_replay_cache) {
@@ -1890,6 +1941,9 @@
status = nfserr_clid_inuse;
goto out_free_conn;
}
+ status = nfserr_wrong_cred;
+ if (!mach_creds_match(unconf, rqstp))
+ goto out_free_conn;
cs_slot = &unconf->cl_cs_slot;
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
if (status) {
@@ -1957,7 +2011,11 @@
{
struct nfsd4_session *session = cstate->session;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ __be32 status;
+ status = nfsd4_check_cb_sec(&bc->bc_cb_sec);
+ if (status)
+ return status;
spin_lock(&nn->client_lock);
session->se_cb_prog = bc->bc_cb_program;
session->se_cb_sec = bc->bc_cb_sec;
@@ -1986,6 +2044,9 @@
status = nfserr_badsession;
if (!session)
goto out;
+ status = nfserr_wrong_cred;
+ if (!mach_creds_match(session->se_client, rqstp))
+ goto out;
status = nfsd4_map_bcts_dir(&bcts->dir);
if (status)
goto out;
@@ -2014,6 +2075,7 @@
{
struct nfsd4_session *ses;
__be32 status;
+ int ref_held_by_me = 0;
struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);
nfs4_lock_state();
@@ -2021,6 +2083,7 @@
if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) {
if (!nfsd4_last_compound_op(r))
goto out;
+ ref_held_by_me++;
}
dump_sessionid(__func__, &sessionid->sessionid);
spin_lock(&nn->client_lock);
@@ -2028,17 +2091,22 @@
status = nfserr_badsession;
if (!ses)
goto out_client_lock;
- status = mark_session_dead_locked(ses);
- if (status)
+ status = nfserr_wrong_cred;
+ if (!mach_creds_match(ses->se_client, r))
goto out_client_lock;
+ nfsd4_get_session_locked(ses);
+ status = mark_session_dead_locked(ses, 1 + ref_held_by_me);
+ if (status)
+ goto out_put_session;
unhash_session(ses);
spin_unlock(&nn->client_lock);
nfsd4_probe_callback_sync(ses->se_client);
spin_lock(&nn->client_lock);
- free_session(ses);
status = nfs_ok;
+out_put_session:
+ nfsd4_put_session(ses);
out_client_lock:
spin_unlock(&nn->client_lock);
out:
@@ -2058,26 +2126,31 @@
return NULL;
}
-static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
+static __be32 nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
struct nfsd4_conn *c;
+ __be32 status = nfs_ok;
int ret;
spin_lock(&clp->cl_lock);
c = __nfsd4_find_conn(new->cn_xprt, ses);
- if (c) {
- spin_unlock(&clp->cl_lock);
- free_conn(new);
- return;
- }
+ if (c)
+ goto out_free;
+ status = nfserr_conn_not_bound_to_session;
+ if (clp->cl_mach_cred)
+ goto out_free;
__nfsd4_hash_conn(new, ses);
spin_unlock(&clp->cl_lock);
ret = nfsd4_register_conn(new);
if (ret)
/* oops; xprt is already down: */
nfsd4_conn_lost(&new->cn_xpt_user);
- return;
+ return nfs_ok;
+out_free:
+ spin_unlock(&clp->cl_lock);
+ free_conn(new);
+ return status;
}
static bool nfsd4_session_too_many_ops(struct svc_rqst *rqstp, struct nfsd4_session *session)
@@ -2169,8 +2242,10 @@
if (status)
goto out_put_session;
- nfsd4_sequence_check_conn(conn, session);
+ status = nfsd4_sequence_check_conn(conn, session);
conn = NULL;
+ if (status)
+ goto out_put_session;
/* Success! bump slot seqid */
slot->sl_seqid = seq->seqid;
@@ -2232,7 +2307,10 @@
status = nfserr_stale_clientid;
goto out;
}
-
+ if (!mach_creds_match(clp, rqstp)) {
+ status = nfserr_wrong_cred;
+ goto out;
+ }
expire_client(clp);
out:
nfs4_unlock_state();
@@ -2940,13 +3018,13 @@
return fl;
}
-static int nfs4_setlease(struct nfs4_delegation *dp, int flag)
+static int nfs4_setlease(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_file;
struct file_lock *fl;
int status;
- fl = nfs4_alloc_init_lease(dp, flag);
+ fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ);
if (!fl)
return -ENOMEM;
fl->fl_file = find_readable_file(fp);
@@ -2964,12 +3042,12 @@
return 0;
}
-static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag)
+static int nfs4_set_delegation(struct nfs4_delegation *dp)
{
struct nfs4_file *fp = dp->dl_file;
if (!fp->fi_lease)
- return nfs4_setlease(dp, flag);
+ return nfs4_setlease(dp);
spin_lock(&recall_lock);
if (fp->fi_had_conflict) {
spin_unlock(&recall_lock);
@@ -3005,6 +3083,9 @@
/*
* Attempt to hand out a delegation.
+ *
+ * Note we don't support write delegations, and won't until the vfs has
+ * proper support for them.
*/
static void
nfs4_open_delegation(struct net *net, struct svc_fh *fh,
@@ -3013,39 +3094,45 @@
struct nfs4_delegation *dp;
struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner);
int cb_up;
- int status = 0, flag = 0;
+ int status = 0;
cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
- flag = NFS4_OPEN_DELEGATE_NONE;
open->op_recall = 0;
switch (open->op_claim_type) {
case NFS4_OPEN_CLAIM_PREVIOUS:
if (!cb_up)
open->op_recall = 1;
- flag = open->op_delegate_type;
- if (flag == NFS4_OPEN_DELEGATE_NONE)
- goto out;
+ if (open->op_delegate_type != NFS4_OPEN_DELEGATE_READ)
+ goto out_no_deleg;
break;
case NFS4_OPEN_CLAIM_NULL:
- /* Let's not give out any delegations till everyone's
- * had the chance to reclaim theirs.... */
+ /*
+ * Let's not give out any delegations till everyone's
+ * had the chance to reclaim theirs....
+ */
if (locks_in_grace(net))
- goto out;
+ goto out_no_deleg;
if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED))
- goto out;
+ goto out_no_deleg;
+ /*
+ * Also, if the file was opened for write or
+ * create, there's a good chance the client's
+ * about to write to it, resulting in an
+ * immediate recall (since we don't support
+ * write delegations):
+ */
if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
- flag = NFS4_OPEN_DELEGATE_WRITE;
- else
- flag = NFS4_OPEN_DELEGATE_READ;
+ goto out_no_deleg;
+ if (open->op_create == NFS4_OPEN_CREATE)
+ goto out_no_deleg;
break;
default:
- goto out;
+ goto out_no_deleg;
}
-
- dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh, flag);
+ dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh);
if (dp == NULL)
goto out_no_deleg;
- status = nfs4_set_delegation(dp, flag);
+ status = nfs4_set_delegation(dp);
if (status)
goto out_free;
@@ -3053,24 +3140,23 @@
dprintk("NFSD: delegation stateid=" STATEID_FMT "\n",
STATEID_VAL(&dp->dl_stid.sc_stateid));
-out:
- open->op_delegate_type = flag;
- if (flag == NFS4_OPEN_DELEGATE_NONE) {
- if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
- open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE)
- dprintk("NFSD: WARNING: refusing delegation reclaim\n");
-
- /* 4.1 client asking for a delegation? */
- if (open->op_deleg_want)
- nfsd4_open_deleg_none_ext(open, status);
- }
+ open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
return;
out_free:
unhash_stid(&dp->dl_stid);
nfs4_put_delegation(dp);
out_no_deleg:
- flag = NFS4_OPEN_DELEGATE_NONE;
- goto out;
+ open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
+ if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
+ open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) {
+ dprintk("NFSD: WARNING: refusing delegation reclaim\n");
+ open->op_recall = 1;
+ }
+
+ /* 4.1 client asking for a delegation? */
+ if (open->op_deleg_want)
+ nfsd4_open_deleg_none_ext(open, status);
+ return;
}
static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open,
@@ -3427,7 +3513,7 @@
/* Returns true iff a is later than b: */
static bool stateid_generation_after(stateid_t *a, stateid_t *b)
{
- return (s32)a->si_generation - (s32)b->si_generation > 0;
+ return (s32)(a->si_generation - b->si_generation) > 0;
}
static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
@@ -4435,7 +4521,6 @@
nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_locku *locku)
{
- struct nfs4_lockowner *lo;
struct nfs4_ol_stateid *stp;
struct file *filp = NULL;
struct file_lock *file_lock = NULL;
@@ -4468,10 +4553,9 @@
status = nfserr_jukebox;
goto out;
}
- lo = lockowner(stp->st_stateowner);
locks_init_lock(file_lock);
file_lock->fl_type = F_UNLCK;
- file_lock->fl_owner = (fl_owner_t)lo;
+ file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
file_lock->fl_pid = current->tgid;
file_lock->fl_file = filp;
file_lock->fl_flags = FL_POSIX;
@@ -4490,11 +4574,6 @@
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
- if (nfsd4_has_session(cstate) && !check_for_locks(stp->st_file, lo)) {
- WARN_ON_ONCE(cstate->replay_owner);
- release_lockowner(lo);
- }
-
out:
nfsd4_bump_seqid(cstate, status);
if (!cstate->replay_owner)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 6cd86e0..0c0f3ea9 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -55,6 +55,11 @@
#include "cache.h"
#include "netns.h"
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+#endif
+
+
#define NFSDDBG_FACILITY NFSDDBG_XDR
/*
@@ -134,6 +139,19 @@
} \
} while (0)
+static void next_decode_page(struct nfsd4_compoundargs *argp)
+{
+ argp->pagelist++;
+ argp->p = page_address(argp->pagelist[0]);
+ if (argp->pagelen < PAGE_SIZE) {
+ argp->end = argp->p + (argp->pagelen>>2);
+ argp->pagelen = 0;
+ } else {
+ argp->end = argp->p + (PAGE_SIZE>>2);
+ argp->pagelen -= PAGE_SIZE;
+ }
+}
+
static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
{
/* We want more bytes than seem to be available.
@@ -161,16 +179,7 @@
* guarantee p points to at least nbytes bytes.
*/
memcpy(p, argp->p, avail);
- /* step to next page */
- argp->p = page_address(argp->pagelist[0]);
- argp->pagelist++;
- if (argp->pagelen < PAGE_SIZE) {
- argp->end = argp->p + (argp->pagelen>>2);
- argp->pagelen = 0;
- } else {
- argp->end = argp->p + (PAGE_SIZE>>2);
- argp->pagelen -= PAGE_SIZE;
- }
+ next_decode_page(argp);
memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
argp->p += XDR_QUADLEN(nbytes - avail);
return p;
@@ -242,7 +251,8 @@
static __be32
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
- struct iattr *iattr, struct nfs4_acl **acl)
+ struct iattr *iattr, struct nfs4_acl **acl,
+ struct xdr_netobj *label)
{
int expected_len, len = 0;
u32 dummy32;
@@ -380,6 +390,32 @@
goto xdr_error;
}
}
+
+ label->len = 0;
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
+ READ_BUF(4);
+ len += 4;
+ READ32(dummy32); /* lfs: we don't use it */
+ READ_BUF(4);
+ len += 4;
+ READ32(dummy32); /* pi: we don't use it either */
+ READ_BUF(4);
+ len += 4;
+ READ32(dummy32);
+ READ_BUF(dummy32);
+ if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN)
+ return nfserr_badlabel;
+ len += (XDR_QUADLEN(dummy32) << 2);
+ READMEM(buf, dummy32);
+ label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
+ if (!label->data)
+ return nfserr_jukebox;
+ defer_free(argp, kfree, label->data);
+ memcpy(label->data, buf, dummy32);
+ }
+#endif
+
if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
|| bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
|| bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
@@ -428,7 +464,11 @@
/* callback_sec_params4 */
READ_BUF(4);
READ32(nr_secflavs);
- cbs->flavor = (u32)(-1);
+ if (nr_secflavs)
+ cbs->flavor = (u32)(-1);
+ else
+ /* Is this legal? Be generous, take it to mean AUTH_NONE: */
+ cbs->flavor = 0;
for (i = 0; i < nr_secflavs; ++i) {
READ_BUF(4);
READ32(dummy);
@@ -576,7 +616,7 @@
return status;
status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
- &create->cr_acl);
+ &create->cr_acl, &create->cr_label);
if (status)
goto out;
@@ -827,7 +867,7 @@
case NFS4_CREATE_UNCHECKED:
case NFS4_CREATE_GUARDED:
status = nfsd4_decode_fattr(argp, open->op_bmval,
- &open->op_iattr, &open->op_acl);
+ &open->op_iattr, &open->op_acl, &open->op_label);
if (status)
goto out;
break;
@@ -841,7 +881,7 @@
READ_BUF(NFS4_VERIFIER_SIZE);
COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
status = nfsd4_decode_fattr(argp, open->op_bmval,
- &open->op_iattr, &open->op_acl);
+ &open->op_iattr, &open->op_acl, &open->op_label);
if (status)
goto out;
break;
@@ -1063,7 +1103,7 @@
if (status)
return status;
return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
- &setattr->sa_acl);
+ &setattr->sa_acl, &setattr->sa_label);
}
static __be32
@@ -1567,6 +1607,7 @@
static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
[0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
[1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
+ [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
};
static __be32
@@ -1953,6 +1994,36 @@
FATTR4_WORD0_RDATTR_ERROR)
#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+static inline __be32
+nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
+{
+ __be32 *p = *pp;
+
+ if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4))
+ return nfserr_resource;
+
+ /*
+ * For now we use a 0 here to indicate the null translation; in
+ * the future we may place a call to translation code here.
+ */
+ if ((*buflen -= 8) < 0)
+ return nfserr_resource;
+
+ WRITE32(0); /* lfs */
+ WRITE32(0); /* pi */
+ p = xdr_encode_opaque(p, context, len);
+ *buflen -= (XDR_QUADLEN(len) << 2) + 4;
+
+ *pp = p;
+ return 0;
+}
+#else
+static inline __be32
+nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
+{ return 0; }
+#endif
+
static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
{
/* As per referral draft: */
@@ -2012,6 +2083,9 @@
int err;
int aclsupport = 0;
struct nfs4_acl *acl = NULL;
+ void *context = NULL;
+ int contextlen;
+ bool contextsupport = false;
struct nfsd4_compoundres *resp = rqstp->rq_resp;
u32 minorversion = resp->cstate.minorversion;
struct path path = {
@@ -2065,6 +2139,21 @@
}
}
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) ||
+ bmval[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
+ err = security_inode_getsecctx(dentry->d_inode,
+ &context, &contextlen);
+ contextsupport = (err == 0);
+ if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
+ if (err == -EOPNOTSUPP)
+ bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
+ else if (err)
+ goto out_nfserr;
+ }
+ }
+#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
+
if (bmval2) {
if ((buflen -= 16) < 0)
goto out_resource;
@@ -2093,6 +2182,8 @@
if (!aclsupport)
word0 &= ~FATTR4_WORD0_ACL;
+ if (!contextsupport)
+ word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
if (!word2) {
if ((buflen -= 12) < 0)
goto out_resource;
@@ -2400,6 +2491,12 @@
get_parent_attributes(exp, &stat);
WRITE64(stat.ino);
}
+ if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
+ status = nfsd4_encode_security_label(rqstp, context,
+ contextlen, &p, &buflen);
+ if (status)
+ goto out;
+ }
if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
WRITE32(3);
WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
@@ -2412,6 +2509,10 @@
status = nfs_ok;
out:
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+ if (context)
+ security_release_secctx(context, contextlen);
+#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
kfree(acl);
if (fhp == &tempfh)
fh_put(&tempfh);
@@ -3176,16 +3277,18 @@
{
__be32 *p;
- RESERVE_SPACE(12);
+ RESERVE_SPACE(16);
if (nfserr) {
- WRITE32(2);
+ WRITE32(3);
+ WRITE32(0);
WRITE32(0);
WRITE32(0);
}
else {
- WRITE32(2);
+ WRITE32(3);
WRITE32(setattr->sa_bmval[0]);
WRITE32(setattr->sa_bmval[1]);
+ WRITE32(setattr->sa_bmval[2]);
}
ADJUST_ARGS();
return nfserr;
@@ -3226,6 +3329,14 @@
return nfserr;
}
+static const u32 nfs4_minimal_spo_must_enforce[2] = {
+ [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
+ 1 << (OP_EXCHANGE_ID - 32) |
+ 1 << (OP_CREATE_SESSION - 32) |
+ 1 << (OP_DESTROY_SESSION - 32) |
+ 1 << (OP_DESTROY_CLIENTID - 32)
+};
+
static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_exchange_id *exid)
@@ -3264,6 +3375,20 @@
/* state_protect4_r. Currently only support SP4_NONE */
BUG_ON(exid->spa_how != SP4_NONE);
WRITE32(exid->spa_how);
+ switch (exid->spa_how) {
+ case SP4_NONE:
+ break;
+ case SP4_MACH_CRED:
+ /* spo_must_enforce bitmap: */
+ WRITE32(2);
+ WRITE32(nfs4_minimal_spo_must_enforce[0]);
+ WRITE32(nfs4_minimal_spo_must_enforce[1]);
+ /* empty spo_must_allow bitmap: */
+ WRITE32(0);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
/* The server_owner struct */
WRITE64(minor_id); /* Minor id */
@@ -3635,13 +3760,17 @@
iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
BUG_ON(iov->iov_len > PAGE_SIZE);
if (nfsd4_has_session(cs)) {
+ struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ struct nfs4_client *clp = cs->session->se_client;
if (cs->status != nfserr_replay_cache) {
nfsd4_store_cache_entry(resp);
cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
}
/* Renew the clientid on success and on replay */
- put_client_renew(cs->session->se_client);
+ spin_lock(&nn->client_lock);
nfsd4_put_session(cs->session);
+ spin_unlock(&nn->client_lock);
+ put_client_renew(clp);
}
return 1;
}
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index c0d9317..2bbd94e 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -24,7 +24,7 @@
/*
* nfsd version
*/
-#define NFSD_SUPPORTED_MINOR_VERSION 1
+#define NFSD_SUPPORTED_MINOR_VERSION 2
/*
* Maximum blocksizes supported by daemon under various circumstances.
*/
@@ -328,6 +328,13 @@
#define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
(NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
+ (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL)
+#else
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0
+#endif
+
static inline u32 nfsd_suppattrs0(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
@@ -342,8 +349,11 @@
static inline u32 nfsd_suppattrs2(u32 minorversion)
{
- return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2
- : NFSD4_SUPPORTED_ATTRS_WORD2;
+ switch (minorversion) {
+ default: return NFSD4_2_SUPPORTED_ATTRS_WORD2;
+ case 1: return NFSD4_1_SUPPORTED_ATTRS_WORD2;
+ case 0: return NFSD4_SUPPORTED_ATTRS_WORD2;
+ }
}
/* These will return ERR_INVAL if specified in GETATTR or READDIR. */
@@ -356,7 +366,11 @@
#define NFSD_WRITEABLE_ATTRS_WORD1 \
(FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
| FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL
+#else
#define NFSD_WRITEABLE_ATTRS_WORD2 0
+#endif
#define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
NFSD_WRITEABLE_ATTRS_WORD0
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 262df5c..6b9f48c 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -116,7 +116,7 @@
};
-u32 nfsd_supported_minorversion;
+u32 nfsd_supported_minorversion = 1;
int nfsd_vers(int vers, enum vers_op change)
{
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 274e2a1..424d8f5 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -246,6 +246,7 @@
nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */
struct sockaddr_storage cl_addr; /* client ipaddress */
+ bool cl_mach_cred; /* SP4_MACH_CRED in force */
struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index a6bc8a7..8ff6a00 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -28,6 +28,7 @@
#include <asm/uaccess.h>
#include <linux/exportfs.h>
#include <linux/writeback.h>
+#include <linux/security.h>
#ifdef CONFIG_NFSD_V3
#include "xdr3.h"
@@ -621,6 +622,33 @@
return 0;
return 1;
}
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct xdr_netobj *label)
+{
+ __be32 error;
+ int host_error;
+ struct dentry *dentry;
+
+ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
+ if (error)
+ return error;
+
+ dentry = fhp->fh_dentry;
+
+ mutex_lock(&dentry->d_inode->i_mutex);
+ host_error = security_inode_setsecctx(dentry, label->data, label->len);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+ return nfserrno(host_error);
+}
+#else
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct xdr_netobj *label)
+{
+ return nfserr_notsupp;
+}
+#endif
+
#endif /* defined(CONFIG_NFSD_V4) */
#ifdef CONFIG_NFSD_V3
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 5b58941..a4be2e3 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -39,7 +39,6 @@
typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int);
/* nfsd/vfs.c */
-int fh_lock_parent(struct svc_fh *, struct dentry *);
int nfsd_racache_init(int);
void nfsd_racache_shutdown(void);
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
@@ -56,6 +55,8 @@
__be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
struct nfs4_acl *);
int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **);
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
+ struct xdr_netobj *);
#endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
@@ -92,17 +93,13 @@
struct svc_fh *, char *, int);
__be32 nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type,
char *name, int len);
-int nfsd_truncate(struct svc_rqst *, struct svc_fh *,
- unsigned long size);
__be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *,
loff_t *, struct readdir_cd *, filldir_t);
__be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *,
struct kstatfs *, int access);
-int nfsd_notify_change(struct inode *, struct iattr *);
__be32 nfsd_permission(struct svc_rqst *, struct svc_export *,
struct dentry *, int);
-int nfsd_sync_dir(struct dentry *dp);
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 3b271d2..b3ed644 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -40,6 +40,7 @@
#include "state.h"
#include "nfsd.h"
+#define NFSD4_MAX_SEC_LABEL_LEN 2048
#define NFSD4_MAX_TAGLEN 128
#define XDR_LEN(n) (((n) + 3) & ~3)
@@ -118,6 +119,7 @@
struct iattr cr_iattr; /* request */
struct nfsd4_change_info cr_cinfo; /* response */
struct nfs4_acl *cr_acl;
+ struct xdr_netobj cr_label;
};
#define cr_linklen u.link.namelen
#define cr_linkname u.link.name
@@ -246,6 +248,7 @@
struct nfs4_file *op_file; /* used during processing */
struct nfs4_ol_stateid *op_stp; /* used during processing */
struct nfs4_acl *op_acl;
+ struct xdr_netobj op_label;
};
#define op_iattr iattr
@@ -330,6 +333,7 @@
u32 sa_bmval[3]; /* request */
struct iattr sa_iattr; /* request */
struct nfs4_acl *sa_acl;
+ struct xdr_netobj sa_label;
};
struct nfsd4_setclientid {
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index 31d3cd1..b800fbc 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -690,6 +690,8 @@
sf = (xfs_attr_shortform_t *)tmpbuffer;
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+ xfs_bmap_local_to_extents_empty(dp, XFS_ATTR_FORK);
+
bp = NULL;
error = xfs_da_grow_inode(args, &blkno);
if (error) {
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 8904284..05c698c 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -1161,6 +1161,24 @@
* since the file data needs to get logged so things will stay consistent.
* (The bmap-level manipulations are ok, though).
*/
+void
+xfs_bmap_local_to_extents_empty(
+ struct xfs_inode *ip,
+ int whichfork)
+{
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
+
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
+ ASSERT(ifp->if_bytes == 0);
+ ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
+
+ xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork);
+ ifp->if_flags &= ~XFS_IFINLINE;
+ ifp->if_flags |= XFS_IFEXTENTS;
+ XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
+}
+
+
STATIC int /* error */
xfs_bmap_local_to_extents(
xfs_trans_t *tp, /* transaction pointer */
@@ -1174,9 +1192,12 @@
struct xfs_inode *ip,
struct xfs_ifork *ifp))
{
- int error; /* error return value */
+ int error = 0;
int flags; /* logging flags returned */
xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_alloc_arg_t args; /* allocation arguments */
+ xfs_buf_t *bp; /* buffer for extent block */
+ xfs_bmbt_rec_host_t *ep; /* extent record pointer */
/*
* We don't want to deal with the case of keeping inode data inline yet.
@@ -1185,68 +1206,65 @@
ASSERT(!(S_ISREG(ip->i_d.di_mode) && whichfork == XFS_DATA_FORK));
ifp = XFS_IFORK_PTR(ip, whichfork);
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
+
+ if (!ifp->if_bytes) {
+ xfs_bmap_local_to_extents_empty(ip, whichfork);
+ flags = XFS_ILOG_CORE;
+ goto done;
+ }
+
flags = 0;
error = 0;
- if (ifp->if_bytes) {
- xfs_alloc_arg_t args; /* allocation arguments */
- xfs_buf_t *bp; /* buffer for extent block */
- xfs_bmbt_rec_host_t *ep;/* extent record pointer */
-
- ASSERT((ifp->if_flags &
- (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE);
- memset(&args, 0, sizeof(args));
- args.tp = tp;
- args.mp = ip->i_mount;
- args.firstblock = *firstblock;
- /*
- * Allocate a block. We know we need only one, since the
- * file currently fits in an inode.
- */
- if (*firstblock == NULLFSBLOCK) {
- args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
- args.type = XFS_ALLOCTYPE_START_BNO;
- } else {
- args.fsbno = *firstblock;
- args.type = XFS_ALLOCTYPE_NEAR_BNO;
- }
- args.total = total;
- args.minlen = args.maxlen = args.prod = 1;
- error = xfs_alloc_vextent(&args);
- if (error)
- goto done;
-
- /* Can't fail, the space was reserved. */
- ASSERT(args.fsbno != NULLFSBLOCK);
- ASSERT(args.len == 1);
- *firstblock = args.fsbno;
- bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
-
- /* initialise the block and copy the data */
- init_fn(tp, bp, ip, ifp);
-
- /* account for the change in fork size and log everything */
- xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
- xfs_bmap_forkoff_reset(args.mp, ip, whichfork);
- xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
- xfs_iext_add(ifp, 0, 1);
- ep = xfs_iext_get_ext(ifp, 0);
- xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM);
- trace_xfs_bmap_post_update(ip, 0,
- whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0,
- _THIS_IP_);
- XFS_IFORK_NEXT_SET(ip, whichfork, 1);
- ip->i_d.di_nblocks = 1;
- xfs_trans_mod_dquot_byino(tp, ip,
- XFS_TRANS_DQ_BCOUNT, 1L);
- flags |= xfs_ilog_fext(whichfork);
+ ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) ==
+ XFS_IFINLINE);
+ memset(&args, 0, sizeof(args));
+ args.tp = tp;
+ args.mp = ip->i_mount;
+ args.firstblock = *firstblock;
+ /*
+ * Allocate a block. We know we need only one, since the
+ * file currently fits in an inode.
+ */
+ if (*firstblock == NULLFSBLOCK) {
+ args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
+ args.type = XFS_ALLOCTYPE_START_BNO;
} else {
- ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
- xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork);
+ args.fsbno = *firstblock;
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
}
- ifp->if_flags &= ~XFS_IFINLINE;
- ifp->if_flags |= XFS_IFEXTENTS;
- XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
+ args.total = total;
+ args.minlen = args.maxlen = args.prod = 1;
+ error = xfs_alloc_vextent(&args);
+ if (error)
+ goto done;
+
+ /* Can't fail, the space was reserved. */
+ ASSERT(args.fsbno != NULLFSBLOCK);
+ ASSERT(args.len == 1);
+ *firstblock = args.fsbno;
+ bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
+
+ /* initialise the block and copy the data */
+ init_fn(tp, bp, ip, ifp);
+
+ /* account for the change in fork size and log everything */
+ xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
+ xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
+ xfs_bmap_local_to_extents_empty(ip, whichfork);
flags |= XFS_ILOG_CORE;
+
+ xfs_iext_add(ifp, 0, 1);
+ ep = xfs_iext_get_ext(ifp, 0);
+ xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM);
+ trace_xfs_bmap_post_update(ip, 0,
+ whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0,
+ _THIS_IP_);
+ XFS_IFORK_NEXT_SET(ip, whichfork, 1);
+ ip->i_d.di_nblocks = 1;
+ xfs_trans_mod_dquot_byino(tp, ip,
+ XFS_TRANS_DQ_BCOUNT, 1L);
+ flags |= xfs_ilog_fext(whichfork);
+
done:
*logflagsp = flags;
return error;
@@ -1323,25 +1341,6 @@
}
/*
- * Block initialisation function for local to extent format conversion.
- *
- * This shouldn't actually be called by anyone, so make sure debug kernels cause
- * a noticable failure.
- */
-STATIC void
-xfs_bmap_local_to_extents_init_fn(
- struct xfs_trans *tp,
- struct xfs_buf *bp,
- struct xfs_inode *ip,
- struct xfs_ifork *ifp)
-{
- ASSERT(0);
- bp->b_ops = &xfs_bmbt_buf_ops;
- memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
- xfs_trans_buf_set_type(tp, bp, XFS_BLFT_BTREE_BUF);
-}
-
-/*
* Called from xfs_bmap_add_attrfork to handle local format files. Each
* different data fork content type needs a different callout to do the
* conversion. Some are basic and only require special block initialisation
@@ -1381,9 +1380,9 @@
flags, XFS_DATA_FORK,
xfs_symlink_local_to_remote);
- return xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
- XFS_DATA_FORK,
- xfs_bmap_local_to_extents_init_fn);
+ /* should only be called for types that support local format data */
+ ASSERT(0);
+ return EFSCORRUPTED;
}
/*
@@ -4907,20 +4906,19 @@
orig_mval = mval;
orig_nmap = *nmap;
#endif
+ whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
+ XFS_ATTR_FORK : XFS_DATA_FORK;
ASSERT(*nmap >= 1);
ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
ASSERT(!(flags & XFS_BMAPI_IGSTATE));
ASSERT(tp != NULL);
ASSERT(len > 0);
-
- whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
- XFS_ATTR_FORK : XFS_DATA_FORK;
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
if (unlikely(XFS_TEST_ERROR(
(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
- XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
- XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL),
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
XFS_ERROR_REPORT("xfs_bmapi_write", XFS_ERRLEVEL_LOW, mp);
return XFS_ERROR(EFSCORRUPTED);
@@ -4933,37 +4931,6 @@
XFS_STATS_INC(xs_blk_mapw);
- if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
- /*
- * XXX (dgc): This assumes we are only called for inodes that
- * contain content neutral data in local format. Anything that
- * contains caller-specific data in local format that needs
- * transformation to move to a block format needs to do the
- * conversion to extent format itself.
- *
- * Directory data forks and attribute forks handle this
- * themselves, but with the addition of metadata verifiers every
- * data fork in local format now contains caller specific data
- * and as such conversion through this function is likely to be
- * broken.
- *
- * The only likely user of this branch is for remote symlinks,
- * but we cannot overwrite the data fork contents of the symlink
- * (EEXIST occurs higher up the stack) and so it will never go
- * from local format to extent format here. Hence I don't think
- * this branch is ever executed intentionally and we should
- * consider removing it and asserting that xfs_bmapi_write()
- * cannot be called directly on local format forks. i.e. callers
- * are completely responsible for local to extent format
- * conversion, not xfs_bmapi_write().
- */
- error = xfs_bmap_local_to_extents(tp, ip, firstblock, total,
- &bma.logflags, whichfork,
- xfs_bmap_local_to_extents_init_fn);
- if (error)
- goto error0;
- }
-
if (*firstblock == NULLFSBLOCK) {
if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE)
bma.minleft = be16_to_cpu(ifp->if_broot->bb_level) + 1;
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
index 5f469c3..1cf1292 100644
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -172,6 +172,7 @@
#endif
int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
+void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork);
void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len,
struct xfs_bmap_free *flist, struct xfs_mount *mp);
void xfs_bmap_cancel(struct xfs_bmap_free *flist);
diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h
index f7a0e95..07d735a 100644
--- a/fs/xfs/xfs_dinode.h
+++ b/fs/xfs/xfs_dinode.h
@@ -132,9 +132,6 @@
#define XFS_LITINO(mp, version) \
((int)(((mp)->m_sb.sb_inodesize) - xfs_dinode_size(version)))
-#define XFS_BROOT_SIZE_ADJ(ip) \
- (XFS_BMBT_BLOCK_LEN((ip)->i_mount) - sizeof(xfs_bmdr_block_t))
-
/*
* Inode data & attribute fork sizes, per inode.
*/
diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c
index 09aea02..5e7fbd7 100644
--- a/fs/xfs/xfs_dir2_block.c
+++ b/fs/xfs/xfs_dir2_block.c
@@ -29,6 +29,7 @@
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
+#include "xfs_bmap.h"
#include "xfs_buf_item.h"
#include "xfs_dir2.h"
#include "xfs_dir2_format.h"
@@ -1164,13 +1165,15 @@
__be16 *tagp; /* end of data entry */
xfs_trans_t *tp; /* transaction pointer */
struct xfs_name name;
+ struct xfs_ifork *ifp;
trace_xfs_dir2_sf_to_block(args);
dp = args->dp;
tp = args->trans;
mp = dp->i_mount;
- ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ ifp = XFS_IFORK_PTR(dp, XFS_DATA_FORK);
+ ASSERT(ifp->if_flags & XFS_IFINLINE);
/*
* Bomb out if the shortform directory is way too short.
*/
@@ -1179,22 +1182,23 @@
return XFS_ERROR(EIO);
}
- oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
+ oldsfp = (xfs_dir2_sf_hdr_t *)ifp->if_u1.if_data;
- ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
- ASSERT(dp->i_df.if_u1.if_data != NULL);
+ ASSERT(ifp->if_bytes == dp->i_d.di_size);
+ ASSERT(ifp->if_u1.if_data != NULL);
ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(oldsfp->i8count));
+ ASSERT(dp->i_d.di_nextents == 0);
/*
* Copy the directory into a temporary buffer.
* Then pitch the incore inode data so we can make extents.
*/
- sfp = kmem_alloc(dp->i_df.if_bytes, KM_SLEEP);
- memcpy(sfp, oldsfp, dp->i_df.if_bytes);
+ sfp = kmem_alloc(ifp->if_bytes, KM_SLEEP);
+ memcpy(sfp, oldsfp, ifp->if_bytes);
- xfs_idata_realloc(dp, -dp->i_df.if_bytes, XFS_DATA_FORK);
+ xfs_idata_realloc(dp, -ifp->if_bytes, XFS_DATA_FORK);
+ xfs_bmap_local_to_extents_empty(dp, XFS_DATA_FORK);
dp->i_d.di_size = 0;
- xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
/*
* Add block 0 to the inode.
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index f01012d..0adf27e 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -936,6 +936,7 @@
{
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
struct xfs_dquot *gdqp;
+ struct xfs_dquot *pdqp;
trace_xfs_dqput_free(dqp);
@@ -949,21 +950,29 @@
/*
* If we just added a udquot to the freelist, then we want to release
- * the gdquot reference that it (probably) has. Otherwise it'll keep
- * the gdquot from getting reclaimed.
+ * the gdquot/pdquot reference that it (probably) has. Otherwise it'll
+ * keep the gdquot/pdquot from getting reclaimed.
*/
gdqp = dqp->q_gdquot;
if (gdqp) {
xfs_dqlock(gdqp);
dqp->q_gdquot = NULL;
}
+
+ pdqp = dqp->q_pdquot;
+ if (pdqp) {
+ xfs_dqlock(pdqp);
+ dqp->q_pdquot = NULL;
+ }
xfs_dqunlock(dqp);
/*
- * If we had a group quota hint, release it now.
+ * If we had a group/project quota hint, release it now.
*/
if (gdqp)
xfs_qm_dqput(gdqp);
+ if (pdqp)
+ xfs_qm_dqput(pdqp);
}
/*
diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h
index b596626..55abbca 100644
--- a/fs/xfs/xfs_dquot.h
+++ b/fs/xfs/xfs_dquot.h
@@ -53,6 +53,7 @@
xfs_fileoff_t q_fileoffset; /* offset in quotas file */
struct xfs_dquot*q_gdquot; /* group dquot, hint only */
+ struct xfs_dquot*q_pdquot; /* project dquot, hint only */
xfs_disk_dquot_t q_core; /* actual usage & quotas */
xfs_dq_logitem_t q_logitem; /* dquot log item */
xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
@@ -118,8 +119,9 @@
case XFS_DQ_USER:
return XFS_IS_UQUOTA_ON(mp);
case XFS_DQ_GROUP:
+ return XFS_IS_GQUOTA_ON(mp);
case XFS_DQ_PROJ:
- return XFS_IS_OQUOTA_ON(mp);
+ return XFS_IS_PQUOTA_ON(mp);
default:
return 0;
}
@@ -131,8 +133,9 @@
case XFS_DQ_USER:
return ip->i_udquot;
case XFS_DQ_GROUP:
- case XFS_DQ_PROJ:
return ip->i_gdquot;
+ case XFS_DQ_PROJ:
+ return ip->i_pdquot;
default:
return NULL;
}
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 9560dc1f..3f90e1c 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -337,6 +337,7 @@
iflags |= XFS_IDONTCACHE;
ip->i_udquot = NULL;
ip->i_gdquot = NULL;
+ ip->i_pdquot = NULL;
xfs_iflags_set(ip, iflags);
/* insert the new inode */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 9ecfe1e..b78481f 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2156,8 +2156,8 @@
np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
(int)new_size);
ifp->if_broot_bytes = (int)new_size;
- ASSERT(ifp->if_broot_bytes <=
- XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ(ip));
+ ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
+ XFS_IFORK_SIZE(ip, whichfork));
memmove(np, op, cur_max * (uint)sizeof(xfs_dfsbno_t));
return;
}
@@ -2210,8 +2210,9 @@
kmem_free(ifp->if_broot);
ifp->if_broot = new_broot;
ifp->if_broot_bytes = (int)new_size;
- ASSERT(ifp->if_broot_bytes <=
- XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ(ip));
+ if (ifp->if_broot)
+ ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
+ XFS_IFORK_SIZE(ip, whichfork));
return;
}
@@ -2522,9 +2523,8 @@
if ((iip->ili_fields & brootflag[whichfork]) &&
(ifp->if_broot_bytes > 0)) {
ASSERT(ifp->if_broot != NULL);
- ASSERT(ifp->if_broot_bytes <=
- (XFS_IFORK_SIZE(ip, whichfork) +
- XFS_BROOT_SIZE_ADJ(ip)));
+ ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
+ XFS_IFORK_SIZE(ip, whichfork));
xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes,
(xfs_bmdr_block_t *)cp,
XFS_DFORK_SIZE(dip, mp, whichfork));
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 9112979..b55fd34 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -250,6 +250,7 @@
struct xfs_mount *i_mount; /* fs mount struct ptr */
struct xfs_dquot *i_udquot; /* user dquot */
struct xfs_dquot *i_gdquot; /* group dquot */
+ struct xfs_dquot *i_pdquot; /* project dquot */
/* Inode location stuff */
xfs_ino_t i_ino; /* inode number (agno/agino)*/
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 5e99968..6e2bca5 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -248,7 +248,7 @@
goto out_dput;
}
- fd = get_unused_fd();
+ fd = get_unused_fd_flags(0);
if (fd < 0) {
error = fd;
goto out_dput;
@@ -928,7 +928,7 @@
struct xfs_trans *tp;
unsigned int lock_flags = 0;
struct xfs_dquot *udqp = NULL;
- struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
struct xfs_dquot *olddquot = NULL;
int code;
@@ -957,7 +957,7 @@
if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
ip->i_d.di_gid, fa->fsx_projid,
- XFS_QMOPT_PQUOTA, &udqp, &gdqp);
+ XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
if (code)
return code;
}
@@ -994,8 +994,8 @@
XFS_IS_PQUOTA_ON(mp) &&
xfs_get_projid(ip) != fa->fsx_projid) {
ASSERT(tp);
- code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
- capable(CAP_FOWNER) ?
+ code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
+ pdqp, capable(CAP_FOWNER) ?
XFS_QMOPT_FORCE_RES : 0);
if (code) /* out of quota */
goto error_return;
@@ -1113,7 +1113,7 @@
if (xfs_get_projid(ip) != fa->fsx_projid) {
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
olddquot = xfs_qm_vop_chown(tp, ip,
- &ip->i_gdquot, gdqp);
+ &ip->i_pdquot, pdqp);
}
xfs_set_projid(ip, fa->fsx_projid);
@@ -1160,13 +1160,13 @@
*/
xfs_qm_dqrele(olddquot);
xfs_qm_dqrele(udqp);
- xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
return code;
error_return:
xfs_qm_dqrele(udqp);
- xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
xfs_trans_cancel(tp, 0);
if (lock_flags)
xfs_iunlock(ip, lock_flags);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index c69bbc4..96dda62 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -467,9 +467,6 @@
ASSERT(tp);
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
- if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
- mode &= ~S_ISGID;
-
ip->i_d.di_mode &= S_IFMT;
ip->i_d.di_mode |= mode & ~S_IFMT;
@@ -495,15 +492,18 @@
trace_xfs_setattr(ip);
- if (mp->m_flags & XFS_MOUNT_RDONLY)
- return XFS_ERROR(EROFS);
+ /* If acls are being inherited, we already have this checked */
+ if (!(flags & XFS_ATTR_NOACL)) {
+ if (mp->m_flags & XFS_MOUNT_RDONLY)
+ return XFS_ERROR(EROFS);
- if (XFS_FORCED_SHUTDOWN(mp))
- return XFS_ERROR(EIO);
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
- error = -inode_change_ok(inode, iattr);
- if (error)
- return XFS_ERROR(error);
+ error = -inode_change_ok(inode, iattr);
+ if (error)
+ return XFS_ERROR(error);
+ }
ASSERT((mask & ATTR_SIZE) == 0);
@@ -539,7 +539,7 @@
ASSERT(udqp == NULL);
ASSERT(gdqp == NULL);
error = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip),
- qflags, &udqp, &gdqp);
+ qflags, &udqp, &gdqp, NULL);
if (error)
return error;
}
@@ -575,7 +575,7 @@
(XFS_IS_GQUOTA_ON(mp) && igid != gid))) {
ASSERT(tp);
error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
- capable(CAP_FOWNER) ?
+ NULL, capable(CAP_FOWNER) ?
XFS_QMOPT_FORCE_RES : 0);
if (error) /* out of quota */
goto out_trans_cancel;
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index bc92c53..b93e14b 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -221,7 +221,6 @@
char __user *ubufp; /* pointer into user's buffer */
int ubelem; /* spaces used in user's buffer */
int ubused; /* bytes used by formatter */
- xfs_buf_t *bp; /* ptr to on-disk inode cluster buf */
/*
* Get the last inode value, see if there's nothing to do.
@@ -263,7 +262,6 @@
rval = 0;
while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
cond_resched();
- bp = NULL;
error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
if (error) {
/*
@@ -436,27 +434,7 @@
irbp->ir_freecount < XFS_INODES_PER_CHUNK;
chunkidx++, clustidx++, agino++) {
ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
- /*
- * Recompute agbno if this is the
- * first inode of the cluster.
- *
- * Careful with clustidx. There can be
- * multiple clusters per chunk, a single
- * cluster per chunk or a cluster that has
- * inodes represented from several different
- * chunks (if blocksize is large).
- *
- * Because of this, the starting clustidx is
- * initialized to zero in this loop but must
- * later be reset after reading in the cluster
- * buffer.
- */
- if ((chunkidx & (nicluster - 1)) == 0) {
- agbno = XFS_AGINO_TO_AGBNO(mp,
- irbp->ir_startino) +
- ((chunkidx & nimask) >>
- mp->m_sb.sb_inopblog);
- }
+
ino = XFS_AGINO_TO_INO(mp, agno, agino);
/*
* Skip if this inode is free.
@@ -502,10 +480,6 @@
cond_resched();
}
-
- if (bp)
- xfs_buf_relse(bp);
-
/*
* Set up for the next loop iteration.
*/
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 7a3e007..d320794 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -137,6 +137,7 @@
struct xfs_mount *mp = dqp->q_mount;
struct xfs_quotainfo *qi = mp->m_quotainfo;
struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
xfs_dqlock(dqp);
if ((dqp->dq_flags & XFS_DQ_FREEING) || dqp->q_nrefs != 0) {
@@ -145,8 +146,7 @@
}
/*
- * If this quota has a group hint attached, prepare for releasing it
- * now.
+ * If this quota has a hint attached, prepare for releasing it now.
*/
gdqp = dqp->q_gdquot;
if (gdqp) {
@@ -154,6 +154,12 @@
dqp->q_gdquot = NULL;
}
+ pdqp = dqp->q_pdquot;
+ if (pdqp) {
+ xfs_dqlock(pdqp);
+ dqp->q_pdquot = NULL;
+ }
+
dqp->dq_flags |= XFS_DQ_FREEING;
xfs_dqflock(dqp);
@@ -208,6 +214,8 @@
if (gdqp)
xfs_qm_dqput(gdqp);
+ if (pdqp)
+ xfs_qm_dqput(pdqp);
return 0;
}
@@ -364,6 +372,10 @@
IRELE(mp->m_quotainfo->qi_gquotaip);
mp->m_quotainfo->qi_gquotaip = NULL;
}
+ if (mp->m_quotainfo->qi_pquotaip) {
+ IRELE(mp->m_quotainfo->qi_pquotaip);
+ mp->m_quotainfo->qi_pquotaip = NULL;
+ }
}
}
@@ -410,7 +422,10 @@
* be reclaimed as long as we have a ref from inode and we
* hold the ilock.
*/
- dqp = udqhint->q_gdquot;
+ if (type == XFS_DQ_GROUP)
+ dqp = udqhint->q_gdquot;
+ else
+ dqp = udqhint->q_pdquot;
if (dqp && be32_to_cpu(dqp->q_core.d_id) == id) {
ASSERT(*IO_idqpp == NULL);
@@ -453,28 +468,42 @@
/*
- * Given a udquot and gdquot, attach a ptr to the group dquot in the
- * udquot as a hint for future lookups.
+ * Given a udquot and group/project type, attach the group/project
+ * dquot pointer to the udquot as a hint for future lookups.
*/
STATIC void
-xfs_qm_dqattach_grouphint(
- xfs_dquot_t *udq,
- xfs_dquot_t *gdq)
+xfs_qm_dqattach_hint(
+ struct xfs_inode *ip,
+ int type)
{
- xfs_dquot_t *tmp;
+ struct xfs_dquot **dqhintp;
+ struct xfs_dquot *dqp;
+ struct xfs_dquot *udq = ip->i_udquot;
+
+ ASSERT(type == XFS_DQ_GROUP || type == XFS_DQ_PROJ);
xfs_dqlock(udq);
- tmp = udq->q_gdquot;
- if (tmp) {
- if (tmp == gdq)
+ if (type == XFS_DQ_GROUP) {
+ dqp = ip->i_gdquot;
+ dqhintp = &udq->q_gdquot;
+ } else {
+ dqp = ip->i_pdquot;
+ dqhintp = &udq->q_pdquot;
+ }
+
+ if (*dqhintp) {
+ struct xfs_dquot *tmp;
+
+ if (*dqhintp == dqp)
goto done;
- udq->q_gdquot = NULL;
+ tmp = *dqhintp;
+ *dqhintp = NULL;
xfs_qm_dqrele(tmp);
}
- udq->q_gdquot = xfs_qm_dqhold(gdq);
+ *dqhintp = xfs_qm_dqhold(dqp);
done:
xfs_dqunlock(udq);
}
@@ -527,12 +556,8 @@
}
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
- if (XFS_IS_OQUOTA_ON(mp)) {
- error = XFS_IS_GQUOTA_ON(mp) ?
- xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
- flags & XFS_QMOPT_DQALLOC,
- ip->i_udquot, &ip->i_gdquot) :
- xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
flags & XFS_QMOPT_DQALLOC,
ip->i_udquot, &ip->i_gdquot);
/*
@@ -544,14 +569,28 @@
nquotas++;
}
+ ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+ if (XFS_IS_PQUOTA_ON(mp)) {
+ error = xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
+ flags & XFS_QMOPT_DQALLOC,
+ ip->i_udquot, &ip->i_pdquot);
+ /*
+ * Don't worry about the udquot that we may have
+ * attached above. It'll get detached, if not already.
+ */
+ if (error)
+ goto done;
+ nquotas++;
+ }
+
/*
- * Attach this group quota to the user quota as a hint.
+ * Attach this group/project quota to the user quota as a hint.
* This WON'T, in general, result in a thrash.
*/
- if (nquotas == 2) {
+ if (nquotas > 1 && ip->i_udquot) {
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
- ASSERT(ip->i_udquot);
- ASSERT(ip->i_gdquot);
+ ASSERT(ip->i_gdquot || !XFS_IS_GQUOTA_ON(mp));
+ ASSERT(ip->i_pdquot || !XFS_IS_PQUOTA_ON(mp));
/*
* We do not have i_udquot locked at this point, but this check
@@ -560,7 +599,10 @@
* succeed in general.
*/
if (ip->i_udquot->q_gdquot != ip->i_gdquot)
- xfs_qm_dqattach_grouphint(ip->i_udquot, ip->i_gdquot);
+ xfs_qm_dqattach_hint(ip, XFS_DQ_GROUP);
+
+ if (ip->i_udquot->q_pdquot != ip->i_pdquot)
+ xfs_qm_dqattach_hint(ip, XFS_DQ_PROJ);
}
done:
@@ -568,8 +610,10 @@
if (!error) {
if (XFS_IS_UQUOTA_ON(mp))
ASSERT(ip->i_udquot);
- if (XFS_IS_OQUOTA_ON(mp))
+ if (XFS_IS_GQUOTA_ON(mp))
ASSERT(ip->i_gdquot);
+ if (XFS_IS_PQUOTA_ON(mp))
+ ASSERT(ip->i_pdquot);
}
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
#endif
@@ -602,7 +646,7 @@
xfs_qm_dqdetach(
xfs_inode_t *ip)
{
- if (!(ip->i_udquot || ip->i_gdquot))
+ if (!(ip->i_udquot || ip->i_gdquot || ip->i_pdquot))
return;
trace_xfs_dquot_dqdetach(ip);
@@ -616,6 +660,10 @@
xfs_qm_dqrele(ip->i_gdquot);
ip->i_gdquot = NULL;
}
+ if (ip->i_pdquot) {
+ xfs_qm_dqrele(ip->i_pdquot);
+ ip->i_pdquot = NULL;
+ }
}
int
@@ -660,6 +708,7 @@
INIT_RADIX_TREE(&qinf->qi_uquota_tree, GFP_NOFS);
INIT_RADIX_TREE(&qinf->qi_gquota_tree, GFP_NOFS);
+ INIT_RADIX_TREE(&qinf->qi_pquota_tree, GFP_NOFS);
mutex_init(&qinf->qi_tree_lock);
INIT_LIST_HEAD(&qinf->qi_lru_list);
@@ -761,6 +810,10 @@
IRELE(qi->qi_gquotaip);
qi->qi_gquotaip = NULL;
}
+ if (qi->qi_pquotaip) {
+ IRELE(qi->qi_pquotaip);
+ qi->qi_pquotaip = NULL;
+ }
mutex_destroy(&qi->qi_quotaofflock);
kmem_free(qi);
mp->m_quotainfo = NULL;
@@ -1269,13 +1322,14 @@
LIST_HEAD (buffer_list);
struct xfs_inode *uip = mp->m_quotainfo->qi_uquotaip;
struct xfs_inode *gip = mp->m_quotainfo->qi_gquotaip;
+ struct xfs_inode *pip = mp->m_quotainfo->qi_pquotaip;
count = INT_MAX;
structsz = 1;
lastino = 0;
flags = 0;
- ASSERT(uip || gip);
+ ASSERT(uip || gip || pip);
ASSERT(XFS_IS_QUOTA_RUNNING(mp));
xfs_notice(mp, "Quotacheck needed: Please wait.");
@@ -1294,13 +1348,19 @@
}
if (gip) {
- error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ?
- XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA,
+ error = xfs_qm_dqiterate(mp, gip, XFS_QMOPT_GQUOTA,
&buffer_list);
if (error)
goto error_return;
- flags |= XFS_IS_GQUOTA_ON(mp) ?
- XFS_GQUOTA_CHKD : XFS_PQUOTA_CHKD;
+ flags |= XFS_GQUOTA_CHKD;
+ }
+
+ if (pip) {
+ error = xfs_qm_dqiterate(mp, pip, XFS_QMOPT_PQUOTA,
+ &buffer_list);
+ if (error)
+ goto error_return;
+ flags |= XFS_PQUOTA_CHKD;
}
do {
@@ -1397,6 +1457,7 @@
{
struct xfs_inode *uip = NULL;
struct xfs_inode *gip = NULL;
+ struct xfs_inode *pip = NULL;
int error;
__int64_t sbflags = 0;
uint flags = 0;
@@ -1415,7 +1476,7 @@
if (error)
return XFS_ERROR(error);
}
- if (XFS_IS_OQUOTA_ON(mp) &&
+ if (XFS_IS_GQUOTA_ON(mp) &&
mp->m_sb.sb_gquotino != NULLFSINO) {
ASSERT(mp->m_sb.sb_gquotino > 0);
error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
@@ -1423,6 +1484,15 @@
if (error)
goto error_rele;
}
+ /* XXX: Use gquotino for now */
+ if (XFS_IS_PQUOTA_ON(mp) &&
+ mp->m_sb.sb_gquotino != NULLFSINO) {
+ ASSERT(mp->m_sb.sb_gquotino > 0);
+ error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
+ 0, 0, &pip);
+ if (error)
+ goto error_rele;
+ }
} else {
flags |= XFS_QMOPT_SBVERSION;
sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
@@ -1430,7 +1500,7 @@
}
/*
- * Create the two inodes, if they don't exist already. The changes
+ * Create the three inodes, if they don't exist already. The changes
* made above will get added to a transaction and logged in one of
* the qino_alloc calls below. If the device is readonly,
* temporarily switch to read-write to do this.
@@ -1444,17 +1514,27 @@
flags &= ~XFS_QMOPT_SBVERSION;
}
- if (XFS_IS_OQUOTA_ON(mp) && gip == NULL) {
- flags |= (XFS_IS_GQUOTA_ON(mp) ?
- XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA);
+ if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) {
error = xfs_qm_qino_alloc(mp, &gip,
- sbflags | XFS_SB_GQUOTINO, flags);
+ sbflags | XFS_SB_GQUOTINO,
+ flags | XFS_QMOPT_GQUOTA);
+ if (error)
+ goto error_rele;
+
+ flags &= ~XFS_QMOPT_SBVERSION;
+ }
+ if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) {
+ /* XXX: Use XFS_SB_GQUOTINO for now */
+ error = xfs_qm_qino_alloc(mp, &pip,
+ sbflags | XFS_SB_GQUOTINO,
+ flags | XFS_QMOPT_PQUOTA);
if (error)
goto error_rele;
}
mp->m_quotainfo->qi_uquotaip = uip;
mp->m_quotainfo->qi_gquotaip = gip;
+ mp->m_quotainfo->qi_pquotaip = pip;
return 0;
@@ -1463,6 +1543,8 @@
IRELE(uip);
if (gip)
IRELE(gip);
+ if (pip)
+ IRELE(pip);
return XFS_ERROR(error);
}
@@ -1657,11 +1739,13 @@
prid_t prid,
uint flags,
struct xfs_dquot **O_udqpp,
- struct xfs_dquot **O_gdqpp)
+ struct xfs_dquot **O_gdqpp,
+ struct xfs_dquot **O_pdqpp)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_dquot *uq = NULL;
struct xfs_dquot *gq = NULL;
+ struct xfs_dquot *pq = NULL;
int error;
uint lockflags;
@@ -1741,24 +1825,25 @@
ASSERT(ip->i_gdquot);
gq = xfs_qm_dqhold(ip->i_gdquot);
}
- } else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
+ }
+ if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
if (xfs_get_projid(ip) != prid) {
xfs_iunlock(ip, lockflags);
error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid,
XFS_DQ_PROJ,
XFS_QMOPT_DQALLOC |
XFS_QMOPT_DOWARN,
- &gq);
+ &pq);
if (error) {
ASSERT(error != ENOENT);
goto error_rele;
}
- xfs_dqunlock(gq);
+ xfs_dqunlock(pq);
lockflags = XFS_ILOCK_SHARED;
xfs_ilock(ip, lockflags);
} else {
- ASSERT(ip->i_gdquot);
- gq = xfs_qm_dqhold(ip->i_gdquot);
+ ASSERT(ip->i_pdquot);
+ pq = xfs_qm_dqhold(ip->i_pdquot);
}
}
if (uq)
@@ -1773,9 +1858,15 @@
*O_gdqpp = gq;
else if (gq)
xfs_qm_dqrele(gq);
+ if (O_pdqpp)
+ *O_pdqpp = pq;
+ else if (pq)
+ xfs_qm_dqrele(pq);
return 0;
error_rele:
+ if (gq)
+ xfs_qm_dqrele(gq);
if (uq)
xfs_qm_dqrele(uq);
return error;
@@ -1830,14 +1921,17 @@
struct xfs_inode *ip,
struct xfs_dquot *udqp,
struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp,
uint flags)
{
struct xfs_mount *mp = ip->i_mount;
uint delblks, blkflags, prjflags = 0;
struct xfs_dquot *udq_unres = NULL;
struct xfs_dquot *gdq_unres = NULL;
+ struct xfs_dquot *pdq_unres = NULL;
struct xfs_dquot *udq_delblks = NULL;
struct xfs_dquot *gdq_delblks = NULL;
+ struct xfs_dquot *pdq_delblks = NULL;
int error;
@@ -1861,24 +1955,28 @@
udq_unres = ip->i_udquot;
}
}
- if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) {
- if (XFS_IS_PQUOTA_ON(ip->i_mount) &&
- xfs_get_projid(ip) != be32_to_cpu(gdqp->q_core.d_id))
- prjflags = XFS_QMOPT_ENOSPC;
+ if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp &&
+ ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id)) {
+ gdq_delblks = gdqp;
+ if (delblks) {
+ ASSERT(ip->i_gdquot);
+ gdq_unres = ip->i_gdquot;
+ }
+ }
- if (prjflags ||
- (XFS_IS_GQUOTA_ON(ip->i_mount) &&
- ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) {
- gdq_delblks = gdqp;
- if (delblks) {
- ASSERT(ip->i_gdquot);
- gdq_unres = ip->i_gdquot;
- }
+ if (XFS_IS_PQUOTA_ON(ip->i_mount) && pdqp &&
+ xfs_get_projid(ip) != be32_to_cpu(pdqp->q_core.d_id)) {
+ prjflags = XFS_QMOPT_ENOSPC;
+ pdq_delblks = pdqp;
+ if (delblks) {
+ ASSERT(ip->i_pdquot);
+ pdq_unres = ip->i_pdquot;
}
}
error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
- udq_delblks, gdq_delblks, ip->i_d.di_nblocks, 1,
+ udq_delblks, gdq_delblks, pdq_delblks,
+ ip->i_d.di_nblocks, 1,
flags | blkflags | prjflags);
if (error)
return error;
@@ -1893,16 +1991,17 @@
/*
* Do the reservations first. Unreservation can't fail.
*/
- ASSERT(udq_delblks || gdq_delblks);
- ASSERT(udq_unres || gdq_unres);
+ ASSERT(udq_delblks || gdq_delblks || pdq_delblks);
+ ASSERT(udq_unres || gdq_unres || pdq_unres);
error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
- udq_delblks, gdq_delblks, (xfs_qcnt_t)delblks, 0,
+ udq_delblks, gdq_delblks, pdq_delblks,
+ (xfs_qcnt_t)delblks, 0,
flags | blkflags | prjflags);
if (error)
return error;
xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
- udq_unres, gdq_unres, -((xfs_qcnt_t)delblks), 0,
- blkflags);
+ udq_unres, gdq_unres, pdq_unres,
+ -((xfs_qcnt_t)delblks), 0, blkflags);
}
return (0);
@@ -1941,7 +2040,8 @@
struct xfs_trans *tp,
struct xfs_inode *ip,
struct xfs_dquot *udqp,
- struct xfs_dquot *gdqp)
+ struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp)
{
struct xfs_mount *mp = tp->t_mountp;
@@ -1961,13 +2061,18 @@
}
if (gdqp) {
ASSERT(ip->i_gdquot == NULL);
- ASSERT(XFS_IS_OQUOTA_ON(mp));
- ASSERT((XFS_IS_GQUOTA_ON(mp) ?
- ip->i_d.di_gid : xfs_get_projid(ip)) ==
- be32_to_cpu(gdqp->q_core.d_id));
-
+ ASSERT(XFS_IS_GQUOTA_ON(mp));
+ ASSERT(ip->i_d.di_gid == be32_to_cpu(gdqp->q_core.d_id));
ip->i_gdquot = xfs_qm_dqhold(gdqp);
xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1);
}
+ if (pdqp) {
+ ASSERT(ip->i_pdquot == NULL);
+ ASSERT(XFS_IS_PQUOTA_ON(mp));
+ ASSERT(xfs_get_projid(ip) == be32_to_cpu(pdqp->q_core.d_id));
+
+ ip->i_pdquot = xfs_qm_dqhold(pdqp);
+ xfs_trans_mod_dquot(tp, pdqp, XFS_TRANS_DQ_ICOUNT, 1);
+ }
}
diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h
index bdb4f8b..579d6a0 100644
--- a/fs/xfs/xfs_qm.h
+++ b/fs/xfs/xfs_qm.h
@@ -44,9 +44,11 @@
typedef struct xfs_quotainfo {
struct radix_tree_root qi_uquota_tree;
struct radix_tree_root qi_gquota_tree;
+ struct radix_tree_root qi_pquota_tree;
struct mutex qi_tree_lock;
- xfs_inode_t *qi_uquotaip; /* user quota inode */
- xfs_inode_t *qi_gquotaip; /* group quota inode */
+ struct xfs_inode *qi_uquotaip; /* user quota inode */
+ struct xfs_inode *qi_gquotaip; /* group quota inode */
+ struct xfs_inode *qi_pquotaip; /* project quota inode */
struct list_head qi_lru_list;
struct mutex qi_lru_lock;
int qi_lru_count;
@@ -78,8 +80,9 @@
case XFS_DQ_USER:
return &qi->qi_uquota_tree;
case XFS_DQ_GROUP:
- case XFS_DQ_PROJ:
return &qi->qi_gquota_tree;
+ case XFS_DQ_PROJ:
+ return &qi->qi_pquota_tree;
default:
ASSERT(0);
}
@@ -93,8 +96,9 @@
case XFS_DQ_USER:
return dqp->q_mount->m_quotainfo->qi_uquotaip;
case XFS_DQ_GROUP:
- case XFS_DQ_PROJ:
return dqp->q_mount->m_quotainfo->qi_gquotaip;
+ case XFS_DQ_PROJ:
+ return dqp->q_mount->m_quotainfo->qi_pquotaip;
default:
ASSERT(0);
}
@@ -107,18 +111,20 @@
struct xfs_dquot *, uint, long);
extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
struct xfs_mount *, struct xfs_dquot *,
- struct xfs_dquot *, long, long, uint);
+ struct xfs_dquot *, struct xfs_dquot *,
+ long, long, uint);
extern void xfs_trans_dqjoin(struct xfs_trans *, struct xfs_dquot *);
extern void xfs_trans_log_dquot(struct xfs_trans *, struct xfs_dquot *);
/*
- * We keep the usr and grp dquots separately so that locking will be easier
- * to do at commit time. All transactions that we know of at this point
+ * We keep the usr, grp, and prj dquots separately so that locking will be
+ * easier to do at commit time. All transactions that we know of at this point
* affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value.
*/
enum {
XFS_QM_TRANS_USR = 0,
XFS_QM_TRANS_GRP,
+ XFS_QM_TRANS_PRJ,
XFS_QM_TRANS_DQTYPES
};
#define XFS_QM_TRANS_MAXDQS 2
diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c
index 2d02eac1..437a52d 100644
--- a/fs/xfs/xfs_qm_bhv.c
+++ b/fs/xfs/xfs_qm_bhv.c
@@ -112,16 +112,16 @@
if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) ||
(!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) ||
- (pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
- (!pquotaondisk && XFS_IS_PQUOTA_ON(mp)) ||
(gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
- (!gquotaondisk && XFS_IS_OQUOTA_ON(mp))) &&
+ (!gquotaondisk && XFS_IS_GQUOTA_ON(mp)) ||
+ (pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
+ (!pquotaondisk && XFS_IS_PQUOTA_ON(mp))) &&
xfs_dev_is_read_only(mp, "changing quota state")) {
xfs_warn(mp, "please mount with%s%s%s%s.",
(!quotaondisk ? "out quota" : ""),
(uquotaondisk ? " usrquota" : ""),
- (pquotaondisk ? " prjquota" : ""),
- (gquotaondisk ? " grpquota" : ""));
+ (gquotaondisk ? " grpquota" : ""),
+ (pquotaondisk ? " prjquota" : ""));
return XFS_ERROR(EPERM);
}
diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c
index a08801a..e4f8b2d 100644
--- a/fs/xfs/xfs_qm_syscalls.c
+++ b/fs/xfs/xfs_qm_syscalls.c
@@ -119,7 +119,8 @@
dqtype |= XFS_QMOPT_GQUOTA;
flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD);
inactivate_flags |= XFS_GQUOTA_ACTIVE;
- } else if (flags & XFS_PQUOTA_ACCT) {
+ }
+ if (flags & XFS_PQUOTA_ACCT) {
dqtype |= XFS_QMOPT_PQUOTA;
flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD);
inactivate_flags |= XFS_PQUOTA_ACTIVE;
@@ -198,10 +199,9 @@
}
/*
- * If quotas is completely disabled, close shop.
+ * If all quotas are completely turned off, close shop.
*/
- if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) ||
- ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {
+ if (mp->m_qflags == 0) {
mutex_unlock(&q->qi_quotaofflock);
xfs_qm_destroy_quotainfo(mp);
return (0);
@@ -214,10 +214,14 @@
IRELE(q->qi_uquotaip);
q->qi_uquotaip = NULL;
}
- if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) {
+ if ((dqtype & XFS_QMOPT_GQUOTA) && q->qi_gquotaip) {
IRELE(q->qi_gquotaip);
q->qi_gquotaip = NULL;
}
+ if ((dqtype & XFS_QMOPT_PQUOTA) && q->qi_pquotaip) {
+ IRELE(q->qi_pquotaip);
+ q->qi_pquotaip = NULL;
+ }
out_unlock:
mutex_unlock(&q->qi_quotaofflock);
@@ -859,9 +863,11 @@
{
/* skip quota inodes */
if (ip == ip->i_mount->m_quotainfo->qi_uquotaip ||
- ip == ip->i_mount->m_quotainfo->qi_gquotaip) {
+ ip == ip->i_mount->m_quotainfo->qi_gquotaip ||
+ ip == ip->i_mount->m_quotainfo->qi_pquotaip) {
ASSERT(ip->i_udquot == NULL);
ASSERT(ip->i_gdquot == NULL);
+ ASSERT(ip->i_pdquot == NULL);
return 0;
}
@@ -870,10 +876,14 @@
xfs_qm_dqrele(ip->i_udquot);
ip->i_udquot = NULL;
}
- if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) {
+ if ((flags & XFS_GQUOTA_ACCT) && ip->i_gdquot) {
xfs_qm_dqrele(ip->i_gdquot);
ip->i_gdquot = NULL;
}
+ if ((flags & XFS_PQUOTA_ACCT) && ip->i_pdquot) {
+ xfs_qm_dqrele(ip->i_pdquot);
+ ip->i_pdquot = NULL;
+ }
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return 0;
}
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index c3483ba..b14f42c 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -108,11 +108,28 @@
{ XFS_DQ_FREEING, "FREEING" }
/*
- * In the worst case, when both user and group quotas are on,
- * we can have a max of three dquots changing in a single transaction.
+ * We have the possibility of all three quota types being active at once, and
+ * hence free space modification requires modification of all three current
+ * dquots in a single transaction. For this case we need to have a reservation
+ * of at least 3 dquots.
+ *
+ * However, a chmod operation can change both UID and GID in a single
+ * transaction, resulting in requiring {old, new} x {uid, gid} dquots to be
+ * modified. Hence for this case we need to reserve space for at least 4 dquots.
+ *
+ * And in the worst case, there's a rename operation that can be modifying up to
+ * 4 inodes with dquots attached to them. In reality, the only inodes that can
+ * have their dquots modified are the source and destination directory inodes
+ * due to directory name creation and removal. That can require space allocation
+ * and/or freeing on both directory inodes, and hence all three dquots on each
+ * inode can be modified. And if the directories are world writeable, all the
+ * dquots can be unique and so 6 dquots can be modified....
+ *
+ * And, of course, we also need to take into account the dquot log format item
+ * used to describe each dquot.
*/
-#define XFS_DQUOT_LOGRES(mp) (sizeof(xfs_disk_dquot_t) * 3)
-
+#define XFS_DQUOT_LOGRES(mp) \
+ ((sizeof(struct xfs_dq_logformat) + sizeof(struct xfs_disk_dquot)) * 6)
/*
* These are the structures used to lay out dquots and quotaoff
@@ -271,10 +288,10 @@
* we didn't have the inode locked, the appropriate dquot(s) will be
* attached atomically.
*/
-#define XFS_NOT_DQATTACHED(mp, ip) ((XFS_IS_UQUOTA_ON(mp) &&\
- (ip)->i_udquot == NULL) || \
- (XFS_IS_OQUOTA_ON(mp) && \
- (ip)->i_gdquot == NULL))
+#define XFS_NOT_DQATTACHED(mp, ip) \
+ ((XFS_IS_UQUOTA_ON(mp) && (ip)->i_udquot == NULL) || \
+ (XFS_IS_GQUOTA_ON(mp) && (ip)->i_gdquot == NULL) || \
+ (XFS_IS_PQUOTA_ON(mp) && (ip)->i_pdquot == NULL))
#define XFS_QM_NEED_QUOTACHECK(mp) \
((XFS_IS_UQUOTA_ON(mp) && \
@@ -284,14 +301,6 @@
(XFS_IS_PQUOTA_ON(mp) && \
(mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0))
-#define XFS_MOUNT_QUOTA_SET1 (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
- XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
- XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD)
-
-#define XFS_MOUNT_QUOTA_SET2 (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
- XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\
- XFS_PQUOTA_ENFD|XFS_PQUOTA_CHKD)
-
#define XFS_MOUNT_QUOTA_ALL (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD|\
@@ -329,17 +338,18 @@
struct xfs_inode *, long, long, uint);
extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
struct xfs_mount *, struct xfs_dquot *,
- struct xfs_dquot *, long, long, uint);
+ struct xfs_dquot *, struct xfs_dquot *, long, long, uint);
extern int xfs_qm_vop_dqalloc(struct xfs_inode *, uid_t, gid_t, prid_t, uint,
- struct xfs_dquot **, struct xfs_dquot **);
+ struct xfs_dquot **, struct xfs_dquot **, struct xfs_dquot **);
extern void xfs_qm_vop_create_dqattach(struct xfs_trans *, struct xfs_inode *,
- struct xfs_dquot *, struct xfs_dquot *);
+ struct xfs_dquot *, struct xfs_dquot *, struct xfs_dquot *);
extern int xfs_qm_vop_rename_dqattach(struct xfs_inode **);
extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *,
struct xfs_inode *, struct xfs_dquot **, struct xfs_dquot *);
extern int xfs_qm_vop_chown_reserve(struct xfs_trans *, struct xfs_inode *,
- struct xfs_dquot *, struct xfs_dquot *, uint);
+ struct xfs_dquot *, struct xfs_dquot *,
+ struct xfs_dquot *, uint);
extern int xfs_qm_dqattach(struct xfs_inode *, uint);
extern int xfs_qm_dqattach_locked(struct xfs_inode *, uint);
extern void xfs_qm_dqdetach(struct xfs_inode *);
@@ -353,10 +363,12 @@
#else
static inline int
xfs_qm_vop_dqalloc(struct xfs_inode *ip, uid_t uid, gid_t gid, prid_t prid,
- uint flags, struct xfs_dquot **udqp, struct xfs_dquot **gdqp)
+ uint flags, struct xfs_dquot **udqp, struct xfs_dquot **gdqp,
+ struct xfs_dquot **pdqp)
{
*udqp = NULL;
*gdqp = NULL;
+ *pdqp = NULL;
return 0;
}
#define xfs_trans_dup_dqinfo(tp, tp2)
@@ -371,14 +383,15 @@
}
static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
struct xfs_mount *mp, struct xfs_dquot *udqp,
- struct xfs_dquot *gdqp, long nblks, long nions, uint flags)
+ struct xfs_dquot *gdqp, struct xfs_dquot *pdqp,
+ long nblks, long nions, uint flags)
{
return 0;
}
-#define xfs_qm_vop_create_dqattach(tp, ip, u, g)
+#define xfs_qm_vop_create_dqattach(tp, ip, u, g, p)
#define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
-#define xfs_qm_vop_chown_reserve(tp, ip, u, g, fl) (0)
+#define xfs_qm_vop_chown_reserve(tp, ip, u, g, p, fl) (0)
#define xfs_qm_dqattach(ip, fl) (0)
#define xfs_qm_dqattach_locked(ip, fl) (0)
#define xfs_qm_dqdetach(ip)
@@ -392,8 +405,8 @@
#define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \
xfs_trans_reserve_quota_nblks(tp, ip, -(nblks), -(ninos), flags)
-#define xfs_trans_reserve_quota(tp, mp, ud, gd, nb, ni, f) \
- xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, nb, ni, \
+#define xfs_trans_reserve_quota(tp, mp, ud, gd, pd, nb, ni, f) \
+ xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, pd, nb, ni, \
f | XFS_QMOPT_RES_REGBLKS)
extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index e830fb56..f4895b6 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -360,6 +360,7 @@
prid_t prid;
struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
uint resblks;
*ipp = NULL;
@@ -386,7 +387,7 @@
* Make sure that we have allocated dquot(s) on disk.
*/
error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
- XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
+ XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp);
if (error)
goto std_return;
@@ -427,7 +428,8 @@
/*
* Reserve disk quota : blocks and inode.
*/
- error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0);
+ error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
+ pdqp, resblks, 1, 0);
if (error)
goto error_return;
@@ -465,7 +467,7 @@
/*
* Also attach the dquot(s) to it, if applicable.
*/
- xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp);
+ xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
if (resblks)
resblks -= XFS_IALLOC_SPACE_RES(mp);
@@ -563,6 +565,7 @@
error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
*ipp = ip;
return 0;
@@ -576,6 +579,7 @@
xfs_trans_cancel(tp, cancel_flags);
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
if (unlock_dp_on_error)
xfs_iunlock(dp, XFS_ILOCK_EXCL);
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 3ba64d5..61407a8 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -163,8 +163,10 @@
if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot)
(void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta);
- if (XFS_IS_OQUOTA_ON(mp) && ip->i_gdquot)
+ if (XFS_IS_GQUOTA_ON(mp) && ip->i_gdquot)
(void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta);
+ if (XFS_IS_PQUOTA_ON(mp) && ip->i_pdquot)
+ (void) xfs_trans_mod_dquot(tp, ip->i_pdquot, field, delta);
}
STATIC struct xfs_dqtrx *
@@ -177,8 +179,12 @@
if (XFS_QM_ISUDQ(dqp))
qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR];
- else
+ else if (XFS_QM_ISGDQ(dqp))
qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP];
+ else if (XFS_QM_ISPDQ(dqp))
+ qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_PRJ];
+ else
+ return NULL;
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
if (qa[i].qt_dquot == NULL ||
@@ -291,11 +297,10 @@
/*
- * Given an array of dqtrx structures, lock all the dquots associated
- * and join them to the transaction, provided they have been modified.
- * We know that the highest number of dquots (of one type - usr OR grp),
- * involved in a transaction is 2 and that both usr and grp combined - 3.
- * So, we don't attempt to make this very generic.
+ * Given an array of dqtrx structures, lock all the dquots associated and join
+ * them to the transaction, provided they have been modified. We know that the
+ * highest number of dquots of one type - usr, grp OR prj - involved in a
+ * transaction is 2 so we don't need to make this very generic.
*/
STATIC void
xfs_trans_dqlockedjoin(
@@ -728,8 +733,8 @@
/*
* Given dquot(s), make disk block and/or inode reservations against them.
- * The fact that this does the reservation against both the usr and
- * grp/prj quotas is important, because this follows a both-or-nothing
+ * The fact that this does the reservation against user, group and
+ * project quotas is important, because this follows a all-or-nothing
* approach.
*
* flags = XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
@@ -744,6 +749,7 @@
struct xfs_mount *mp,
struct xfs_dquot *udqp,
struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp,
long nblks,
long ninos,
uint flags)
@@ -771,11 +777,21 @@
goto unwind_usr;
}
+ if (pdqp) {
+ error = xfs_trans_dqresv(tp, mp, pdqp, nblks, ninos, flags);
+ if (error)
+ goto unwind_grp;
+ }
+
/*
* Didn't change anything critical, so, no need to log
*/
return 0;
+unwind_grp:
+ flags |= XFS_QMOPT_FORCE_RES;
+ if (gdqp)
+ xfs_trans_dqresv(tp, mp, gdqp, -nblks, -ninos, flags);
unwind_usr:
flags |= XFS_QMOPT_FORCE_RES;
if (udqp)
@@ -817,6 +833,7 @@
*/
return xfs_trans_reserve_quota_bydquots(tp, mp,
ip->i_udquot, ip->i_gdquot,
+ ip->i_pdquot,
nblks, ninos, flags);
}
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 42c0ef2..dc730ac 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -489,6 +489,7 @@
prid_t prid;
struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
uint resblks;
uint log_res;
uint log_count;
@@ -507,7 +508,8 @@
* Make sure that we have allocated dquot(s) on disk.
*/
error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
- XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
+ XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
+ &udqp, &gdqp, &pdqp);
if (error)
return error;
@@ -559,7 +561,8 @@
/*
* Reserve disk quota and the inode.
*/
- error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0);
+ error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
+ pdqp, resblks, 1, 0);
if (error)
goto out_trans_cancel;
@@ -623,7 +626,7 @@
* These ids of the inode couldn't have changed since the new
* inode has been locked ever since it was created.
*/
- xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp);
+ xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
error = xfs_bmap_finish(&tp, &free_list, &committed);
if (error)
@@ -635,6 +638,7 @@
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
*ipp = ip;
return 0;
@@ -656,6 +660,7 @@
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
if (unlock_dp_on_error)
xfs_iunlock(dp, XFS_ILOCK_EXCL);
@@ -1568,7 +1573,7 @@
}
xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_quota(tp, mp,
- ip->i_udquot, ip->i_gdquot,
+ ip->i_udquot, ip->i_gdquot, ip->i_pdquot,
resblks, 0, XFS_QMOPT_RES_REGBLKS);
if (error)
goto error1;
diff --git a/include/linux/bio.h b/include/linux/bio.h
index ef24466..ec48bac 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -97,11 +97,11 @@
* permanent PIO fall back, user is probably better off disabling highmem
* I/O completely on that queue (see ide-dma for example)
*/
-#define __bio_kmap_atomic(bio, idx, kmtype) \
+#define __bio_kmap_atomic(bio, idx) \
(kmap_atomic(bio_iovec_idx((bio), (idx))->bv_page) + \
bio_iovec_idx((bio), (idx))->bv_offset)
-#define __bio_kunmap_atomic(addr, kmtype) kunmap_atomic(addr)
+#define __bio_kunmap_atomic(addr) kunmap_atomic(addr)
/*
* merge helpers etc
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index fd097ec..297462b 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -278,6 +278,8 @@
*
* - memcg: use_hierarchy is on by default and the cgroup file for
* the flag is not created.
+ *
+ * - blkcg: blk-throttle becomes properly hierarchical.
*/
CGRP_ROOT_SANE_BEHAVIOR = (1 << 0),
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
index 282e270..a5d52ee 100644
--- a/include/linux/cpu_cooling.h
+++ b/include/linux/cpu_cooling.h
@@ -41,7 +41,7 @@
*/
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
-unsigned long cpufreq_cooling_get_level(unsigned int, unsigned int);
+unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq);
#else /* !CONFIG_CPU_THERMAL */
static inline struct thermal_cooling_device *
cpufreq_cooling_register(const struct cpumask *clip_cpus)
@@ -54,7 +54,7 @@
return;
}
static inline
-unsigned long cpufreq_cooling_get_level(unsigned int, unsigned int)
+unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
{
return THERMAL_CSTATE_INVALID;
}
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 4d7390b..90d5a15 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -119,7 +119,7 @@
struct kobject kobj;
struct completion kobj_unregister;
- bool transition_ongoing; /* Tracks transition status */
+ int transition_ongoing; /* Tracks transition status */
};
#define CPUFREQ_ADJUST (0)
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 3cd3247..e151d4c 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -446,9 +446,9 @@
/*
* Table reference counting.
*/
-struct dm_table *dm_get_live_table(struct mapped_device *md);
-void dm_table_get(struct dm_table *t);
-void dm_table_put(struct dm_table *t);
+struct dm_table *dm_get_live_table(struct mapped_device *md, int *srcu_idx);
+void dm_put_live_table(struct mapped_device *md, int srcu_idx);
+void dm_sync_table(struct mapped_device *md);
/*
* Queries
diff --git a/include/linux/elevator.h b/include/linux/elevator.h
index acd0312..306dd8c 100644
--- a/include/linux/elevator.h
+++ b/include/linux/elevator.h
@@ -7,6 +7,7 @@
#ifdef CONFIG_BLOCK
struct io_cq;
+struct elevator_type;
typedef int (elevator_merge_fn) (struct request_queue *, struct request **,
struct bio *);
@@ -35,7 +36,8 @@
typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *);
typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *);
-typedef int (elevator_init_fn) (struct request_queue *);
+typedef int (elevator_init_fn) (struct request_queue *,
+ struct elevator_type *e);
typedef void (elevator_exit_fn) (struct elevator_queue *);
struct elevator_ops
@@ -155,6 +157,8 @@
extern void elevator_exit(struct elevator_queue *);
extern int elevator_change(struct request_queue *, const char *);
extern bool elv_rq_merge_ok(struct request *, struct bio *);
+extern struct elevator_queue *elevator_alloc(struct request_queue *,
+ struct elevator_type *);
/*
* Helper functions.
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 99d0fbc..9f15c00 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -566,10 +566,6 @@
size_t cnt, loff_t *ppos) { return -ENODEV; }
static inline ssize_t ftrace_notrace_write(struct file *file, const char __user *ubuf,
size_t cnt, loff_t *ppos) { return -ENODEV; }
-static inline loff_t ftrace_regex_lseek(struct file *file, loff_t offset, int whence)
-{
- return -ENODEV;
-}
static inline int
ftrace_regex_release(struct inode *inode, struct file *file) { return -ENODEV; }
#endif /* CONFIG_DYNAMIC_FTRACE */
@@ -828,10 +824,15 @@
extern enum ftrace_dump_mode ftrace_dump_on_oops;
+extern void disable_trace_on_warning(void);
+extern int __disable_trace_on_warning;
+
#ifdef CONFIG_PREEMPT
#define INIT_TRACE_RECURSION .trace_recursion = 0,
#endif
+#else /* CONFIG_TRACING */
+static inline void disable_trace_on_warning(void) { }
#endif /* CONFIG_TRACING */
#ifndef INIT_TRACE_RECURSION
diff --git a/include/linux/mlx5/cmd.h b/include/linux/mlx5/cmd.h
new file mode 100644
index 0000000..2826a4b
--- /dev/null
+++ b/include/linux/mlx5/cmd.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_CMD_H
+#define MLX5_CMD_H
+
+#include <linux/types.h>
+
+struct manage_pages_layout {
+ u64 ptr;
+ u32 reserved;
+ u16 num_entries;
+ u16 func_id;
+};
+
+
+struct mlx5_cmd_alloc_uar_imm_out {
+ u32 rsvd[3];
+ u32 uarn;
+};
+
+#endif /* MLX5_CMD_H */
diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h
new file mode 100644
index 0000000..3db67f7
--- /dev/null
+++ b/include/linux/mlx5/cq.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_CORE_CQ_H
+#define MLX5_CORE_CQ_H
+
+#include <rdma/ib_verbs.h>
+#include <linux/mlx5/driver.h>
+
+
+struct mlx5_core_cq {
+ u32 cqn;
+ int cqe_sz;
+ __be32 *set_ci_db;
+ __be32 *arm_db;
+ atomic_t refcount;
+ struct completion free;
+ unsigned vector;
+ int irqn;
+ void (*comp) (struct mlx5_core_cq *);
+ void (*event) (struct mlx5_core_cq *, enum mlx5_event);
+ struct mlx5_uar *uar;
+ u32 cons_index;
+ unsigned arm_sn;
+ struct mlx5_rsc_debug *dbg;
+ int pid;
+};
+
+
+enum {
+ MLX5_CQE_SYNDROME_LOCAL_LENGTH_ERR = 0x01,
+ MLX5_CQE_SYNDROME_LOCAL_QP_OP_ERR = 0x02,
+ MLX5_CQE_SYNDROME_LOCAL_PROT_ERR = 0x04,
+ MLX5_CQE_SYNDROME_WR_FLUSH_ERR = 0x05,
+ MLX5_CQE_SYNDROME_MW_BIND_ERR = 0x06,
+ MLX5_CQE_SYNDROME_BAD_RESP_ERR = 0x10,
+ MLX5_CQE_SYNDROME_LOCAL_ACCESS_ERR = 0x11,
+ MLX5_CQE_SYNDROME_REMOTE_INVAL_REQ_ERR = 0x12,
+ MLX5_CQE_SYNDROME_REMOTE_ACCESS_ERR = 0x13,
+ MLX5_CQE_SYNDROME_REMOTE_OP_ERR = 0x14,
+ MLX5_CQE_SYNDROME_TRANSPORT_RETRY_EXC_ERR = 0x15,
+ MLX5_CQE_SYNDROME_RNR_RETRY_EXC_ERR = 0x16,
+ MLX5_CQE_SYNDROME_REMOTE_ABORTED_ERR = 0x22,
+};
+
+enum {
+ MLX5_CQE_OWNER_MASK = 1,
+ MLX5_CQE_REQ = 0,
+ MLX5_CQE_RESP_WR_IMM = 1,
+ MLX5_CQE_RESP_SEND = 2,
+ MLX5_CQE_RESP_SEND_IMM = 3,
+ MLX5_CQE_RESP_SEND_INV = 4,
+ MLX5_CQE_RESIZE_CQ = 0xff, /* TBD */
+ MLX5_CQE_REQ_ERR = 13,
+ MLX5_CQE_RESP_ERR = 14,
+};
+
+enum {
+ MLX5_CQ_MODIFY_RESEIZE = 0,
+ MLX5_CQ_MODIFY_MODER = 1,
+ MLX5_CQ_MODIFY_MAPPING = 2,
+};
+
+struct mlx5_cq_modify_params {
+ int type;
+ union {
+ struct {
+ u32 page_offset;
+ u8 log_cq_size;
+ } resize;
+
+ struct {
+ } moder;
+
+ struct {
+ } mapping;
+ } params;
+};
+
+enum {
+ CQE_SIZE_64 = 0,
+ CQE_SIZE_128 = 1,
+};
+
+static inline int cqe_sz_to_mlx_sz(u8 size)
+{
+ return size == 64 ? CQE_SIZE_64 : CQE_SIZE_128;
+}
+
+static inline void mlx5_cq_set_ci(struct mlx5_core_cq *cq)
+{
+ *cq->set_ci_db = cpu_to_be32(cq->cons_index & 0xffffff);
+}
+
+enum {
+ MLX5_CQ_DB_REQ_NOT_SOL = 1 << 24,
+ MLX5_CQ_DB_REQ_NOT = 0 << 24
+};
+
+static inline void mlx5_cq_arm(struct mlx5_core_cq *cq, u32 cmd,
+ void __iomem *uar_page,
+ spinlock_t *doorbell_lock)
+{
+ __be32 doorbell[2];
+ u32 sn;
+ u32 ci;
+
+ sn = cq->arm_sn & 3;
+ ci = cq->cons_index & 0xffffff;
+
+ *cq->arm_db = cpu_to_be32(sn << 28 | cmd | ci);
+
+ /* Make sure that the doorbell record in host memory is
+ * written before ringing the doorbell via PCI MMIO.
+ */
+ wmb();
+
+ doorbell[0] = cpu_to_be32(sn << 28 | cmd | ci);
+ doorbell[1] = cpu_to_be32(cq->cqn);
+
+ mlx5_write64(doorbell, uar_page + MLX5_CQ_DOORBELL, doorbell_lock);
+}
+
+int mlx5_init_cq_table(struct mlx5_core_dev *dev);
+void mlx5_cleanup_cq_table(struct mlx5_core_dev *dev);
+int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ struct mlx5_create_cq_mbox_in *in, int inlen);
+int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq);
+int mlx5_core_query_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ struct mlx5_query_cq_mbox_out *out);
+int mlx5_core_modify_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
+ int type, struct mlx5_cq_modify_params *params);
+int mlx5_debug_cq_add(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq);
+void mlx5_debug_cq_remove(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq);
+
+#endif /* MLX5_CORE_CQ_H */
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
new file mode 100644
index 0000000..8de8d8f
--- /dev/null
+++ b/include/linux/mlx5/device.h
@@ -0,0 +1,893 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_DEVICE_H
+#define MLX5_DEVICE_H
+
+#include <linux/types.h>
+#include <rdma/ib_verbs.h>
+
+#if defined(__LITTLE_ENDIAN)
+#define MLX5_SET_HOST_ENDIANNESS 0
+#elif defined(__BIG_ENDIAN)
+#define MLX5_SET_HOST_ENDIANNESS 0x80
+#else
+#error Host endianness not defined
+#endif
+
+enum {
+ MLX5_MAX_COMMANDS = 32,
+ MLX5_CMD_DATA_BLOCK_SIZE = 512,
+ MLX5_PCI_CMD_XPORT = 7,
+};
+
+enum {
+ MLX5_EXTENDED_UD_AV = 0x80000000,
+};
+
+enum {
+ MLX5_CQ_STATE_ARMED = 9,
+ MLX5_CQ_STATE_ALWAYS_ARMED = 0xb,
+ MLX5_CQ_STATE_FIRED = 0xa,
+};
+
+enum {
+ MLX5_STAT_RATE_OFFSET = 5,
+};
+
+enum {
+ MLX5_INLINE_SEG = 0x80000000,
+};
+
+enum {
+ MLX5_PERM_LOCAL_READ = 1 << 2,
+ MLX5_PERM_LOCAL_WRITE = 1 << 3,
+ MLX5_PERM_REMOTE_READ = 1 << 4,
+ MLX5_PERM_REMOTE_WRITE = 1 << 5,
+ MLX5_PERM_ATOMIC = 1 << 6,
+ MLX5_PERM_UMR_EN = 1 << 7,
+};
+
+enum {
+ MLX5_PCIE_CTRL_SMALL_FENCE = 1 << 0,
+ MLX5_PCIE_CTRL_RELAXED_ORDERING = 1 << 2,
+ MLX5_PCIE_CTRL_NO_SNOOP = 1 << 3,
+ MLX5_PCIE_CTRL_TLP_PROCE_EN = 1 << 6,
+ MLX5_PCIE_CTRL_TPH_MASK = 3 << 4,
+};
+
+enum {
+ MLX5_ACCESS_MODE_PA = 0,
+ MLX5_ACCESS_MODE_MTT = 1,
+ MLX5_ACCESS_MODE_KLM = 2
+};
+
+enum {
+ MLX5_MKEY_REMOTE_INVAL = 1 << 24,
+ MLX5_MKEY_FLAG_SYNC_UMR = 1 << 29,
+ MLX5_MKEY_BSF_EN = 1 << 30,
+ MLX5_MKEY_LEN64 = 1 << 31,
+};
+
+enum {
+ MLX5_EN_RD = (u64)1,
+ MLX5_EN_WR = (u64)2
+};
+
+enum {
+ MLX5_BF_REGS_PER_PAGE = 4,
+ MLX5_MAX_UAR_PAGES = 1 << 8,
+ MLX5_MAX_UUARS = MLX5_MAX_UAR_PAGES * MLX5_BF_REGS_PER_PAGE,
+};
+
+enum {
+ MLX5_MKEY_MASK_LEN = 1ull << 0,
+ MLX5_MKEY_MASK_PAGE_SIZE = 1ull << 1,
+ MLX5_MKEY_MASK_START_ADDR = 1ull << 6,
+ MLX5_MKEY_MASK_PD = 1ull << 7,
+ MLX5_MKEY_MASK_EN_RINVAL = 1ull << 8,
+ MLX5_MKEY_MASK_BSF_EN = 1ull << 12,
+ MLX5_MKEY_MASK_KEY = 1ull << 13,
+ MLX5_MKEY_MASK_QPN = 1ull << 14,
+ MLX5_MKEY_MASK_LR = 1ull << 17,
+ MLX5_MKEY_MASK_LW = 1ull << 18,
+ MLX5_MKEY_MASK_RR = 1ull << 19,
+ MLX5_MKEY_MASK_RW = 1ull << 20,
+ MLX5_MKEY_MASK_A = 1ull << 21,
+ MLX5_MKEY_MASK_SMALL_FENCE = 1ull << 23,
+ MLX5_MKEY_MASK_FREE = 1ull << 29,
+};
+
+enum mlx5_event {
+ MLX5_EVENT_TYPE_COMP = 0x0,
+
+ MLX5_EVENT_TYPE_PATH_MIG = 0x01,
+ MLX5_EVENT_TYPE_COMM_EST = 0x02,
+ MLX5_EVENT_TYPE_SQ_DRAINED = 0x03,
+ MLX5_EVENT_TYPE_SRQ_LAST_WQE = 0x13,
+ MLX5_EVENT_TYPE_SRQ_RQ_LIMIT = 0x14,
+
+ MLX5_EVENT_TYPE_CQ_ERROR = 0x04,
+ MLX5_EVENT_TYPE_WQ_CATAS_ERROR = 0x05,
+ MLX5_EVENT_TYPE_PATH_MIG_FAILED = 0x07,
+ MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR = 0x10,
+ MLX5_EVENT_TYPE_WQ_ACCESS_ERROR = 0x11,
+ MLX5_EVENT_TYPE_SRQ_CATAS_ERROR = 0x12,
+
+ MLX5_EVENT_TYPE_INTERNAL_ERROR = 0x08,
+ MLX5_EVENT_TYPE_PORT_CHANGE = 0x09,
+ MLX5_EVENT_TYPE_GPIO_EVENT = 0x15,
+ MLX5_EVENT_TYPE_REMOTE_CONFIG = 0x19,
+
+ MLX5_EVENT_TYPE_DB_BF_CONGESTION = 0x1a,
+ MLX5_EVENT_TYPE_STALL_EVENT = 0x1b,
+
+ MLX5_EVENT_TYPE_CMD = 0x0a,
+ MLX5_EVENT_TYPE_PAGE_REQUEST = 0xb,
+};
+
+enum {
+ MLX5_PORT_CHANGE_SUBTYPE_DOWN = 1,
+ MLX5_PORT_CHANGE_SUBTYPE_ACTIVE = 4,
+ MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED = 5,
+ MLX5_PORT_CHANGE_SUBTYPE_LID = 6,
+ MLX5_PORT_CHANGE_SUBTYPE_PKEY = 7,
+ MLX5_PORT_CHANGE_SUBTYPE_GUID = 8,
+ MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG = 9,
+};
+
+enum {
+ MLX5_DEV_CAP_FLAG_RC = 1LL << 0,
+ MLX5_DEV_CAP_FLAG_UC = 1LL << 1,
+ MLX5_DEV_CAP_FLAG_UD = 1LL << 2,
+ MLX5_DEV_CAP_FLAG_XRC = 1LL << 3,
+ MLX5_DEV_CAP_FLAG_SRQ = 1LL << 6,
+ MLX5_DEV_CAP_FLAG_BAD_PKEY_CNTR = 1LL << 8,
+ MLX5_DEV_CAP_FLAG_BAD_QKEY_CNTR = 1LL << 9,
+ MLX5_DEV_CAP_FLAG_APM = 1LL << 17,
+ MLX5_DEV_CAP_FLAG_ATOMIC = 1LL << 18,
+ MLX5_DEV_CAP_FLAG_ON_DMND_PG = 1LL << 24,
+ MLX5_DEV_CAP_FLAG_RESIZE_SRQ = 1LL << 32,
+ MLX5_DEV_CAP_FLAG_REMOTE_FENCE = 1LL << 38,
+ MLX5_DEV_CAP_FLAG_TLP_HINTS = 1LL << 39,
+ MLX5_DEV_CAP_FLAG_SIG_HAND_OVER = 1LL << 40,
+ MLX5_DEV_CAP_FLAG_DCT = 1LL << 41,
+ MLX5_DEV_CAP_FLAG_CMDIF_CSUM = 1LL << 46,
+};
+
+enum {
+ MLX5_OPCODE_NOP = 0x00,
+ MLX5_OPCODE_SEND_INVAL = 0x01,
+ MLX5_OPCODE_RDMA_WRITE = 0x08,
+ MLX5_OPCODE_RDMA_WRITE_IMM = 0x09,
+ MLX5_OPCODE_SEND = 0x0a,
+ MLX5_OPCODE_SEND_IMM = 0x0b,
+ MLX5_OPCODE_RDMA_READ = 0x10,
+ MLX5_OPCODE_ATOMIC_CS = 0x11,
+ MLX5_OPCODE_ATOMIC_FA = 0x12,
+ MLX5_OPCODE_ATOMIC_MASKED_CS = 0x14,
+ MLX5_OPCODE_ATOMIC_MASKED_FA = 0x15,
+ MLX5_OPCODE_BIND_MW = 0x18,
+ MLX5_OPCODE_CONFIG_CMD = 0x1f,
+
+ MLX5_RECV_OPCODE_RDMA_WRITE_IMM = 0x00,
+ MLX5_RECV_OPCODE_SEND = 0x01,
+ MLX5_RECV_OPCODE_SEND_IMM = 0x02,
+ MLX5_RECV_OPCODE_SEND_INVAL = 0x03,
+
+ MLX5_CQE_OPCODE_ERROR = 0x1e,
+ MLX5_CQE_OPCODE_RESIZE = 0x16,
+
+ MLX5_OPCODE_SET_PSV = 0x20,
+ MLX5_OPCODE_GET_PSV = 0x21,
+ MLX5_OPCODE_CHECK_PSV = 0x22,
+ MLX5_OPCODE_RGET_PSV = 0x26,
+ MLX5_OPCODE_RCHECK_PSV = 0x27,
+
+ MLX5_OPCODE_UMR = 0x25,
+
+};
+
+enum {
+ MLX5_SET_PORT_RESET_QKEY = 0,
+ MLX5_SET_PORT_GUID0 = 16,
+ MLX5_SET_PORT_NODE_GUID = 17,
+ MLX5_SET_PORT_SYS_GUID = 18,
+ MLX5_SET_PORT_GID_TABLE = 19,
+ MLX5_SET_PORT_PKEY_TABLE = 20,
+};
+
+enum {
+ MLX5_MAX_PAGE_SHIFT = 31
+};
+
+struct mlx5_inbox_hdr {
+ __be16 opcode;
+ u8 rsvd[4];
+ __be16 opmod;
+};
+
+struct mlx5_outbox_hdr {
+ u8 status;
+ u8 rsvd[3];
+ __be32 syndrome;
+};
+
+struct mlx5_cmd_query_adapter_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_cmd_query_adapter_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[24];
+ u8 intapin;
+ u8 rsvd1[13];
+ __be16 vsd_vendor_id;
+ u8 vsd[208];
+ u8 vsd_psid[16];
+};
+
+struct mlx5_hca_cap {
+ u8 rsvd1[16];
+ u8 log_max_srq_sz;
+ u8 log_max_qp_sz;
+ u8 rsvd2;
+ u8 log_max_qp;
+ u8 log_max_strq_sz;
+ u8 log_max_srqs;
+ u8 rsvd4[2];
+ u8 rsvd5;
+ u8 log_max_cq_sz;
+ u8 rsvd6;
+ u8 log_max_cq;
+ u8 log_max_eq_sz;
+ u8 log_max_mkey;
+ u8 rsvd7;
+ u8 log_max_eq;
+ u8 max_indirection;
+ u8 log_max_mrw_sz;
+ u8 log_max_bsf_list_sz;
+ u8 log_max_klm_list_sz;
+ u8 rsvd_8_0;
+ u8 log_max_ra_req_dc;
+ u8 rsvd_8_1;
+ u8 log_max_ra_res_dc;
+ u8 rsvd9;
+ u8 log_max_ra_req_qp;
+ u8 rsvd10;
+ u8 log_max_ra_res_qp;
+ u8 rsvd11[4];
+ __be16 max_qp_count;
+ __be16 rsvd12;
+ u8 rsvd13;
+ u8 local_ca_ack_delay;
+ u8 rsvd14;
+ u8 num_ports;
+ u8 log_max_msg;
+ u8 rsvd15[3];
+ __be16 stat_rate_support;
+ u8 rsvd16[2];
+ __be64 flags;
+ u8 rsvd17;
+ u8 uar_sz;
+ u8 rsvd18;
+ u8 log_pg_sz;
+ __be16 bf_log_bf_reg_size;
+ u8 rsvd19[4];
+ __be16 max_desc_sz_sq;
+ u8 rsvd20[2];
+ __be16 max_desc_sz_rq;
+ u8 rsvd21[2];
+ __be16 max_desc_sz_sq_dc;
+ u8 rsvd22[4];
+ __be16 max_qp_mcg;
+ u8 rsvd23;
+ u8 log_max_mcg;
+ u8 rsvd24;
+ u8 log_max_pd;
+ u8 rsvd25;
+ u8 log_max_xrcd;
+ u8 rsvd26[42];
+ __be16 log_uar_page_sz;
+ u8 rsvd27[28];
+ u8 log_msx_atomic_size_qp;
+ u8 rsvd28[2];
+ u8 log_msx_atomic_size_dc;
+ u8 rsvd29[76];
+};
+
+
+struct mlx5_cmd_query_hca_cap_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+
+struct mlx5_cmd_query_hca_cap_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+ struct mlx5_hca_cap hca_cap;
+};
+
+
+struct mlx5_cmd_set_hca_cap_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+ struct mlx5_hca_cap hca_cap;
+};
+
+
+struct mlx5_cmd_set_hca_cap_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+};
+
+
+struct mlx5_cmd_init_hca_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd0[2];
+ __be16 profile;
+ u8 rsvd1[4];
+};
+
+struct mlx5_cmd_init_hca_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_cmd_teardown_hca_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd0[2];
+ __be16 profile;
+ u8 rsvd1[4];
+};
+
+struct mlx5_cmd_teardown_hca_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_cmd_layout {
+ u8 type;
+ u8 rsvd0[3];
+ __be32 inlen;
+ __be64 in_ptr;
+ __be32 in[4];
+ __be32 out[4];
+ __be64 out_ptr;
+ __be32 outlen;
+ u8 token;
+ u8 sig;
+ u8 rsvd1;
+ u8 status_own;
+};
+
+
+struct health_buffer {
+ __be32 assert_var[5];
+ __be32 rsvd0[3];
+ __be32 assert_exit_ptr;
+ __be32 assert_callra;
+ __be32 rsvd1[2];
+ __be32 fw_ver;
+ __be32 hw_id;
+ __be32 rsvd2;
+ u8 irisc_index;
+ u8 synd;
+ __be16 ext_sync;
+};
+
+struct mlx5_init_seg {
+ __be32 fw_rev;
+ __be32 cmdif_rev_fw_sub;
+ __be32 rsvd0[2];
+ __be32 cmdq_addr_h;
+ __be32 cmdq_addr_l_sz;
+ __be32 cmd_dbell;
+ __be32 rsvd1[121];
+ struct health_buffer health;
+ __be32 rsvd2[884];
+ __be32 health_counter;
+ __be32 rsvd3[1023];
+ __be64 ieee1588_clk;
+ __be32 ieee1588_clk_type;
+ __be32 clr_intx;
+};
+
+struct mlx5_eqe_comp {
+ __be32 reserved[6];
+ __be32 cqn;
+};
+
+struct mlx5_eqe_qp_srq {
+ __be32 reserved[6];
+ __be32 qp_srq_n;
+};
+
+struct mlx5_eqe_cq_err {
+ __be32 cqn;
+ u8 reserved1[7];
+ u8 syndrome;
+};
+
+struct mlx5_eqe_dropped_packet {
+};
+
+struct mlx5_eqe_port_state {
+ u8 reserved0[8];
+ u8 port;
+};
+
+struct mlx5_eqe_gpio {
+ __be32 reserved0[2];
+ __be64 gpio_event;
+};
+
+struct mlx5_eqe_congestion {
+ u8 type;
+ u8 rsvd0;
+ u8 congestion_level;
+};
+
+struct mlx5_eqe_stall_vl {
+ u8 rsvd0[3];
+ u8 port_vl;
+};
+
+struct mlx5_eqe_cmd {
+ __be32 vector;
+ __be32 rsvd[6];
+};
+
+struct mlx5_eqe_page_req {
+ u8 rsvd0[2];
+ __be16 func_id;
+ u8 rsvd1[2];
+ __be16 num_pages;
+ __be32 rsvd2[5];
+};
+
+union ev_data {
+ __be32 raw[7];
+ struct mlx5_eqe_cmd cmd;
+ struct mlx5_eqe_comp comp;
+ struct mlx5_eqe_qp_srq qp_srq;
+ struct mlx5_eqe_cq_err cq_err;
+ struct mlx5_eqe_dropped_packet dp;
+ struct mlx5_eqe_port_state port;
+ struct mlx5_eqe_gpio gpio;
+ struct mlx5_eqe_congestion cong;
+ struct mlx5_eqe_stall_vl stall_vl;
+ struct mlx5_eqe_page_req req_pages;
+} __packed;
+
+struct mlx5_eqe {
+ u8 rsvd0;
+ u8 type;
+ u8 rsvd1;
+ u8 sub_type;
+ __be32 rsvd2[7];
+ union ev_data data;
+ __be16 rsvd3;
+ u8 signature;
+ u8 owner;
+} __packed;
+
+struct mlx5_cmd_prot_block {
+ u8 data[MLX5_CMD_DATA_BLOCK_SIZE];
+ u8 rsvd0[48];
+ __be64 next;
+ __be32 block_num;
+ u8 rsvd1;
+ u8 token;
+ u8 ctrl_sig;
+ u8 sig;
+};
+
+struct mlx5_err_cqe {
+ u8 rsvd0[32];
+ __be32 srqn;
+ u8 rsvd1[18];
+ u8 vendor_err_synd;
+ u8 syndrome;
+ __be32 s_wqe_opcode_qpn;
+ __be16 wqe_counter;
+ u8 signature;
+ u8 op_own;
+};
+
+struct mlx5_cqe64 {
+ u8 rsvd0[17];
+ u8 ml_path;
+ u8 rsvd20[4];
+ __be16 slid;
+ __be32 flags_rqpn;
+ u8 rsvd28[4];
+ __be32 srqn;
+ __be32 imm_inval_pkey;
+ u8 rsvd40[4];
+ __be32 byte_cnt;
+ __be64 timestamp;
+ __be32 sop_drop_qpn;
+ __be16 wqe_counter;
+ u8 signature;
+ u8 op_own;
+};
+
+struct mlx5_wqe_srq_next_seg {
+ u8 rsvd0[2];
+ __be16 next_wqe_index;
+ u8 signature;
+ u8 rsvd1[11];
+};
+
+union mlx5_ext_cqe {
+ struct ib_grh grh;
+ u8 inl[64];
+};
+
+struct mlx5_cqe128 {
+ union mlx5_ext_cqe inl_grh;
+ struct mlx5_cqe64 cqe64;
+};
+
+struct mlx5_srq_ctx {
+ u8 state_log_sz;
+ u8 rsvd0[3];
+ __be32 flags_xrcd;
+ __be32 pgoff_cqn;
+ u8 rsvd1[4];
+ u8 log_pg_sz;
+ u8 rsvd2[7];
+ __be32 pd;
+ __be16 lwm;
+ __be16 wqe_cnt;
+ u8 rsvd3[8];
+ __be64 db_record;
+};
+
+struct mlx5_create_srq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 input_srqn;
+ u8 rsvd0[4];
+ struct mlx5_srq_ctx ctx;
+ u8 rsvd1[208];
+ __be64 pas[0];
+};
+
+struct mlx5_create_srq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 srqn;
+ u8 rsvd[4];
+};
+
+struct mlx5_destroy_srq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 srqn;
+ u8 rsvd[4];
+};
+
+struct mlx5_destroy_srq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_query_srq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 srqn;
+ u8 rsvd0[4];
+};
+
+struct mlx5_query_srq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+ struct mlx5_srq_ctx ctx;
+ u8 rsvd1[32];
+ __be64 pas[0];
+};
+
+struct mlx5_arm_srq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 srqn;
+ __be16 rsvd;
+ __be16 lwm;
+};
+
+struct mlx5_arm_srq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_cq_context {
+ u8 status;
+ u8 cqe_sz_flags;
+ u8 st;
+ u8 rsvd3;
+ u8 rsvd4[6];
+ __be16 page_offset;
+ __be32 log_sz_usr_page;
+ __be16 cq_period;
+ __be16 cq_max_count;
+ __be16 rsvd20;
+ __be16 c_eqn;
+ u8 log_pg_sz;
+ u8 rsvd25[7];
+ __be32 last_notified_index;
+ __be32 solicit_producer_index;
+ __be32 consumer_counter;
+ __be32 producer_counter;
+ u8 rsvd48[8];
+ __be64 db_record_addr;
+};
+
+struct mlx5_create_cq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 input_cqn;
+ u8 rsvdx[4];
+ struct mlx5_cq_context ctx;
+ u8 rsvd6[192];
+ __be64 pas[0];
+};
+
+struct mlx5_create_cq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 cqn;
+ u8 rsvd0[4];
+};
+
+struct mlx5_destroy_cq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 cqn;
+ u8 rsvd0[4];
+};
+
+struct mlx5_destroy_cq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+};
+
+struct mlx5_query_cq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 cqn;
+ u8 rsvd0[4];
+};
+
+struct mlx5_query_cq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+ struct mlx5_cq_context ctx;
+ u8 rsvd6[16];
+ __be64 pas[0];
+};
+
+struct mlx5_eq_context {
+ u8 status;
+ u8 ec_oi;
+ u8 st;
+ u8 rsvd2[7];
+ __be16 page_pffset;
+ __be32 log_sz_usr_page;
+ u8 rsvd3[7];
+ u8 intr;
+ u8 log_page_size;
+ u8 rsvd4[15];
+ __be32 consumer_counter;
+ __be32 produser_counter;
+ u8 rsvd5[16];
+};
+
+struct mlx5_create_eq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd0[3];
+ u8 input_eqn;
+ u8 rsvd1[4];
+ struct mlx5_eq_context ctx;
+ u8 rsvd2[8];
+ __be64 events_mask;
+ u8 rsvd3[176];
+ __be64 pas[0];
+};
+
+struct mlx5_create_eq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[3];
+ u8 eq_number;
+ u8 rsvd1[4];
+};
+
+struct mlx5_destroy_eq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd0[3];
+ u8 eqn;
+ u8 rsvd1[4];
+};
+
+struct mlx5_destroy_eq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_map_eq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be64 mask;
+ u8 mu;
+ u8 rsvd0[2];
+ u8 eqn;
+ u8 rsvd1[24];
+};
+
+struct mlx5_map_eq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_query_eq_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd0[3];
+ u8 eqn;
+ u8 rsvd1[4];
+};
+
+struct mlx5_query_eq_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+ struct mlx5_eq_context ctx;
+};
+
+struct mlx5_mkey_seg {
+ /* This is a two bit field occupying bits 31-30.
+ * bit 31 is always 0,
+ * bit 30 is zero for regular MRs and 1 (e.g free) for UMRs that do not have tanslation
+ */
+ u8 status;
+ u8 pcie_control;
+ u8 flags;
+ u8 version;
+ __be32 qpn_mkey7_0;
+ u8 rsvd1[4];
+ __be32 flags_pd;
+ __be64 start_addr;
+ __be64 len;
+ __be32 bsfs_octo_size;
+ u8 rsvd2[16];
+ __be32 xlt_oct_size;
+ u8 rsvd3[3];
+ u8 log2_page_size;
+ u8 rsvd4[4];
+};
+
+struct mlx5_query_special_ctxs_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_query_special_ctxs_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 dump_fill_mkey;
+ __be32 reserved_lkey;
+};
+
+struct mlx5_create_mkey_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 input_mkey_index;
+ u8 rsvd0[4];
+ struct mlx5_mkey_seg seg;
+ u8 rsvd1[16];
+ __be32 xlat_oct_act_size;
+ __be32 bsf_coto_act_size;
+ u8 rsvd2[168];
+ __be64 pas[0];
+};
+
+struct mlx5_create_mkey_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 mkey;
+ u8 rsvd[4];
+};
+
+struct mlx5_destroy_mkey_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 mkey;
+ u8 rsvd[4];
+};
+
+struct mlx5_destroy_mkey_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_query_mkey_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 mkey;
+};
+
+struct mlx5_query_mkey_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be64 pas[0];
+};
+
+struct mlx5_modify_mkey_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 mkey;
+ __be64 pas[0];
+};
+
+struct mlx5_modify_mkey_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+};
+
+struct mlx5_dump_mkey_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+};
+
+struct mlx5_dump_mkey_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 mkey;
+};
+
+struct mlx5_mad_ifc_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be16 remote_lid;
+ u8 rsvd0;
+ u8 port;
+ u8 rsvd1[4];
+ u8 data[256];
+};
+
+struct mlx5_mad_ifc_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+ u8 data[256];
+};
+
+struct mlx5_access_reg_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd0[2];
+ __be16 register_id;
+ __be32 arg;
+ __be32 data[0];
+};
+
+struct mlx5_access_reg_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+ __be32 data[0];
+};
+
+#define MLX5_ATTR_EXTENDED_PORT_INFO cpu_to_be16(0xff90)
+
+enum {
+ MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO = 1 << 0
+};
+
+#endif /* MLX5_DEVICE_H */
diff --git a/include/linux/mlx5/doorbell.h b/include/linux/mlx5/doorbell.h
new file mode 100644
index 0000000..163a818
--- /dev/null
+++ b/include/linux/mlx5/doorbell.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_DOORBELL_H
+#define MLX5_DOORBELL_H
+
+#define MLX5_BF_OFFSET 0x800
+#define MLX5_CQ_DOORBELL 0x20
+
+#if BITS_PER_LONG == 64
+/* Assume that we can just write a 64-bit doorbell atomically. s390
+ * actually doesn't have writeq() but S/390 systems don't even have
+ * PCI so we won't worry about it.
+ */
+
+#define MLX5_DECLARE_DOORBELL_LOCK(name)
+#define MLX5_INIT_DOORBELL_LOCK(ptr) do { } while (0)
+#define MLX5_GET_DOORBELL_LOCK(ptr) (NULL)
+
+static inline void mlx5_write64(__be32 val[2], void __iomem *dest,
+ spinlock_t *doorbell_lock)
+{
+ __raw_writeq(*(u64 *)val, dest);
+}
+
+#else
+
+/* Just fall back to a spinlock to protect the doorbell if
+ * BITS_PER_LONG is 32 -- there's no portable way to do atomic 64-bit
+ * MMIO writes.
+ */
+
+#define MLX5_DECLARE_DOORBELL_LOCK(name) spinlock_t name;
+#define MLX5_INIT_DOORBELL_LOCK(ptr) spin_lock_init(ptr)
+#define MLX5_GET_DOORBELL_LOCK(ptr) (ptr)
+
+static inline void mlx5_write64(__be32 val[2], void __iomem *dest,
+ spinlock_t *doorbell_lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(doorbell_lock, flags);
+ __raw_writel((__force u32) val[0], dest);
+ __raw_writel((__force u32) val[1], dest + 4);
+ spin_unlock_irqrestore(doorbell_lock, flags);
+}
+
+#endif
+
+#endif /* MLX5_DOORBELL_H */
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
new file mode 100644
index 0000000..f22e441
--- /dev/null
+++ b/include/linux/mlx5/driver.h
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_DRIVER_H
+#define MLX5_DRIVER_H
+
+#include <linux/kernel.h>
+#include <linux/completion.h>
+#include <linux/pci.h>
+#include <linux/spinlock_types.h>
+#include <linux/semaphore.h>
+#include <linux/vmalloc.h>
+#include <linux/radix-tree.h>
+#include <linux/mlx5/device.h>
+#include <linux/mlx5/doorbell.h>
+
+enum {
+ MLX5_BOARD_ID_LEN = 64,
+ MLX5_MAX_NAME_LEN = 16,
+};
+
+enum {
+ /* one minute for the sake of bringup. Generally, commands must always
+ * complete and we may need to increase this timeout value
+ */
+ MLX5_CMD_TIMEOUT_MSEC = 7200 * 1000,
+ MLX5_CMD_WQ_MAX_NAME = 32,
+};
+
+enum {
+ CMD_OWNER_SW = 0x0,
+ CMD_OWNER_HW = 0x1,
+ CMD_STATUS_SUCCESS = 0,
+};
+
+enum mlx5_sqp_t {
+ MLX5_SQP_SMI = 0,
+ MLX5_SQP_GSI = 1,
+ MLX5_SQP_IEEE_1588 = 2,
+ MLX5_SQP_SNIFFER = 3,
+ MLX5_SQP_SYNC_UMR = 4,
+};
+
+enum {
+ MLX5_MAX_PORTS = 2,
+};
+
+enum {
+ MLX5_EQ_VEC_PAGES = 0,
+ MLX5_EQ_VEC_CMD = 1,
+ MLX5_EQ_VEC_ASYNC = 2,
+ MLX5_EQ_VEC_COMP_BASE,
+};
+
+enum {
+ MLX5_MAX_EQ_NAME = 20
+};
+
+enum {
+ MLX5_ATOMIC_MODE_IB_COMP = 1 << 16,
+ MLX5_ATOMIC_MODE_CX = 2 << 16,
+ MLX5_ATOMIC_MODE_8B = 3 << 16,
+ MLX5_ATOMIC_MODE_16B = 4 << 16,
+ MLX5_ATOMIC_MODE_32B = 5 << 16,
+ MLX5_ATOMIC_MODE_64B = 6 << 16,
+ MLX5_ATOMIC_MODE_128B = 7 << 16,
+ MLX5_ATOMIC_MODE_256B = 8 << 16,
+};
+
+enum {
+ MLX5_CMD_OP_QUERY_HCA_CAP = 0x100,
+ MLX5_CMD_OP_QUERY_ADAPTER = 0x101,
+ MLX5_CMD_OP_INIT_HCA = 0x102,
+ MLX5_CMD_OP_TEARDOWN_HCA = 0x103,
+ MLX5_CMD_OP_QUERY_PAGES = 0x107,
+ MLX5_CMD_OP_MANAGE_PAGES = 0x108,
+ MLX5_CMD_OP_SET_HCA_CAP = 0x109,
+
+ MLX5_CMD_OP_CREATE_MKEY = 0x200,
+ MLX5_CMD_OP_QUERY_MKEY = 0x201,
+ MLX5_CMD_OP_DESTROY_MKEY = 0x202,
+ MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS = 0x203,
+
+ MLX5_CMD_OP_CREATE_EQ = 0x301,
+ MLX5_CMD_OP_DESTROY_EQ = 0x302,
+ MLX5_CMD_OP_QUERY_EQ = 0x303,
+
+ MLX5_CMD_OP_CREATE_CQ = 0x400,
+ MLX5_CMD_OP_DESTROY_CQ = 0x401,
+ MLX5_CMD_OP_QUERY_CQ = 0x402,
+ MLX5_CMD_OP_MODIFY_CQ = 0x403,
+
+ MLX5_CMD_OP_CREATE_QP = 0x500,
+ MLX5_CMD_OP_DESTROY_QP = 0x501,
+ MLX5_CMD_OP_RST2INIT_QP = 0x502,
+ MLX5_CMD_OP_INIT2RTR_QP = 0x503,
+ MLX5_CMD_OP_RTR2RTS_QP = 0x504,
+ MLX5_CMD_OP_RTS2RTS_QP = 0x505,
+ MLX5_CMD_OP_SQERR2RTS_QP = 0x506,
+ MLX5_CMD_OP_2ERR_QP = 0x507,
+ MLX5_CMD_OP_RTS2SQD_QP = 0x508,
+ MLX5_CMD_OP_SQD2RTS_QP = 0x509,
+ MLX5_CMD_OP_2RST_QP = 0x50a,
+ MLX5_CMD_OP_QUERY_QP = 0x50b,
+ MLX5_CMD_OP_CONF_SQP = 0x50c,
+ MLX5_CMD_OP_MAD_IFC = 0x50d,
+ MLX5_CMD_OP_INIT2INIT_QP = 0x50e,
+ MLX5_CMD_OP_SUSPEND_QP = 0x50f,
+ MLX5_CMD_OP_UNSUSPEND_QP = 0x510,
+ MLX5_CMD_OP_SQD2SQD_QP = 0x511,
+ MLX5_CMD_OP_ALLOC_QP_COUNTER_SET = 0x512,
+ MLX5_CMD_OP_DEALLOC_QP_COUNTER_SET = 0x513,
+ MLX5_CMD_OP_QUERY_QP_COUNTER_SET = 0x514,
+
+ MLX5_CMD_OP_CREATE_PSV = 0x600,
+ MLX5_CMD_OP_DESTROY_PSV = 0x601,
+ MLX5_CMD_OP_QUERY_PSV = 0x602,
+ MLX5_CMD_OP_QUERY_SIG_RULE_TABLE = 0x603,
+ MLX5_CMD_OP_QUERY_BLOCK_SIZE_TABLE = 0x604,
+
+ MLX5_CMD_OP_CREATE_SRQ = 0x700,
+ MLX5_CMD_OP_DESTROY_SRQ = 0x701,
+ MLX5_CMD_OP_QUERY_SRQ = 0x702,
+ MLX5_CMD_OP_ARM_RQ = 0x703,
+ MLX5_CMD_OP_RESIZE_SRQ = 0x704,
+
+ MLX5_CMD_OP_ALLOC_PD = 0x800,
+ MLX5_CMD_OP_DEALLOC_PD = 0x801,
+ MLX5_CMD_OP_ALLOC_UAR = 0x802,
+ MLX5_CMD_OP_DEALLOC_UAR = 0x803,
+
+ MLX5_CMD_OP_ATTACH_TO_MCG = 0x806,
+ MLX5_CMD_OP_DETACH_FROM_MCG = 0x807,
+
+
+ MLX5_CMD_OP_ALLOC_XRCD = 0x80e,
+ MLX5_CMD_OP_DEALLOC_XRCD = 0x80f,
+
+ MLX5_CMD_OP_ACCESS_REG = 0x805,
+ MLX5_CMD_OP_MAX = 0x810,
+};
+
+enum {
+ MLX5_REG_PCAP = 0x5001,
+ MLX5_REG_PMTU = 0x5003,
+ MLX5_REG_PTYS = 0x5004,
+ MLX5_REG_PAOS = 0x5006,
+ MLX5_REG_PMAOS = 0x5012,
+ MLX5_REG_PUDE = 0x5009,
+ MLX5_REG_PMPE = 0x5010,
+ MLX5_REG_PELC = 0x500e,
+ MLX5_REG_PMLP = 0, /* TBD */
+ MLX5_REG_NODE_DESC = 0x6001,
+ MLX5_REG_HOST_ENDIANNESS = 0x7004,
+};
+
+enum dbg_rsc_type {
+ MLX5_DBG_RSC_QP,
+ MLX5_DBG_RSC_EQ,
+ MLX5_DBG_RSC_CQ,
+};
+
+struct mlx5_field_desc {
+ struct dentry *dent;
+ int i;
+};
+
+struct mlx5_rsc_debug {
+ struct mlx5_core_dev *dev;
+ void *object;
+ enum dbg_rsc_type type;
+ struct dentry *root;
+ struct mlx5_field_desc fields[0];
+};
+
+enum mlx5_dev_event {
+ MLX5_DEV_EVENT_SYS_ERROR,
+ MLX5_DEV_EVENT_PORT_UP,
+ MLX5_DEV_EVENT_PORT_DOWN,
+ MLX5_DEV_EVENT_PORT_INITIALIZED,
+ MLX5_DEV_EVENT_LID_CHANGE,
+ MLX5_DEV_EVENT_PKEY_CHANGE,
+ MLX5_DEV_EVENT_GUID_CHANGE,
+ MLX5_DEV_EVENT_CLIENT_REREG,
+};
+
+struct mlx5_uuar_info {
+ struct mlx5_uar *uars;
+ int num_uars;
+ int num_low_latency_uuars;
+ unsigned long *bitmap;
+ unsigned int *count;
+ struct mlx5_bf *bfs;
+
+ /*
+ * protect uuar allocation data structs
+ */
+ struct mutex lock;
+};
+
+struct mlx5_bf {
+ void __iomem *reg;
+ void __iomem *regreg;
+ int buf_size;
+ struct mlx5_uar *uar;
+ unsigned long offset;
+ int need_lock;
+ /* protect blue flame buffer selection when needed
+ */
+ spinlock_t lock;
+
+ /* serialize 64 bit writes when done as two 32 bit accesses
+ */
+ spinlock_t lock32;
+ int uuarn;
+};
+
+struct mlx5_cmd_first {
+ __be32 data[4];
+};
+
+struct mlx5_cmd_msg {
+ struct list_head list;
+ struct cache_ent *cache;
+ u32 len;
+ struct mlx5_cmd_first first;
+ struct mlx5_cmd_mailbox *next;
+};
+
+struct mlx5_cmd_debug {
+ struct dentry *dbg_root;
+ struct dentry *dbg_in;
+ struct dentry *dbg_out;
+ struct dentry *dbg_outlen;
+ struct dentry *dbg_status;
+ struct dentry *dbg_run;
+ void *in_msg;
+ void *out_msg;
+ u8 status;
+ u16 inlen;
+ u16 outlen;
+};
+
+struct cache_ent {
+ /* protect block chain allocations
+ */
+ spinlock_t lock;
+ struct list_head head;
+};
+
+struct cmd_msg_cache {
+ struct cache_ent large;
+ struct cache_ent med;
+
+};
+
+struct mlx5_cmd_stats {
+ u64 sum;
+ u64 n;
+ struct dentry *root;
+ struct dentry *avg;
+ struct dentry *count;
+ /* protect command average calculations */
+ spinlock_t lock;
+};
+
+struct mlx5_cmd {
+ void *cmd_buf;
+ dma_addr_t dma;
+ u16 cmdif_rev;
+ u8 log_sz;
+ u8 log_stride;
+ int max_reg_cmds;
+ int events;
+ u32 __iomem *vector;
+
+ /* protect command queue allocations
+ */
+ spinlock_t alloc_lock;
+
+ /* protect token allocations
+ */
+ spinlock_t token_lock;
+ u8 token;
+ unsigned long bitmask;
+ char wq_name[MLX5_CMD_WQ_MAX_NAME];
+ struct workqueue_struct *wq;
+ struct semaphore sem;
+ struct semaphore pages_sem;
+ int mode;
+ struct mlx5_cmd_work_ent *ent_arr[MLX5_MAX_COMMANDS];
+ struct pci_pool *pool;
+ struct mlx5_cmd_debug dbg;
+ struct cmd_msg_cache cache;
+ int checksum_disabled;
+ struct mlx5_cmd_stats stats[MLX5_CMD_OP_MAX];
+};
+
+struct mlx5_port_caps {
+ int gid_table_len;
+ int pkey_table_len;
+};
+
+struct mlx5_caps {
+ u8 log_max_eq;
+ u8 log_max_cq;
+ u8 log_max_qp;
+ u8 log_max_mkey;
+ u8 log_max_pd;
+ u8 log_max_srq;
+ u32 max_cqes;
+ int max_wqes;
+ int max_sq_desc_sz;
+ int max_rq_desc_sz;
+ u64 flags;
+ u16 stat_rate_support;
+ int log_max_msg;
+ int num_ports;
+ int max_ra_res_qp;
+ int max_ra_req_qp;
+ int max_srq_wqes;
+ int bf_reg_size;
+ int bf_regs_per_page;
+ struct mlx5_port_caps port[MLX5_MAX_PORTS];
+ u8 ext_port_cap[MLX5_MAX_PORTS];
+ int max_vf;
+ u32 reserved_lkey;
+ u8 local_ca_ack_delay;
+ u8 log_max_mcg;
+ u16 max_qp_mcg;
+ int min_page_sz;
+};
+
+struct mlx5_cmd_mailbox {
+ void *buf;
+ dma_addr_t dma;
+ struct mlx5_cmd_mailbox *next;
+};
+
+struct mlx5_buf_list {
+ void *buf;
+ dma_addr_t map;
+};
+
+struct mlx5_buf {
+ struct mlx5_buf_list direct;
+ struct mlx5_buf_list *page_list;
+ int nbufs;
+ int npages;
+ int page_shift;
+ int size;
+};
+
+struct mlx5_eq {
+ struct mlx5_core_dev *dev;
+ __be32 __iomem *doorbell;
+ u32 cons_index;
+ struct mlx5_buf buf;
+ int size;
+ u8 irqn;
+ u8 eqn;
+ int nent;
+ u64 mask;
+ char name[MLX5_MAX_EQ_NAME];
+ struct list_head list;
+ int index;
+ struct mlx5_rsc_debug *dbg;
+};
+
+
+struct mlx5_core_mr {
+ u64 iova;
+ u64 size;
+ u32 key;
+ u32 pd;
+ u32 access;
+};
+
+struct mlx5_core_srq {
+ u32 srqn;
+ int max;
+ int max_gs;
+ int max_avail_gather;
+ int wqe_shift;
+ void (*event) (struct mlx5_core_srq *, enum mlx5_event);
+
+ atomic_t refcount;
+ struct completion free;
+};
+
+struct mlx5_eq_table {
+ void __iomem *update_ci;
+ void __iomem *update_arm_ci;
+ struct list_head *comp_eq_head;
+ struct mlx5_eq pages_eq;
+ struct mlx5_eq async_eq;
+ struct mlx5_eq cmd_eq;
+ struct msix_entry *msix_arr;
+ int num_comp_vectors;
+ /* protect EQs list
+ */
+ spinlock_t lock;
+};
+
+struct mlx5_uar {
+ u32 index;
+ struct list_head bf_list;
+ unsigned free_bf_bmap;
+ void __iomem *wc_map;
+ void __iomem *map;
+};
+
+
+struct mlx5_core_health {
+ struct health_buffer __iomem *health;
+ __be32 __iomem *health_counter;
+ struct timer_list timer;
+ struct list_head list;
+ u32 prev;
+ int miss_counter;
+};
+
+struct mlx5_cq_table {
+ /* protect radix tree
+ */
+ spinlock_t lock;
+ struct radix_tree_root tree;
+};
+
+struct mlx5_qp_table {
+ /* protect radix tree
+ */
+ spinlock_t lock;
+ struct radix_tree_root tree;
+};
+
+struct mlx5_srq_table {
+ /* protect radix tree
+ */
+ spinlock_t lock;
+ struct radix_tree_root tree;
+};
+
+struct mlx5_priv {
+ char name[MLX5_MAX_NAME_LEN];
+ struct mlx5_eq_table eq_table;
+ struct mlx5_uuar_info uuari;
+ MLX5_DECLARE_DOORBELL_LOCK(cq_uar_lock);
+
+ /* pages stuff */
+ struct workqueue_struct *pg_wq;
+ struct rb_root page_root;
+ int fw_pages;
+ int reg_pages;
+
+ struct mlx5_core_health health;
+
+ struct mlx5_srq_table srq_table;
+
+ /* start: qp staff */
+ struct mlx5_qp_table qp_table;
+ struct dentry *qp_debugfs;
+ struct dentry *eq_debugfs;
+ struct dentry *cq_debugfs;
+ struct dentry *cmdif_debugfs;
+ /* end: qp staff */
+
+ /* start: cq staff */
+ struct mlx5_cq_table cq_table;
+ /* end: cq staff */
+
+ /* start: alloc staff */
+ struct mutex pgdir_mutex;
+ struct list_head pgdir_list;
+ /* end: alloc staff */
+ struct dentry *dbg_root;
+
+ /* protect mkey key part */
+ spinlock_t mkey_lock;
+ u8 mkey_key;
+};
+
+struct mlx5_core_dev {
+ struct pci_dev *pdev;
+ u8 rev_id;
+ char board_id[MLX5_BOARD_ID_LEN];
+ struct mlx5_cmd cmd;
+ struct mlx5_caps caps;
+ phys_addr_t iseg_base;
+ struct mlx5_init_seg __iomem *iseg;
+ void (*event) (struct mlx5_core_dev *dev,
+ enum mlx5_dev_event event,
+ void *data);
+ struct mlx5_priv priv;
+ struct mlx5_profile *profile;
+ atomic_t num_qps;
+};
+
+struct mlx5_db {
+ __be32 *db;
+ union {
+ struct mlx5_db_pgdir *pgdir;
+ struct mlx5_ib_user_db_page *user_page;
+ } u;
+ dma_addr_t dma;
+ int index;
+};
+
+enum {
+ MLX5_DB_PER_PAGE = PAGE_SIZE / L1_CACHE_BYTES,
+};
+
+enum {
+ MLX5_COMP_EQ_SIZE = 1024,
+};
+
+struct mlx5_db_pgdir {
+ struct list_head list;
+ DECLARE_BITMAP(bitmap, MLX5_DB_PER_PAGE);
+ __be32 *db_page;
+ dma_addr_t db_dma;
+};
+
+typedef void (*mlx5_cmd_cbk_t)(int status, void *context);
+
+struct mlx5_cmd_work_ent {
+ struct mlx5_cmd_msg *in;
+ struct mlx5_cmd_msg *out;
+ mlx5_cmd_cbk_t callback;
+ void *context;
+ int idx;
+ struct completion done;
+ struct mlx5_cmd *cmd;
+ struct work_struct work;
+ struct mlx5_cmd_layout *lay;
+ int ret;
+ int page_queue;
+ u8 status;
+ u8 token;
+ struct timespec ts1;
+ struct timespec ts2;
+};
+
+struct mlx5_pas {
+ u64 pa;
+ u8 log_sz;
+};
+
+static inline void *mlx5_buf_offset(struct mlx5_buf *buf, int offset)
+{
+ if (likely(BITS_PER_LONG == 64 || buf->nbufs == 1))
+ return buf->direct.buf + offset;
+ else
+ return buf->page_list[offset >> PAGE_SHIFT].buf +
+ (offset & (PAGE_SIZE - 1));
+}
+
+extern struct workqueue_struct *mlx5_core_wq;
+
+#define STRUCT_FIELD(header, field) \
+ .struct_offset_bytes = offsetof(struct ib_unpacked_ ## header, field), \
+ .struct_size_bytes = sizeof((struct ib_unpacked_ ## header *)0)->field
+
+struct ib_field {
+ size_t struct_offset_bytes;
+ size_t struct_size_bytes;
+ int offset_bits;
+ int size_bits;
+};
+
+static inline struct mlx5_core_dev *pci2mlx5_core_dev(struct pci_dev *pdev)
+{
+ return pci_get_drvdata(pdev);
+}
+
+extern struct dentry *mlx5_debugfs_root;
+
+static inline u16 fw_rev_maj(struct mlx5_core_dev *dev)
+{
+ return ioread32be(&dev->iseg->fw_rev) & 0xffff;
+}
+
+static inline u16 fw_rev_min(struct mlx5_core_dev *dev)
+{
+ return ioread32be(&dev->iseg->fw_rev) >> 16;
+}
+
+static inline u16 fw_rev_sub(struct mlx5_core_dev *dev)
+{
+ return ioread32be(&dev->iseg->cmdif_rev_fw_sub) & 0xffff;
+}
+
+static inline u16 cmdif_rev(struct mlx5_core_dev *dev)
+{
+ return ioread32be(&dev->iseg->cmdif_rev_fw_sub) >> 16;
+}
+
+static inline void *mlx5_vzalloc(unsigned long size)
+{
+ void *rtn;
+
+ rtn = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+ if (!rtn)
+ rtn = vzalloc(size);
+ return rtn;
+}
+
+static inline void mlx5_vfree(const void *addr)
+{
+ if (addr && is_vmalloc_addr(addr))
+ vfree(addr);
+ else
+ kfree(addr);
+}
+
+int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev);
+void mlx5_dev_cleanup(struct mlx5_core_dev *dev);
+int mlx5_cmd_init(struct mlx5_core_dev *dev);
+void mlx5_cmd_cleanup(struct mlx5_core_dev *dev);
+void mlx5_cmd_use_events(struct mlx5_core_dev *dev);
+void mlx5_cmd_use_polling(struct mlx5_core_dev *dev);
+int mlx5_cmd_status_to_err(struct mlx5_outbox_hdr *hdr);
+int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
+ int out_size);
+int mlx5_cmd_alloc_uar(struct mlx5_core_dev *dev, u32 *uarn);
+int mlx5_cmd_free_uar(struct mlx5_core_dev *dev, u32 uarn);
+int mlx5_alloc_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari);
+int mlx5_free_uuars(struct mlx5_core_dev *dev, struct mlx5_uuar_info *uuari);
+void mlx5_health_cleanup(void);
+void __init mlx5_health_init(void);
+void mlx5_start_health_poll(struct mlx5_core_dev *dev);
+void mlx5_stop_health_poll(struct mlx5_core_dev *dev);
+int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, int max_direct,
+ struct mlx5_buf *buf);
+void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf);
+struct mlx5_cmd_mailbox *mlx5_alloc_cmd_mailbox_chain(struct mlx5_core_dev *dev,
+ gfp_t flags, int npages);
+void mlx5_free_cmd_mailbox_chain(struct mlx5_core_dev *dev,
+ struct mlx5_cmd_mailbox *head);
+int mlx5_core_create_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
+ struct mlx5_create_srq_mbox_in *in, int inlen);
+int mlx5_core_destroy_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq);
+int mlx5_core_query_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
+ struct mlx5_query_srq_mbox_out *out);
+int mlx5_core_arm_srq(struct mlx5_core_dev *dev, struct mlx5_core_srq *srq,
+ u16 lwm, int is_srq);
+int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
+ struct mlx5_create_mkey_mbox_in *in, int inlen);
+int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr);
+int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
+ struct mlx5_query_mkey_mbox_out *out, int outlen);
+int mlx5_core_dump_fill_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
+ u32 *mkey);
+int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn);
+int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn);
+int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb,
+ u16 opmod, int port);
+void mlx5_pagealloc_init(struct mlx5_core_dev *dev);
+void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev);
+int mlx5_pagealloc_start(struct mlx5_core_dev *dev);
+void mlx5_pagealloc_stop(struct mlx5_core_dev *dev);
+void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
+ s16 npages);
+int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev);
+int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev);
+void mlx5_register_debugfs(void);
+void mlx5_unregister_debugfs(void);
+int mlx5_eq_init(struct mlx5_core_dev *dev);
+void mlx5_eq_cleanup(struct mlx5_core_dev *dev);
+void mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas);
+void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn);
+void mlx5_qp_event(struct mlx5_core_dev *dev, u32 qpn, int event_type);
+void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type);
+struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn);
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector);
+void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type);
+int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
+ int nent, u64 mask, const char *name, struct mlx5_uar *uar);
+int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+int mlx5_start_eqs(struct mlx5_core_dev *dev);
+int mlx5_stop_eqs(struct mlx5_core_dev *dev);
+int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn);
+int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn);
+
+int mlx5_qp_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev);
+int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in,
+ int size_in, void *data_out, int size_out,
+ u16 reg_num, int arg, int write);
+int mlx5_set_port_caps(struct mlx5_core_dev *dev, int port_num, u32 caps);
+
+int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
+int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
+ struct mlx5_query_eq_mbox_out *out, int outlen);
+int mlx5_eq_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_eq_debugfs_cleanup(struct mlx5_core_dev *dev);
+int mlx5_cq_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev);
+int mlx5_db_alloc(struct mlx5_core_dev *dev, struct mlx5_db *db);
+void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db);
+
+typedef void (*health_handler_t)(struct pci_dev *pdev, struct health_buffer __iomem *buf, int size);
+int mlx5_register_health_report_handler(health_handler_t handler);
+void mlx5_unregister_health_report_handler(void);
+const char *mlx5_command_str(int command);
+int mlx5_cmdif_debugfs_init(struct mlx5_core_dev *dev);
+void mlx5_cmdif_debugfs_cleanup(struct mlx5_core_dev *dev);
+
+static inline u32 mlx5_mkey_to_idx(u32 mkey)
+{
+ return mkey >> 8;
+}
+
+static inline u32 mlx5_idx_to_mkey(u32 mkey_idx)
+{
+ return mkey_idx << 8;
+}
+
+enum {
+ MLX5_PROF_MASK_QP_SIZE = (u64)1 << 0,
+ MLX5_PROF_MASK_CMDIF_CSUM = (u64)1 << 1,
+ MLX5_PROF_MASK_MR_CACHE = (u64)1 << 2,
+};
+
+enum {
+ MAX_MR_CACHE_ENTRIES = 16,
+};
+
+struct mlx5_profile {
+ u64 mask;
+ u32 log_max_qp;
+ int cmdif_csum;
+ struct {
+ int size;
+ int limit;
+ } mr_cache[MAX_MR_CACHE_ENTRIES];
+};
+
+#endif /* MLX5_DRIVER_H */
diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h
new file mode 100644
index 0000000..d9e3eac
--- /dev/null
+++ b/include/linux/mlx5/qp.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_QP_H
+#define MLX5_QP_H
+
+#include <linux/mlx5/device.h>
+#include <linux/mlx5/driver.h>
+
+#define MLX5_INVALID_LKEY 0x100
+
+enum mlx5_qp_optpar {
+ MLX5_QP_OPTPAR_ALT_ADDR_PATH = 1 << 0,
+ MLX5_QP_OPTPAR_RRE = 1 << 1,
+ MLX5_QP_OPTPAR_RAE = 1 << 2,
+ MLX5_QP_OPTPAR_RWE = 1 << 3,
+ MLX5_QP_OPTPAR_PKEY_INDEX = 1 << 4,
+ MLX5_QP_OPTPAR_Q_KEY = 1 << 5,
+ MLX5_QP_OPTPAR_RNR_TIMEOUT = 1 << 6,
+ MLX5_QP_OPTPAR_PRIMARY_ADDR_PATH = 1 << 7,
+ MLX5_QP_OPTPAR_SRA_MAX = 1 << 8,
+ MLX5_QP_OPTPAR_RRA_MAX = 1 << 9,
+ MLX5_QP_OPTPAR_PM_STATE = 1 << 10,
+ MLX5_QP_OPTPAR_RETRY_COUNT = 1 << 12,
+ MLX5_QP_OPTPAR_RNR_RETRY = 1 << 13,
+ MLX5_QP_OPTPAR_ACK_TIMEOUT = 1 << 14,
+ MLX5_QP_OPTPAR_PRI_PORT = 1 << 16,
+ MLX5_QP_OPTPAR_SRQN = 1 << 18,
+ MLX5_QP_OPTPAR_CQN_RCV = 1 << 19,
+ MLX5_QP_OPTPAR_DC_HS = 1 << 20,
+ MLX5_QP_OPTPAR_DC_KEY = 1 << 21,
+};
+
+enum mlx5_qp_state {
+ MLX5_QP_STATE_RST = 0,
+ MLX5_QP_STATE_INIT = 1,
+ MLX5_QP_STATE_RTR = 2,
+ MLX5_QP_STATE_RTS = 3,
+ MLX5_QP_STATE_SQER = 4,
+ MLX5_QP_STATE_SQD = 5,
+ MLX5_QP_STATE_ERR = 6,
+ MLX5_QP_STATE_SQ_DRAINING = 7,
+ MLX5_QP_STATE_SUSPENDED = 9,
+ MLX5_QP_NUM_STATE
+};
+
+enum {
+ MLX5_QP_ST_RC = 0x0,
+ MLX5_QP_ST_UC = 0x1,
+ MLX5_QP_ST_UD = 0x2,
+ MLX5_QP_ST_XRC = 0x3,
+ MLX5_QP_ST_MLX = 0x4,
+ MLX5_QP_ST_DCI = 0x5,
+ MLX5_QP_ST_DCT = 0x6,
+ MLX5_QP_ST_QP0 = 0x7,
+ MLX5_QP_ST_QP1 = 0x8,
+ MLX5_QP_ST_RAW_ETHERTYPE = 0x9,
+ MLX5_QP_ST_RAW_IPV6 = 0xa,
+ MLX5_QP_ST_SNIFFER = 0xb,
+ MLX5_QP_ST_SYNC_UMR = 0xe,
+ MLX5_QP_ST_PTP_1588 = 0xd,
+ MLX5_QP_ST_REG_UMR = 0xc,
+ MLX5_QP_ST_MAX
+};
+
+enum {
+ MLX5_QP_PM_MIGRATED = 0x3,
+ MLX5_QP_PM_ARMED = 0x0,
+ MLX5_QP_PM_REARM = 0x1
+};
+
+enum {
+ MLX5_NON_ZERO_RQ = 0 << 24,
+ MLX5_SRQ_RQ = 1 << 24,
+ MLX5_CRQ_RQ = 2 << 24,
+ MLX5_ZERO_LEN_RQ = 3 << 24
+};
+
+enum {
+ /* params1 */
+ MLX5_QP_BIT_SRE = 1 << 15,
+ MLX5_QP_BIT_SWE = 1 << 14,
+ MLX5_QP_BIT_SAE = 1 << 13,
+ /* params2 */
+ MLX5_QP_BIT_RRE = 1 << 15,
+ MLX5_QP_BIT_RWE = 1 << 14,
+ MLX5_QP_BIT_RAE = 1 << 13,
+ MLX5_QP_BIT_RIC = 1 << 4,
+};
+
+enum {
+ MLX5_WQE_CTRL_CQ_UPDATE = 2 << 2,
+ MLX5_WQE_CTRL_SOLICITED = 1 << 1,
+};
+
+enum {
+ MLX5_SEND_WQE_BB = 64,
+};
+
+enum {
+ MLX5_WQE_FMR_PERM_LOCAL_READ = 1 << 27,
+ MLX5_WQE_FMR_PERM_LOCAL_WRITE = 1 << 28,
+ MLX5_WQE_FMR_PERM_REMOTE_READ = 1 << 29,
+ MLX5_WQE_FMR_PERM_REMOTE_WRITE = 1 << 30,
+ MLX5_WQE_FMR_PERM_ATOMIC = 1 << 31
+};
+
+enum {
+ MLX5_FENCE_MODE_NONE = 0 << 5,
+ MLX5_FENCE_MODE_INITIATOR_SMALL = 1 << 5,
+ MLX5_FENCE_MODE_STRONG_ORDERING = 3 << 5,
+ MLX5_FENCE_MODE_SMALL_AND_FENCE = 4 << 5,
+};
+
+enum {
+ MLX5_QP_LAT_SENSITIVE = 1 << 28,
+ MLX5_QP_ENABLE_SIG = 1 << 31,
+};
+
+enum {
+ MLX5_RCV_DBR = 0,
+ MLX5_SND_DBR = 1,
+};
+
+struct mlx5_wqe_fmr_seg {
+ __be32 flags;
+ __be32 mem_key;
+ __be64 buf_list;
+ __be64 start_addr;
+ __be64 reg_len;
+ __be32 offset;
+ __be32 page_size;
+ u32 reserved[2];
+};
+
+struct mlx5_wqe_ctrl_seg {
+ __be32 opmod_idx_opcode;
+ __be32 qpn_ds;
+ u8 signature;
+ u8 rsvd[2];
+ u8 fm_ce_se;
+ __be32 imm;
+};
+
+struct mlx5_wqe_xrc_seg {
+ __be32 xrc_srqn;
+ u8 rsvd[12];
+};
+
+struct mlx5_wqe_masked_atomic_seg {
+ __be64 swap_add;
+ __be64 compare;
+ __be64 swap_add_mask;
+ __be64 compare_mask;
+};
+
+struct mlx5_av {
+ union {
+ struct {
+ __be32 qkey;
+ __be32 reserved;
+ } qkey;
+ __be64 dc_key;
+ } key;
+ __be32 dqp_dct;
+ u8 stat_rate_sl;
+ u8 fl_mlid;
+ __be16 rlid;
+ u8 reserved0[10];
+ u8 tclass;
+ u8 hop_limit;
+ __be32 grh_gid_fl;
+ u8 rgid[16];
+};
+
+struct mlx5_wqe_datagram_seg {
+ struct mlx5_av av;
+};
+
+struct mlx5_wqe_raddr_seg {
+ __be64 raddr;
+ __be32 rkey;
+ u32 reserved;
+};
+
+struct mlx5_wqe_atomic_seg {
+ __be64 swap_add;
+ __be64 compare;
+};
+
+struct mlx5_wqe_data_seg {
+ __be32 byte_count;
+ __be32 lkey;
+ __be64 addr;
+};
+
+struct mlx5_wqe_umr_ctrl_seg {
+ u8 flags;
+ u8 rsvd0[3];
+ __be16 klm_octowords;
+ __be16 bsf_octowords;
+ __be64 mkey_mask;
+ u8 rsvd1[32];
+};
+
+struct mlx5_seg_set_psv {
+ __be32 psv_num;
+ __be16 syndrome;
+ __be16 status;
+ __be32 transient_sig;
+ __be32 ref_tag;
+};
+
+struct mlx5_seg_get_psv {
+ u8 rsvd[19];
+ u8 num_psv;
+ __be32 l_key;
+ __be64 va;
+ __be32 psv_index[4];
+};
+
+struct mlx5_seg_check_psv {
+ u8 rsvd0[2];
+ __be16 err_coalescing_op;
+ u8 rsvd1[2];
+ __be16 xport_err_op;
+ u8 rsvd2[2];
+ __be16 xport_err_mask;
+ u8 rsvd3[7];
+ u8 num_psv;
+ __be32 l_key;
+ __be64 va;
+ __be32 psv_index[4];
+};
+
+struct mlx5_rwqe_sig {
+ u8 rsvd0[4];
+ u8 signature;
+ u8 rsvd1[11];
+};
+
+struct mlx5_wqe_signature_seg {
+ u8 rsvd0[4];
+ u8 signature;
+ u8 rsvd1[11];
+};
+
+struct mlx5_wqe_inline_seg {
+ __be32 byte_count;
+};
+
+struct mlx5_core_qp {
+ void (*event) (struct mlx5_core_qp *, int);
+ int qpn;
+ atomic_t refcount;
+ struct completion free;
+ struct mlx5_rsc_debug *dbg;
+ int pid;
+};
+
+struct mlx5_qp_path {
+ u8 fl;
+ u8 rsvd3;
+ u8 free_ar;
+ u8 pkey_index;
+ u8 rsvd0;
+ u8 grh_mlid;
+ __be16 rlid;
+ u8 ackto_lt;
+ u8 mgid_index;
+ u8 static_rate;
+ u8 hop_limit;
+ __be32 tclass_flowlabel;
+ u8 rgid[16];
+ u8 rsvd1[4];
+ u8 sl;
+ u8 port;
+ u8 rsvd2[6];
+};
+
+struct mlx5_qp_context {
+ __be32 flags;
+ __be32 flags_pd;
+ u8 mtu_msgmax;
+ u8 rq_size_stride;
+ __be16 sq_crq_size;
+ __be32 qp_counter_set_usr_page;
+ __be32 wire_qpn;
+ __be32 log_pg_sz_remote_qpn;
+ struct mlx5_qp_path pri_path;
+ struct mlx5_qp_path alt_path;
+ __be32 params1;
+ u8 reserved2[4];
+ __be32 next_send_psn;
+ __be32 cqn_send;
+ u8 reserved3[8];
+ __be32 last_acked_psn;
+ __be32 ssn;
+ __be32 params2;
+ __be32 rnr_nextrecvpsn;
+ __be32 xrcd;
+ __be32 cqn_recv;
+ __be64 db_rec_addr;
+ __be32 qkey;
+ __be32 rq_type_srqn;
+ __be32 rmsn;
+ __be16 hw_sq_wqe_counter;
+ __be16 sw_sq_wqe_counter;
+ __be16 hw_rcyclic_byte_counter;
+ __be16 hw_rq_counter;
+ __be16 sw_rcyclic_byte_counter;
+ __be16 sw_rq_counter;
+ u8 rsvd0[5];
+ u8 cgs;
+ u8 cs_req;
+ u8 cs_res;
+ __be64 dc_access_key;
+ u8 rsvd1[24];
+};
+
+struct mlx5_create_qp_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 input_qpn;
+ u8 rsvd0[4];
+ __be32 opt_param_mask;
+ u8 rsvd1[4];
+ struct mlx5_qp_context ctx;
+ u8 rsvd3[16];
+ __be64 pas[0];
+};
+
+struct mlx5_create_qp_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 qpn;
+ u8 rsvd0[4];
+};
+
+struct mlx5_destroy_qp_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 qpn;
+ u8 rsvd0[4];
+};
+
+struct mlx5_destroy_qp_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+};
+
+struct mlx5_modify_qp_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 qpn;
+ u8 rsvd1[4];
+ __be32 optparam;
+ u8 rsvd0[4];
+ struct mlx5_qp_context ctx;
+};
+
+struct mlx5_modify_qp_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd0[8];
+};
+
+struct mlx5_query_qp_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 qpn;
+ u8 rsvd[4];
+};
+
+struct mlx5_query_qp_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd1[8];
+ __be32 optparam;
+ u8 rsvd0[4];
+ struct mlx5_qp_context ctx;
+ u8 rsvd2[16];
+ __be64 pas[0];
+};
+
+struct mlx5_conf_sqp_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 qpn;
+ u8 rsvd[3];
+ u8 type;
+};
+
+struct mlx5_conf_sqp_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_alloc_xrcd_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+struct mlx5_alloc_xrcd_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ __be32 xrcdn;
+ u8 rsvd[4];
+};
+
+struct mlx5_dealloc_xrcd_mbox_in {
+ struct mlx5_inbox_hdr hdr;
+ __be32 xrcdn;
+ u8 rsvd[4];
+};
+
+struct mlx5_dealloc_xrcd_mbox_out {
+ struct mlx5_outbox_hdr hdr;
+ u8 rsvd[8];
+};
+
+static inline struct mlx5_core_qp *__mlx5_qp_lookup(struct mlx5_core_dev *dev, u32 qpn)
+{
+ return radix_tree_lookup(&dev->priv.qp_table.tree, qpn);
+}
+
+int mlx5_core_create_qp(struct mlx5_core_dev *dev,
+ struct mlx5_core_qp *qp,
+ struct mlx5_create_qp_mbox_in *in,
+ int inlen);
+int mlx5_core_qp_modify(struct mlx5_core_dev *dev, enum mlx5_qp_state cur_state,
+ enum mlx5_qp_state new_state,
+ struct mlx5_modify_qp_mbox_in *in, int sqd_event,
+ struct mlx5_core_qp *qp);
+int mlx5_core_destroy_qp(struct mlx5_core_dev *dev,
+ struct mlx5_core_qp *qp);
+int mlx5_core_qp_query(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
+ struct mlx5_query_qp_mbox_out *out, int outlen);
+
+int mlx5_core_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn);
+int mlx5_core_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn);
+void mlx5_init_qp_table(struct mlx5_core_dev *dev);
+void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev);
+int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp);
+void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp);
+
+#endif /* MLX5_QP_H */
diff --git a/include/linux/mlx5/srq.h b/include/linux/mlx5/srq.h
new file mode 100644
index 0000000..e1a363a
--- /dev/null
+++ b/include/linux/mlx5/srq.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, Mellanox Technologies 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.
+ */
+
+#ifndef MLX5_SRQ_H
+#define MLX5_SRQ_H
+
+#include <linux/mlx5/driver.h>
+
+void mlx5_init_srq_table(struct mlx5_core_dev *dev);
+void mlx5_cleanup_srq_table(struct mlx5_core_dev *dev);
+
+#endif /* MLX5_SRQ_H */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index ace9a5f..fb425aa 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -330,12 +330,9 @@
unsigned long (*get_unmapped_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags);
- void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
#endif
unsigned long mmap_base; /* base of mmap area */
unsigned long task_size; /* size of task vm space */
- unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */
- unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */
unsigned long highest_vm_end; /* highest vma end address */
pgd_t * pgd;
atomic_t mm_users; /* How many users with user space? */
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 137b419..27d9da3 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -439,7 +439,7 @@
extern int param_set_copystring(const char *val, const struct kernel_param *);
extern int param_get_string(char *buffer, const struct kernel_param *kp);
-/* for exporting parameters in /sys/parameters */
+/* for exporting parameters in /sys/module/.../parameters */
struct module;
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index 3793ed7..ccd4260 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -78,40 +78,6 @@
#endif
};
-struct ww_class {
- atomic_long_t stamp;
- struct lock_class_key acquire_key;
- struct lock_class_key mutex_key;
- const char *acquire_name;
- const char *mutex_name;
-};
-
-struct ww_acquire_ctx {
- struct task_struct *task;
- unsigned long stamp;
- unsigned acquired;
-#ifdef CONFIG_DEBUG_MUTEXES
- unsigned done_acquire;
- struct ww_class *ww_class;
- struct ww_mutex *contending_lock;
-#endif
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- struct lockdep_map dep_map;
-#endif
-#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
- unsigned deadlock_inject_interval;
- unsigned deadlock_inject_countdown;
-#endif
-};
-
-struct ww_mutex {
- struct mutex base;
- struct ww_acquire_ctx *ctx;
-#ifdef CONFIG_DEBUG_MUTEXES
- struct ww_class *ww_class;
-#endif
-};
-
#ifdef CONFIG_DEBUG_MUTEXES
# include <linux/mutex-debug.h>
#else
@@ -136,11 +102,8 @@
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
, .dep_map = { .name = #lockname }
-# define __WW_CLASS_MUTEX_INITIALIZER(lockname, ww_class) \
- , .ww_class = &ww_class
#else
# define __DEP_MAP_MUTEX_INITIALIZER(lockname)
-# define __WW_CLASS_MUTEX_INITIALIZER(lockname, ww_class)
#endif
#define __MUTEX_INITIALIZER(lockname) \
@@ -150,49 +113,13 @@
__DEBUG_MUTEX_INITIALIZER(lockname) \
__DEP_MAP_MUTEX_INITIALIZER(lockname) }
-#define __WW_CLASS_INITIALIZER(ww_class) \
- { .stamp = ATOMIC_LONG_INIT(0) \
- , .acquire_name = #ww_class "_acquire" \
- , .mutex_name = #ww_class "_mutex" }
-
-#define __WW_MUTEX_INITIALIZER(lockname, class) \
- { .base = { \__MUTEX_INITIALIZER(lockname) } \
- __WW_CLASS_MUTEX_INITIALIZER(lockname, class) }
-
#define DEFINE_MUTEX(mutexname) \
struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
-#define DEFINE_WW_CLASS(classname) \
- struct ww_class classname = __WW_CLASS_INITIALIZER(classname)
-
-#define DEFINE_WW_MUTEX(mutexname, ww_class) \
- struct ww_mutex mutexname = __WW_MUTEX_INITIALIZER(mutexname, ww_class)
-
-
extern void __mutex_init(struct mutex *lock, const char *name,
struct lock_class_key *key);
/**
- * ww_mutex_init - initialize the w/w mutex
- * @lock: the mutex to be initialized
- * @ww_class: the w/w class the mutex should belong to
- *
- * Initialize the w/w mutex to unlocked state and associate it with the given
- * class.
- *
- * It is not allowed to initialize an already locked mutex.
- */
-static inline void ww_mutex_init(struct ww_mutex *lock,
- struct ww_class *ww_class)
-{
- __mutex_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
- lock->ctx = NULL;
-#ifdef CONFIG_DEBUG_MUTEXES
- lock->ww_class = ww_class;
-#endif
-}
-
-/**
* mutex_is_locked - is the mutex locked
* @lock: the mutex to be queried
*
@@ -246,291 +173,6 @@
extern int mutex_trylock(struct mutex *lock);
extern void mutex_unlock(struct mutex *lock);
-/**
- * ww_acquire_init - initialize a w/w acquire context
- * @ctx: w/w acquire context to initialize
- * @ww_class: w/w class of the context
- *
- * Initializes an context to acquire multiple mutexes of the given w/w class.
- *
- * Context-based w/w mutex acquiring can be done in any order whatsoever within
- * a given lock class. Deadlocks will be detected and handled with the
- * wait/wound logic.
- *
- * Mixing of context-based w/w mutex acquiring and single w/w mutex locking can
- * result in undetected deadlocks and is so forbidden. Mixing different contexts
- * for the same w/w class when acquiring mutexes can also result in undetected
- * deadlocks, and is hence also forbidden. Both types of abuse will be caught by
- * enabling CONFIG_PROVE_LOCKING.
- *
- * Nesting of acquire contexts for _different_ w/w classes is possible, subject
- * to the usual locking rules between different lock classes.
- *
- * An acquire context must be released with ww_acquire_fini by the same task
- * before the memory is freed. It is recommended to allocate the context itself
- * on the stack.
- */
-static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
- struct ww_class *ww_class)
-{
- ctx->task = current;
- ctx->stamp = atomic_long_inc_return(&ww_class->stamp);
- ctx->acquired = 0;
-#ifdef CONFIG_DEBUG_MUTEXES
- ctx->ww_class = ww_class;
- ctx->done_acquire = 0;
- ctx->contending_lock = NULL;
-#endif
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- debug_check_no_locks_freed((void *)ctx, sizeof(*ctx));
- lockdep_init_map(&ctx->dep_map, ww_class->acquire_name,
- &ww_class->acquire_key, 0);
- mutex_acquire(&ctx->dep_map, 0, 0, _RET_IP_);
-#endif
-#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
- ctx->deadlock_inject_interval = 1;
- ctx->deadlock_inject_countdown = ctx->stamp & 0xf;
-#endif
-}
-
-/**
- * ww_acquire_done - marks the end of the acquire phase
- * @ctx: the acquire context
- *
- * Marks the end of the acquire phase, any further w/w mutex lock calls using
- * this context are forbidden.
- *
- * Calling this function is optional, it is just useful to document w/w mutex
- * code and clearly designated the acquire phase from actually using the locked
- * data structures.
- */
-static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
-{
-#ifdef CONFIG_DEBUG_MUTEXES
- lockdep_assert_held(ctx);
-
- DEBUG_LOCKS_WARN_ON(ctx->done_acquire);
- ctx->done_acquire = 1;
-#endif
-}
-
-/**
- * ww_acquire_fini - releases a w/w acquire context
- * @ctx: the acquire context to free
- *
- * Releases a w/w acquire context. This must be called _after_ all acquired w/w
- * mutexes have been released with ww_mutex_unlock.
- */
-static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx)
-{
-#ifdef CONFIG_DEBUG_MUTEXES
- mutex_release(&ctx->dep_map, 0, _THIS_IP_);
-
- DEBUG_LOCKS_WARN_ON(ctx->acquired);
- if (!config_enabled(CONFIG_PROVE_LOCKING))
- /*
- * lockdep will normally handle this,
- * but fail without anyway
- */
- ctx->done_acquire = 1;
-
- if (!config_enabled(CONFIG_DEBUG_LOCK_ALLOC))
- /* ensure ww_acquire_fini will still fail if called twice */
- ctx->acquired = ~0U;
-#endif
-}
-
-extern int __must_check __ww_mutex_lock(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx);
-extern int __must_check __ww_mutex_lock_interruptible(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx);
-
-/**
- * ww_mutex_lock - acquire the w/w mutex
- * @lock: the mutex to be acquired
- * @ctx: w/w acquire context, or NULL to acquire only a single lock.
- *
- * Lock the w/w mutex exclusively for this task.
- *
- * Deadlocks within a given w/w class of locks are detected and handled with the
- * wait/wound algorithm. If the lock isn't immediately avaiable this function
- * will either sleep until it is (wait case). Or it selects the current context
- * for backing off by returning -EDEADLK (wound case). Trying to acquire the
- * same lock with the same context twice is also detected and signalled by
- * returning -EALREADY. Returns 0 if the mutex was successfully acquired.
- *
- * In the wound case the caller must release all currently held w/w mutexes for
- * the given context and then wait for this contending lock to be available by
- * calling ww_mutex_lock_slow. Alternatively callers can opt to not acquire this
- * lock and proceed with trying to acquire further w/w mutexes (e.g. when
- * scanning through lru lists trying to free resources).
- *
- * The mutex must later on be released by the same task that
- * acquired it. The task may not exit without first unlocking the mutex. Also,
- * kernel memory where the mutex resides must not be freed with the mutex still
- * locked. The mutex must first be initialized (or statically defined) before it
- * can be locked. memset()-ing the mutex to 0 is not allowed. The mutex must be
- * of the same w/w lock class as was used to initialize the acquire context.
- *
- * A mutex acquired with this function must be released with ww_mutex_unlock.
- */
-static inline int ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
-{
- if (ctx)
- return __ww_mutex_lock(lock, ctx);
- else {
- mutex_lock(&lock->base);
- return 0;
- }
-}
-
-/**
- * ww_mutex_lock_interruptible - acquire the w/w mutex, interruptible
- * @lock: the mutex to be acquired
- * @ctx: w/w acquire context
- *
- * Lock the w/w mutex exclusively for this task.
- *
- * Deadlocks within a given w/w class of locks are detected and handled with the
- * wait/wound algorithm. If the lock isn't immediately avaiable this function
- * will either sleep until it is (wait case). Or it selects the current context
- * for backing off by returning -EDEADLK (wound case). Trying to acquire the
- * same lock with the same context twice is also detected and signalled by
- * returning -EALREADY. Returns 0 if the mutex was successfully acquired. If a
- * signal arrives while waiting for the lock then this function returns -EINTR.
- *
- * In the wound case the caller must release all currently held w/w mutexes for
- * the given context and then wait for this contending lock to be available by
- * calling ww_mutex_lock_slow_interruptible. Alternatively callers can opt to
- * not acquire this lock and proceed with trying to acquire further w/w mutexes
- * (e.g. when scanning through lru lists trying to free resources).
- *
- * The mutex must later on be released by the same task that
- * acquired it. The task may not exit without first unlocking the mutex. Also,
- * kernel memory where the mutex resides must not be freed with the mutex still
- * locked. The mutex must first be initialized (or statically defined) before it
- * can be locked. memset()-ing the mutex to 0 is not allowed. The mutex must be
- * of the same w/w lock class as was used to initialize the acquire context.
- *
- * A mutex acquired with this function must be released with ww_mutex_unlock.
- */
-static inline int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx)
-{
- if (ctx)
- return __ww_mutex_lock_interruptible(lock, ctx);
- else
- return mutex_lock_interruptible(&lock->base);
-}
-
-/**
- * ww_mutex_lock_slow - slowpath acquiring of the w/w mutex
- * @lock: the mutex to be acquired
- * @ctx: w/w acquire context
- *
- * Acquires a w/w mutex with the given context after a wound case. This function
- * will sleep until the lock becomes available.
- *
- * The caller must have released all w/w mutexes already acquired with the
- * context and then call this function on the contended lock.
- *
- * Afterwards the caller may continue to (re)acquire the other w/w mutexes it
- * needs with ww_mutex_lock. Note that the -EALREADY return code from
- * ww_mutex_lock can be used to avoid locking this contended mutex twice.
- *
- * It is forbidden to call this function with any other w/w mutexes associated
- * with the context held. It is forbidden to call this on anything else than the
- * contending mutex.
- *
- * Note that the slowpath lock acquiring can also be done by calling
- * ww_mutex_lock directly. This function here is simply to help w/w mutex
- * locking code readability by clearly denoting the slowpath.
- */
-static inline void
-ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
-{
- int ret;
-#ifdef CONFIG_DEBUG_MUTEXES
- DEBUG_LOCKS_WARN_ON(!ctx->contending_lock);
-#endif
- ret = ww_mutex_lock(lock, ctx);
- (void)ret;
-}
-
-/**
- * ww_mutex_lock_slow_interruptible - slowpath acquiring of the w/w mutex,
- * interruptible
- * @lock: the mutex to be acquired
- * @ctx: w/w acquire context
- *
- * Acquires a w/w mutex with the given context after a wound case. This function
- * will sleep until the lock becomes available and returns 0 when the lock has
- * been acquired. If a signal arrives while waiting for the lock then this
- * function returns -EINTR.
- *
- * The caller must have released all w/w mutexes already acquired with the
- * context and then call this function on the contended lock.
- *
- * Afterwards the caller may continue to (re)acquire the other w/w mutexes it
- * needs with ww_mutex_lock. Note that the -EALREADY return code from
- * ww_mutex_lock can be used to avoid locking this contended mutex twice.
- *
- * It is forbidden to call this function with any other w/w mutexes associated
- * with the given context held. It is forbidden to call this on anything else
- * than the contending mutex.
- *
- * Note that the slowpath lock acquiring can also be done by calling
- * ww_mutex_lock_interruptible directly. This function here is simply to help
- * w/w mutex locking code readability by clearly denoting the slowpath.
- */
-static inline int __must_check
-ww_mutex_lock_slow_interruptible(struct ww_mutex *lock,
- struct ww_acquire_ctx *ctx)
-{
-#ifdef CONFIG_DEBUG_MUTEXES
- DEBUG_LOCKS_WARN_ON(!ctx->contending_lock);
-#endif
- return ww_mutex_lock_interruptible(lock, ctx);
-}
-
-extern void ww_mutex_unlock(struct ww_mutex *lock);
-
-/**
- * ww_mutex_trylock - tries to acquire the w/w mutex without acquire context
- * @lock: mutex to lock
- *
- * Trylocks a mutex without acquire context, so no deadlock detection is
- * possible. Returns 1 if the mutex has been acquired successfully, 0 otherwise.
- */
-static inline int __must_check ww_mutex_trylock(struct ww_mutex *lock)
-{
- return mutex_trylock(&lock->base);
-}
-
-/***
- * ww_mutex_destroy - mark a w/w mutex unusable
- * @lock: the mutex to be destroyed
- *
- * This function marks the mutex uninitialized, and any subsequent
- * use of the mutex is forbidden. The mutex must not be locked when
- * this function is called.
- */
-static inline void ww_mutex_destroy(struct ww_mutex *lock)
-{
- mutex_destroy(&lock->base);
-}
-
-/**
- * ww_mutex_is_locked - is the w/w mutex locked
- * @lock: the mutex to be queried
- *
- * Returns 1 if the mutex is locked, 0 if unlocked.
- */
-static inline bool ww_mutex_is_locked(struct ww_mutex *lock)
-{
- return mutex_is_locked(&lock->base);
-}
-
extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
#ifndef CONFIG_HAVE_ARCH_MUTEX_CPU_RELAX
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 0b17629..7125cef 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -348,6 +348,7 @@
extern int nfs_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode);
+extern int nfs_attribute_cache_expired(struct inode *inode);
extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index db50840..6a45fb5 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -46,7 +46,7 @@
#ifdef CONFIG_LOCKUP_DETECTOR
int hw_nmi_is_cpu_stuck(struct pt_regs *);
u64 hw_nmi_get_sample_period(int watchdog_thresh);
-extern int watchdog_enabled;
+extern int watchdog_user_enabled;
extern int watchdog_thresh;
struct ctl_table;
extern int proc_dowatchdog(struct ctl_table *, int ,
diff --git a/include/linux/platform_data/remoteproc-omap.h b/include/linux/platform_data/remoteproc-omap.h
index 3c1c644..bfbd12b 100644
--- a/include/linux/platform_data/remoteproc-omap.h
+++ b/include/linux/platform_data/remoteproc-omap.h
@@ -50,7 +50,7 @@
#else
-void __init omap_rproc_reserve_cma(void)
+static inline void __init omap_rproc_reserve_cma(void)
{
}
diff --git a/include/linux/reservation.h b/include/linux/reservation.h
index e9ee806..813dae9 100644
--- a/include/linux/reservation.h
+++ b/include/linux/reservation.h
@@ -39,7 +39,7 @@
#ifndef _LINUX_RESERVATION_H
#define _LINUX_RESERVATION_H
-#include <linux/mutex.h>
+#include <linux/ww_mutex.h>
extern struct ww_class reservation_ww_class;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f99d57e..50d04b9 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -322,8 +322,6 @@
arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff,
unsigned long flags);
-extern void arch_unmap_area(struct mm_struct *, unsigned long);
-extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
#else
static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
#endif
diff --git a/include/linux/socket.h b/include/linux/socket.h
index b10ce4b..230c04b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -167,6 +167,7 @@
#define AF_PPPOX 24 /* PPPoX sockets */
#define AF_WANPIPE 25 /* Wanpipe API Sockets */
#define AF_LLC 26 /* Linux LLC */
+#define AF_IB 27 /* Native InfiniBand address */
#define AF_CAN 29 /* Controller Area Network */
#define AF_TIPC 30 /* TIPC sockets */
#define AF_BLUETOOTH 31 /* Bluetooth sockets */
@@ -211,6 +212,7 @@
#define PF_PPPOX AF_PPPOX
#define PF_WANPIPE AF_WANPIPE
#define PF_LLC AF_LLC
+#define PF_IB AF_IB
#define PF_CAN AF_CAN
#define PF_TIPC AF_TIPC
#define PF_BLUETOOTH AF_BLUETOOTH
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 303399b..6ce690d 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -57,6 +57,7 @@
#define CACHE_VALID 0 /* Entry contains valid data */
#define CACHE_NEGATIVE 1 /* Negative entry - there is no match for the key */
#define CACHE_PENDING 2 /* An upcall has been sent but no reply received yet*/
+#define CACHE_CLEANED 3 /* Entry has been cleaned from cache */
#define CACHE_NEW_EXPIRY 120 /* keep new things pending confirmation for 120 seconds */
@@ -148,6 +149,24 @@
int too_many);
};
+/*
+ * timestamps kept in the cache are expressed in seconds
+ * since boot. This is the best for measuring differences in
+ * real time.
+ */
+static inline time_t seconds_since_boot(void)
+{
+ struct timespec boot;
+ getboottime(&boot);
+ return get_seconds() - boot.tv_sec;
+}
+
+static inline time_t convert_to_wallclock(time_t sinceboot)
+{
+ struct timespec boot;
+ getboottime(&boot);
+ return boot.tv_sec + sinceboot;
+}
extern const struct file_operations cache_file_operations_pipefs;
extern const struct file_operations content_file_operations_pipefs;
@@ -181,15 +200,10 @@
kref_put(&h->ref, cd->cache_put);
}
-static inline int cache_valid(struct cache_head *h)
+static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
{
- /* If an item has been unhashed pending removal when
- * the refcount drops to 0, the expiry_time will be
- * set to 0. We don't want to consider such items
- * valid in this context even though CACHE_VALID is
- * set.
- */
- return (h->expiry_time != 0 && test_bit(CACHE_VALID, &h->flags));
+ return (h->expiry_time < seconds_since_boot()) ||
+ (detail->flush_time > h->last_refresh);
}
extern int cache_check(struct cache_detail *detail,
@@ -250,25 +264,6 @@
return 0;
}
-/*
- * timestamps kept in the cache are expressed in seconds
- * since boot. This is the best for measuring differences in
- * real time.
- */
-static inline time_t seconds_since_boot(void)
-{
- struct timespec boot;
- getboottime(&boot);
- return get_seconds() - boot.tv_sec;
-}
-
-static inline time_t convert_to_wallclock(time_t sinceboot)
-{
- struct timespec boot;
- getboottime(&boot);
- return boot.tv_sec + sinceboot;
-}
-
static inline time_t get_expiry(char **bpp)
{
int rv;
diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h
index 161463e..1f911cc 100644
--- a/include/linux/sunrpc/gss_api.h
+++ b/include/linux/sunrpc/gss_api.h
@@ -151,6 +151,8 @@
/* Fill in an array with a list of supported pseudoflavors */
int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
+struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
+
/* For every successful gss_mech_get or gss_mech_get_by_* call there must be a
* corresponding call to gss_mech_put. */
void gss_mech_put(struct gss_api_mech *);
diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h
index ff374ab..8d71d65 100644
--- a/include/linux/sunrpc/svcauth.h
+++ b/include/linux/sunrpc/svcauth.h
@@ -14,6 +14,7 @@
#include <linux/string.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/cache.h>
+#include <linux/sunrpc/gss_api.h>
#include <linux/hash.h>
#include <linux/cred.h>
@@ -23,13 +24,23 @@
struct group_info *cr_group_info;
u32 cr_flavor; /* pseudoflavor */
char *cr_principal; /* for gss */
+ struct gss_api_mech *cr_gss_mech;
};
+static inline void init_svc_cred(struct svc_cred *cred)
+{
+ cred->cr_group_info = NULL;
+ cred->cr_principal = NULL;
+ cred->cr_gss_mech = NULL;
+}
+
static inline void free_svc_cred(struct svc_cred *cred)
{
if (cred->cr_group_info)
put_group_info(cred->cr_group_info);
kfree(cred->cr_principal);
+ gss_mech_put(cred->cr_gss_mech);
+ init_svc_cred(cred);
}
struct svc_rqst; /* forward decl */
diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h
index ca3ad41..b300787 100644
--- a/include/linux/virtio_ring.h
+++ b/include/linux/virtio_ring.h
@@ -1,6 +1,7 @@
#ifndef _LINUX_VIRTIO_RING_H
#define _LINUX_VIRTIO_RING_H
+#include <asm/barrier.h>
#include <linux/irqreturn.h>
#include <uapi/linux/virtio_ring.h>
diff --git a/include/linux/ww_mutex.h b/include/linux/ww_mutex.h
new file mode 100644
index 0000000..760399a
--- /dev/null
+++ b/include/linux/ww_mutex.h
@@ -0,0 +1,378 @@
+/*
+ * Wound/Wait Mutexes: blocking mutual exclusion locks with deadlock avoidance
+ *
+ * Original mutex implementation started by Ingo Molnar:
+ *
+ * Copyright (C) 2004, 2005, 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
+ *
+ * Wound/wait implementation:
+ * Copyright (C) 2013 Canonical Ltd.
+ *
+ * This file contains the main data structure and API definitions.
+ */
+
+#ifndef __LINUX_WW_MUTEX_H
+#define __LINUX_WW_MUTEX_H
+
+#include <linux/mutex.h>
+
+struct ww_class {
+ atomic_long_t stamp;
+ struct lock_class_key acquire_key;
+ struct lock_class_key mutex_key;
+ const char *acquire_name;
+ const char *mutex_name;
+};
+
+struct ww_acquire_ctx {
+ struct task_struct *task;
+ unsigned long stamp;
+ unsigned acquired;
+#ifdef CONFIG_DEBUG_MUTEXES
+ unsigned done_acquire;
+ struct ww_class *ww_class;
+ struct ww_mutex *contending_lock;
+#endif
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct lockdep_map dep_map;
+#endif
+#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
+ unsigned deadlock_inject_interval;
+ unsigned deadlock_inject_countdown;
+#endif
+};
+
+struct ww_mutex {
+ struct mutex base;
+ struct ww_acquire_ctx *ctx;
+#ifdef CONFIG_DEBUG_MUTEXES
+ struct ww_class *ww_class;
+#endif
+};
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define __WW_CLASS_MUTEX_INITIALIZER(lockname, ww_class) \
+ , .ww_class = &ww_class
+#else
+# define __WW_CLASS_MUTEX_INITIALIZER(lockname, ww_class)
+#endif
+
+#define __WW_CLASS_INITIALIZER(ww_class) \
+ { .stamp = ATOMIC_LONG_INIT(0) \
+ , .acquire_name = #ww_class "_acquire" \
+ , .mutex_name = #ww_class "_mutex" }
+
+#define __WW_MUTEX_INITIALIZER(lockname, class) \
+ { .base = { \__MUTEX_INITIALIZER(lockname) } \
+ __WW_CLASS_MUTEX_INITIALIZER(lockname, class) }
+
+#define DEFINE_WW_CLASS(classname) \
+ struct ww_class classname = __WW_CLASS_INITIALIZER(classname)
+
+#define DEFINE_WW_MUTEX(mutexname, ww_class) \
+ struct ww_mutex mutexname = __WW_MUTEX_INITIALIZER(mutexname, ww_class)
+
+/**
+ * ww_mutex_init - initialize the w/w mutex
+ * @lock: the mutex to be initialized
+ * @ww_class: the w/w class the mutex should belong to
+ *
+ * Initialize the w/w mutex to unlocked state and associate it with the given
+ * class.
+ *
+ * It is not allowed to initialize an already locked mutex.
+ */
+static inline void ww_mutex_init(struct ww_mutex *lock,
+ struct ww_class *ww_class)
+{
+ __mutex_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
+ lock->ctx = NULL;
+#ifdef CONFIG_DEBUG_MUTEXES
+ lock->ww_class = ww_class;
+#endif
+}
+
+/**
+ * ww_acquire_init - initialize a w/w acquire context
+ * @ctx: w/w acquire context to initialize
+ * @ww_class: w/w class of the context
+ *
+ * Initializes an context to acquire multiple mutexes of the given w/w class.
+ *
+ * Context-based w/w mutex acquiring can be done in any order whatsoever within
+ * a given lock class. Deadlocks will be detected and handled with the
+ * wait/wound logic.
+ *
+ * Mixing of context-based w/w mutex acquiring and single w/w mutex locking can
+ * result in undetected deadlocks and is so forbidden. Mixing different contexts
+ * for the same w/w class when acquiring mutexes can also result in undetected
+ * deadlocks, and is hence also forbidden. Both types of abuse will be caught by
+ * enabling CONFIG_PROVE_LOCKING.
+ *
+ * Nesting of acquire contexts for _different_ w/w classes is possible, subject
+ * to the usual locking rules between different lock classes.
+ *
+ * An acquire context must be released with ww_acquire_fini by the same task
+ * before the memory is freed. It is recommended to allocate the context itself
+ * on the stack.
+ */
+static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
+ struct ww_class *ww_class)
+{
+ ctx->task = current;
+ ctx->stamp = atomic_long_inc_return(&ww_class->stamp);
+ ctx->acquired = 0;
+#ifdef CONFIG_DEBUG_MUTEXES
+ ctx->ww_class = ww_class;
+ ctx->done_acquire = 0;
+ ctx->contending_lock = NULL;
+#endif
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ debug_check_no_locks_freed((void *)ctx, sizeof(*ctx));
+ lockdep_init_map(&ctx->dep_map, ww_class->acquire_name,
+ &ww_class->acquire_key, 0);
+ mutex_acquire(&ctx->dep_map, 0, 0, _RET_IP_);
+#endif
+#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
+ ctx->deadlock_inject_interval = 1;
+ ctx->deadlock_inject_countdown = ctx->stamp & 0xf;
+#endif
+}
+
+/**
+ * ww_acquire_done - marks the end of the acquire phase
+ * @ctx: the acquire context
+ *
+ * Marks the end of the acquire phase, any further w/w mutex lock calls using
+ * this context are forbidden.
+ *
+ * Calling this function is optional, it is just useful to document w/w mutex
+ * code and clearly designated the acquire phase from actually using the locked
+ * data structures.
+ */
+static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
+{
+#ifdef CONFIG_DEBUG_MUTEXES
+ lockdep_assert_held(ctx);
+
+ DEBUG_LOCKS_WARN_ON(ctx->done_acquire);
+ ctx->done_acquire = 1;
+#endif
+}
+
+/**
+ * ww_acquire_fini - releases a w/w acquire context
+ * @ctx: the acquire context to free
+ *
+ * Releases a w/w acquire context. This must be called _after_ all acquired w/w
+ * mutexes have been released with ww_mutex_unlock.
+ */
+static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx)
+{
+#ifdef CONFIG_DEBUG_MUTEXES
+ mutex_release(&ctx->dep_map, 0, _THIS_IP_);
+
+ DEBUG_LOCKS_WARN_ON(ctx->acquired);
+ if (!config_enabled(CONFIG_PROVE_LOCKING))
+ /*
+ * lockdep will normally handle this,
+ * but fail without anyway
+ */
+ ctx->done_acquire = 1;
+
+ if (!config_enabled(CONFIG_DEBUG_LOCK_ALLOC))
+ /* ensure ww_acquire_fini will still fail if called twice */
+ ctx->acquired = ~0U;
+#endif
+}
+
+extern int __must_check __ww_mutex_lock(struct ww_mutex *lock,
+ struct ww_acquire_ctx *ctx);
+extern int __must_check __ww_mutex_lock_interruptible(struct ww_mutex *lock,
+ struct ww_acquire_ctx *ctx);
+
+/**
+ * ww_mutex_lock - acquire the w/w mutex
+ * @lock: the mutex to be acquired
+ * @ctx: w/w acquire context, or NULL to acquire only a single lock.
+ *
+ * Lock the w/w mutex exclusively for this task.
+ *
+ * Deadlocks within a given w/w class of locks are detected and handled with the
+ * wait/wound algorithm. If the lock isn't immediately avaiable this function
+ * will either sleep until it is (wait case). Or it selects the current context
+ * for backing off by returning -EDEADLK (wound case). Trying to acquire the
+ * same lock with the same context twice is also detected and signalled by
+ * returning -EALREADY. Returns 0 if the mutex was successfully acquired.
+ *
+ * In the wound case the caller must release all currently held w/w mutexes for
+ * the given context and then wait for this contending lock to be available by
+ * calling ww_mutex_lock_slow. Alternatively callers can opt to not acquire this
+ * lock and proceed with trying to acquire further w/w mutexes (e.g. when
+ * scanning through lru lists trying to free resources).
+ *
+ * The mutex must later on be released by the same task that
+ * acquired it. The task may not exit without first unlocking the mutex. Also,
+ * kernel memory where the mutex resides must not be freed with the mutex still
+ * locked. The mutex must first be initialized (or statically defined) before it
+ * can be locked. memset()-ing the mutex to 0 is not allowed. The mutex must be
+ * of the same w/w lock class as was used to initialize the acquire context.
+ *
+ * A mutex acquired with this function must be released with ww_mutex_unlock.
+ */
+static inline int ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+ if (ctx)
+ return __ww_mutex_lock(lock, ctx);
+
+ mutex_lock(&lock->base);
+ return 0;
+}
+
+/**
+ * ww_mutex_lock_interruptible - acquire the w/w mutex, interruptible
+ * @lock: the mutex to be acquired
+ * @ctx: w/w acquire context
+ *
+ * Lock the w/w mutex exclusively for this task.
+ *
+ * Deadlocks within a given w/w class of locks are detected and handled with the
+ * wait/wound algorithm. If the lock isn't immediately avaiable this function
+ * will either sleep until it is (wait case). Or it selects the current context
+ * for backing off by returning -EDEADLK (wound case). Trying to acquire the
+ * same lock with the same context twice is also detected and signalled by
+ * returning -EALREADY. Returns 0 if the mutex was successfully acquired. If a
+ * signal arrives while waiting for the lock then this function returns -EINTR.
+ *
+ * In the wound case the caller must release all currently held w/w mutexes for
+ * the given context and then wait for this contending lock to be available by
+ * calling ww_mutex_lock_slow_interruptible. Alternatively callers can opt to
+ * not acquire this lock and proceed with trying to acquire further w/w mutexes
+ * (e.g. when scanning through lru lists trying to free resources).
+ *
+ * The mutex must later on be released by the same task that
+ * acquired it. The task may not exit without first unlocking the mutex. Also,
+ * kernel memory where the mutex resides must not be freed with the mutex still
+ * locked. The mutex must first be initialized (or statically defined) before it
+ * can be locked. memset()-ing the mutex to 0 is not allowed. The mutex must be
+ * of the same w/w lock class as was used to initialize the acquire context.
+ *
+ * A mutex acquired with this function must be released with ww_mutex_unlock.
+ */
+static inline int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock,
+ struct ww_acquire_ctx *ctx)
+{
+ if (ctx)
+ return __ww_mutex_lock_interruptible(lock, ctx);
+ else
+ return mutex_lock_interruptible(&lock->base);
+}
+
+/**
+ * ww_mutex_lock_slow - slowpath acquiring of the w/w mutex
+ * @lock: the mutex to be acquired
+ * @ctx: w/w acquire context
+ *
+ * Acquires a w/w mutex with the given context after a wound case. This function
+ * will sleep until the lock becomes available.
+ *
+ * The caller must have released all w/w mutexes already acquired with the
+ * context and then call this function on the contended lock.
+ *
+ * Afterwards the caller may continue to (re)acquire the other w/w mutexes it
+ * needs with ww_mutex_lock. Note that the -EALREADY return code from
+ * ww_mutex_lock can be used to avoid locking this contended mutex twice.
+ *
+ * It is forbidden to call this function with any other w/w mutexes associated
+ * with the context held. It is forbidden to call this on anything else than the
+ * contending mutex.
+ *
+ * Note that the slowpath lock acquiring can also be done by calling
+ * ww_mutex_lock directly. This function here is simply to help w/w mutex
+ * locking code readability by clearly denoting the slowpath.
+ */
+static inline void
+ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+ int ret;
+#ifdef CONFIG_DEBUG_MUTEXES
+ DEBUG_LOCKS_WARN_ON(!ctx->contending_lock);
+#endif
+ ret = ww_mutex_lock(lock, ctx);
+ (void)ret;
+}
+
+/**
+ * ww_mutex_lock_slow_interruptible - slowpath acquiring of the w/w mutex, interruptible
+ * @lock: the mutex to be acquired
+ * @ctx: w/w acquire context
+ *
+ * Acquires a w/w mutex with the given context after a wound case. This function
+ * will sleep until the lock becomes available and returns 0 when the lock has
+ * been acquired. If a signal arrives while waiting for the lock then this
+ * function returns -EINTR.
+ *
+ * The caller must have released all w/w mutexes already acquired with the
+ * context and then call this function on the contended lock.
+ *
+ * Afterwards the caller may continue to (re)acquire the other w/w mutexes it
+ * needs with ww_mutex_lock. Note that the -EALREADY return code from
+ * ww_mutex_lock can be used to avoid locking this contended mutex twice.
+ *
+ * It is forbidden to call this function with any other w/w mutexes associated
+ * with the given context held. It is forbidden to call this on anything else
+ * than the contending mutex.
+ *
+ * Note that the slowpath lock acquiring can also be done by calling
+ * ww_mutex_lock_interruptible directly. This function here is simply to help
+ * w/w mutex locking code readability by clearly denoting the slowpath.
+ */
+static inline int __must_check
+ww_mutex_lock_slow_interruptible(struct ww_mutex *lock,
+ struct ww_acquire_ctx *ctx)
+{
+#ifdef CONFIG_DEBUG_MUTEXES
+ DEBUG_LOCKS_WARN_ON(!ctx->contending_lock);
+#endif
+ return ww_mutex_lock_interruptible(lock, ctx);
+}
+
+extern void ww_mutex_unlock(struct ww_mutex *lock);
+
+/**
+ * ww_mutex_trylock - tries to acquire the w/w mutex without acquire context
+ * @lock: mutex to lock
+ *
+ * Trylocks a mutex without acquire context, so no deadlock detection is
+ * possible. Returns 1 if the mutex has been acquired successfully, 0 otherwise.
+ */
+static inline int __must_check ww_mutex_trylock(struct ww_mutex *lock)
+{
+ return mutex_trylock(&lock->base);
+}
+
+/***
+ * ww_mutex_destroy - mark a w/w mutex unusable
+ * @lock: the mutex to be destroyed
+ *
+ * This function marks the mutex uninitialized, and any subsequent
+ * use of the mutex is forbidden. The mutex must not be locked when
+ * this function is called.
+ */
+static inline void ww_mutex_destroy(struct ww_mutex *lock)
+{
+ mutex_destroy(&lock->base);
+}
+
+/**
+ * ww_mutex_is_locked - is the w/w mutex locked
+ * @lock: the mutex to be queried
+ *
+ * Returns 1 if the mutex is locked, 0 if unlocked.
+ */
+static inline bool ww_mutex_is_locked(struct ww_mutex *lock)
+{
+ return mutex_is_locked(&lock->base);
+}
+
+#endif
diff --git a/include/linux/zbud.h b/include/linux/zbud.h
new file mode 100644
index 0000000..2571a5c
--- /dev/null
+++ b/include/linux/zbud.h
@@ -0,0 +1,22 @@
+#ifndef _ZBUD_H_
+#define _ZBUD_H_
+
+#include <linux/types.h>
+
+struct zbud_pool;
+
+struct zbud_ops {
+ int (*evict)(struct zbud_pool *pool, unsigned long handle);
+};
+
+struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
+void zbud_destroy_pool(struct zbud_pool *pool);
+int zbud_alloc(struct zbud_pool *pool, int size, gfp_t gfp,
+ unsigned long *handle);
+void zbud_free(struct zbud_pool *pool, unsigned long handle);
+int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
+void *zbud_map(struct zbud_pool *pool, unsigned long handle);
+void zbud_unmap(struct zbud_pool *pool, unsigned long handle);
+u64 zbud_get_pool_size(struct zbud_pool *pool);
+
+#endif /* _ZBUD_H_ */
diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h
index 42628fc..de59364 100644
--- a/include/media/davinci/vpbe_osd.h
+++ b/include/media/davinci/vpbe_osd.h
@@ -82,9 +82,9 @@
PIXFMT_4BPP,
PIXFMT_8BPP,
PIXFMT_RGB565,
- PIXFMT_YCbCrI,
+ PIXFMT_YCBCRI,
PIXFMT_RGB888,
- PIXFMT_YCrCbI,
+ PIXFMT_YCRCBI,
PIXFMT_NV12,
PIXFMT_OSD_ATTR,
};
diff --git a/include/media/media-device.h b/include/media/media-device.h
index eaade98..12155a9 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -45,6 +45,7 @@
* @entities: List of registered entities
* @lock: Entities list lock
* @graph_mutex: Entities graph operation lock
+ * @link_notify: Link state change notification callback
*
* This structure represents an abstract high-level media device. It allows easy
* access to entities and provides basic media device-level support. The
@@ -75,10 +76,14 @@
/* Serializes graph operations. */
struct mutex graph_mutex;
- int (*link_notify)(struct media_pad *source,
- struct media_pad *sink, u32 flags);
+ int (*link_notify)(struct media_link *link, u32 flags,
+ unsigned int notification);
};
+/* Supported link_notify @notification values. */
+#define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0
+#define MEDIA_DEV_NOTIFY_POST_LINK_CH 1
+
/* media_devnode to media_device */
#define to_media_device(node) container_of(node, struct media_device, devnode)
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index 0c16f51..06bacf9 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -128,11 +128,14 @@
int media_entity_create_link(struct media_entity *source, u16 source_pad,
struct media_entity *sink, u16 sink_pad, u32 flags);
+void __media_entity_remove_links(struct media_entity *entity);
+void media_entity_remove_links(struct media_entity *entity);
+
int __media_entity_setup_link(struct media_link *link, u32 flags);
int media_entity_setup_link(struct media_link *link, u32 flags);
struct media_link *media_entity_find_link(struct media_pad *source,
struct media_pad *sink);
-struct media_pad *media_entity_remote_source(struct media_pad *pad);
+struct media_pad *media_entity_remote_pad(struct media_pad *pad);
struct media_entity *media_entity_get(struct media_entity *entity);
void media_entity_put(struct media_entity *entity);
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 5d5d3a3..6628f5d 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -111,6 +111,7 @@
#define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old"
#define RC_MAP_CINERGY_1400 "rc-cinergy-1400"
#define RC_MAP_CINERGY "rc-cinergy"
+#define RC_MAP_DELOCK_61959 "rc-delock-61959"
#define RC_MAP_DIB0700_NEC_TABLE "rc-dib0700-nec"
#define RC_MAP_DIB0700_RC5_TABLE "rc-dib0700-rc5"
#define RC_MAP_DIGITALNOW_TINYTWIN "rc-digitalnow-tinytwin"
diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h
index f509690..b975c28 100644
--- a/include/media/s5p_fimc.h
+++ b/include/media/s5p_fimc.h
@@ -13,6 +13,7 @@
#define S5P_FIMC_H_
#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
#include <media/v4l2-mediabus.h>
/*
@@ -115,6 +116,7 @@
* @color: the driver's private color format id
* @memplanes: number of physically non-contiguous data planes
* @colplanes: number of physically contiguous data planes
+ * @colorspace: v4l2 colorspace (V4L2_COLORSPACE_*)
* @depth: per plane driver's private 'number of bits per pixel'
* @mdataplanes: bitmask indicating meta data plane(s), (1 << plane_no)
* @flags: flags indicating which operation mode format applies to
@@ -126,6 +128,7 @@
u32 color;
u16 memplanes;
u16 colplanes;
+ u8 colorspace;
u8 depth[FIMC_MAX_PLANES];
u16 mdataplanes;
u16 flags;
@@ -140,37 +143,40 @@
#define FMT_FLAGS_YUV (1 << 7)
};
-enum fimc_subdev_index {
- IDX_SENSOR,
- IDX_CSIS,
- IDX_FLITE,
- IDX_IS_ISP,
- IDX_FIMC,
- IDX_MAX,
-};
-
-struct media_pipeline;
-struct v4l2_subdev;
-
-struct fimc_pipeline {
- struct v4l2_subdev *subdevs[IDX_MAX];
- struct media_pipeline *m_pipeline;
-};
+struct exynos_media_pipeline;
/*
- * Media pipeline operations to be called from within the fimc(-lite)
- * video node when it is the last entity of the pipeline. Implemented
- * by corresponding media device driver.
+ * Media pipeline operations to be called from within a video node, i.e. the
+ * last entity within the pipeline. Implemented by related media device driver.
*/
-struct fimc_pipeline_ops {
- int (*open)(struct fimc_pipeline *p, struct media_entity *me,
- bool resume);
- int (*close)(struct fimc_pipeline *p);
- int (*set_stream)(struct fimc_pipeline *p, bool state);
+struct exynos_media_pipeline_ops {
+ int (*prepare)(struct exynos_media_pipeline *p,
+ struct media_entity *me);
+ int (*unprepare)(struct exynos_media_pipeline *p);
+ int (*open)(struct exynos_media_pipeline *p, struct media_entity *me,
+ bool resume);
+ int (*close)(struct exynos_media_pipeline *p);
+ int (*set_stream)(struct exynos_media_pipeline *p, bool state);
};
-#define fimc_pipeline_call(f, op, p, args...) \
- (!(f) ? -ENODEV : (((f)->pipeline_ops && (f)->pipeline_ops->op) ? \
- (f)->pipeline_ops->op((p), ##args) : -ENOIOCTLCMD))
+struct exynos_video_entity {
+ struct video_device vdev;
+ struct exynos_media_pipeline *pipe;
+};
+
+struct exynos_media_pipeline {
+ struct media_pipeline mp;
+ const struct exynos_media_pipeline_ops *ops;
+};
+
+static inline struct exynos_video_entity *vdev_to_exynos_video_entity(
+ struct video_device *vdev)
+{
+ return container_of(vdev, struct exynos_video_entity, vdev);
+}
+
+#define fimc_pipeline_call(ent, op, args...) \
+ (!(ent) ? -ENOENT : (((ent)->pipe->ops && (ent)->pipe->ops->op) ? \
+ (ent)->pipe->ops->op(((ent)->pipe), ##args) : -ENOIOCTLCMD)) \
#endif /* S5P_FIMC_H_ */
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
index 6fdb6ad..7f57056 100644
--- a/include/media/sh_mobile_ceu.h
+++ b/include/media/sh_mobile_ceu.h
@@ -22,6 +22,8 @@
int max_width;
int max_height;
struct sh_mobile_ceu_companion *csi2;
+ struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */
+ unsigned int *asd_sizes; /* 0-terminated array pf asd group sizes */
};
#endif /* __ASM_SH_MOBILE_CEU_H__ */
diff --git a/include/media/sh_mobile_csi2.h b/include/media/sh_mobile_csi2.h
index c586c4f..14030db 100644
--- a/include/media/sh_mobile_csi2.h
+++ b/include/media/sh_mobile_csi2.h
@@ -33,6 +33,7 @@
unsigned char lanes; /* bitmask[3:0] */
unsigned char channel; /* 0..3 */
struct platform_device *pdev; /* client platform device */
+ const char *name; /* async matching: client name */
};
struct v4l2_device;
@@ -42,7 +43,6 @@
unsigned int flags;
struct sh_csi2_client_config *clients;
int num_clients;
- struct v4l2_device *v4l2_dev;
};
#endif
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index ff77d08..34d2414 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -19,11 +19,13 @@
#include <linux/videodev2.h>
#include <media/videobuf-core.h>
#include <media/videobuf2-core.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
struct file;
struct soc_camera_desc;
+struct soc_camera_async_client;
struct soc_camera_device {
struct list_head list; /* list of all registered devices */
@@ -49,6 +51,10 @@
/* soc_camera.c private count. Only accessed with .host_lock held */
int use_count;
struct file *streamer; /* stream owner */
+ struct v4l2_clk *clk;
+ /* Asynchronous subdevice management */
+ struct soc_camera_async_client *sasc;
+ /* video buffer queue */
union {
struct videobuf_queue vb_vidq;
struct vb2_queue vb2_vidq;
@@ -58,21 +64,38 @@
/* Host supports programmable stride */
#define SOCAM_HOST_CAP_STRIDE (1 << 0)
+enum soc_camera_subdev_role {
+ SOCAM_SUBDEV_DATA_SOURCE = 1,
+ SOCAM_SUBDEV_DATA_SINK,
+ SOCAM_SUBDEV_DATA_PROCESSOR,
+};
+
+struct soc_camera_async_subdev {
+ struct v4l2_async_subdev asd;
+ enum soc_camera_subdev_role role;
+};
+
struct soc_camera_host {
struct v4l2_device v4l2_dev;
struct list_head list;
- struct mutex host_lock; /* Protect pipeline modifications */
+ struct mutex host_lock; /* Main synchronisation lock */
+ struct mutex clk_lock; /* Protect pipeline modifications */
unsigned char nr; /* Host number */
u32 capabilities;
+ struct soc_camera_device *icd; /* Currently attached client */
void *priv;
const char *drv_name;
struct soc_camera_host_ops *ops;
+ struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */
+ unsigned int *asd_sizes; /* 0-terminated array of asd group sizes */
};
struct soc_camera_host_ops {
struct module *owner;
int (*add)(struct soc_camera_device *);
void (*remove)(struct soc_camera_device *);
+ int (*clock_start)(struct soc_camera_host *);
+ void (*clock_stop)(struct soc_camera_host *);
/*
* .get_formats() is called for each client device format, but
* .put_formats() is only called once. Further, if any of the calls to
@@ -157,6 +180,7 @@
};
/*
+ * Platform data for "soc-camera-pdrv"
* This MUST be kept binary-identical to struct soc_camera_link below, until
* it is completely replaced by this one, after which we can split it into its
* two components.
@@ -322,14 +346,17 @@
unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd,
const struct v4l2_mbus_config *cfg);
-int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd);
-int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd);
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd);
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+ struct v4l2_clk *clk);
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+ struct v4l2_clk *clk);
static inline int soc_camera_set_power(struct device *dev,
- struct soc_camera_subdev_desc *ssdd, bool on)
+ struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on)
{
- return on ? soc_camera_power_on(dev, ssdd)
- : soc_camera_power_off(dev, ssdd);
+ return on ? soc_camera_power_on(dev, ssdd, clk)
+ : soc_camera_power_off(dev, ssdd, clk);
}
/* This is only temporary here - until v4l2-subdev begins to link to video_device */
@@ -346,9 +373,9 @@
return client->dev.platform_data;
}
-static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(const struct video_device *vdev)
+static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(struct video_device *vdev)
{
- struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
+ struct soc_camera_device *icd = video_get_drvdata(vdev);
return soc_camera_to_subdev(icd);
}
diff --git a/include/media/ths7303.h b/include/media/ths7303.h
index 980ec51..a7b4929 100644
--- a/include/media/ths7303.h
+++ b/include/media/ths7303.h
@@ -30,13 +30,11 @@
* @ch_1: Bias value for channel one.
* @ch_2: Bias value for channel two.
* @ch_3: Bias value for channel three.
- * @init_enable: initalize on init.
*/
struct ths7303_platform_data {
u8 ch_1;
u8 ch_2;
u8 ch_3;
- u8 init_enable;
};
#endif
diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h
index a8ad75a..4a1191a 100644
--- a/include/media/tveeprom.h
+++ b/include/media/tveeprom.h
@@ -1,6 +1,17 @@
/*
*/
+enum tveeprom_audio_processor {
+ /* No audio processor present */
+ TVEEPROM_AUDPROC_NONE,
+ /* The audio processor is internal to the video processor */
+ TVEEPROM_AUDPROC_INTERNAL,
+ /* The audio processor is a MSPXXXX device */
+ TVEEPROM_AUDPROC_MSP,
+ /* The audio processor is another device */
+ TVEEPROM_AUDPROC_OTHER,
+};
+
struct tveeprom {
u32 has_radio;
/* If has_ir == 0, then it is unknown what the IR capabilities are,
diff --git a/include/media/tvp7002.h b/include/media/tvp7002.h
index ee43534..fadb6af 100644
--- a/include/media/tvp7002.h
+++ b/include/media/tvp7002.h
@@ -26,31 +26,29 @@
#ifndef _TVP7002_H_
#define _TVP7002_H_
-/* Platform-dependent data
- *
- * clk_polarity:
- * 0 -> data clocked out on rising edge of DATACLK signal
- * 1 -> data clocked out on falling edge of DATACLK signal
- * hs_polarity:
- * 0 -> active low HSYNC output
- * 1 -> active high HSYNC output
- * sog_polarity:
- * 0 -> normal operation
- * 1 -> operation with polarity inverted
- * vs_polarity:
- * 0 -> active low VSYNC output
- * 1 -> active high VSYNC output
- * fid_polarity:
- * 0 -> the field ID output is set to logic 1 for an odd
- * field (field 1) and set to logic 0 for an even
- * field (field 0).
- * 1 -> operation with polarity inverted.
+#define TVP7002_MODULE_NAME "tvp7002"
+
+/**
+ * struct tvp7002_config - Platform dependent data
+ *@clk_polarity: Clock polarity
+ * 0 - Data clocked out on rising edge of DATACLK signal
+ * 1 - Data clocked out on falling edge of DATACLK signal
+ *@hs_polarity: HSYNC polarity
+ * 0 - Active low HSYNC output, 1 - Active high HSYNC output
+ *@vs_polarity: VSYNC Polarity
+ * 0 - Active low VSYNC output, 1 - Active high VSYNC output
+ *@fid_polarity: Active-high Field ID polarity.
+ * 0 - The field ID output is set to logic 1 for an odd field
+ * (field 1) and set to logic 0 for an even field (field 0).
+ * 1 - Operation with polarity inverted.
+ *@sog_polarity: Active high Sync on Green output polarity.
+ * 0 - Normal operation, 1 - Operation with polarity inverted
*/
struct tvp7002_config {
- u8 clk_polarity;
- u8 hs_polarity;
- u8 vs_polarity;
- u8 fid_polarity;
- u8 sog_polarity;
+ bool clk_polarity;
+ bool hs_polarity;
+ bool vs_polarity;
+ bool fid_polarity;
+ bool sog_polarity;
};
#endif
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
new file mode 100644
index 0000000..c3ec6ac
--- /dev/null
+++ b/include/media/v4l2-async.h
@@ -0,0 +1,105 @@
+/*
+ * V4L2 asynchronous subdevice registration API
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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 V4L2_ASYNC_H
+#define V4L2_ASYNC_H
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct device;
+struct v4l2_device;
+struct v4l2_subdev;
+struct v4l2_async_notifier;
+
+/* A random max subdevice number, used to allocate an array on stack */
+#define V4L2_MAX_SUBDEVS 128U
+
+enum v4l2_async_bus_type {
+ V4L2_ASYNC_BUS_CUSTOM,
+ V4L2_ASYNC_BUS_PLATFORM,
+ V4L2_ASYNC_BUS_I2C,
+};
+
+/**
+ * struct v4l2_async_subdev - sub-device descriptor, as known to a bridge
+ * @bus_type: subdevice bus type to select the appropriate matching method
+ * @match: union of per-bus type matching data sets
+ * @list: used to link struct v4l2_async_subdev objects, waiting to be
+ * probed, to a notifier->waiting list
+ */
+struct v4l2_async_subdev {
+ enum v4l2_async_bus_type bus_type;
+ union {
+ struct {
+ const char *name;
+ } platform;
+ struct {
+ int adapter_id;
+ unsigned short address;
+ } i2c;
+ struct {
+ bool (*match)(struct device *,
+ struct v4l2_async_subdev *);
+ void *priv;
+ } custom;
+ } match;
+
+ /* v4l2-async core private: not to be used by drivers */
+ struct list_head list;
+};
+
+/**
+ * v4l2_async_subdev_list - provided by subdevices
+ * @list: links struct v4l2_async_subdev_list objects to a global list
+ * before probing, and onto notifier->done after probing
+ * @asd: pointer to respective struct v4l2_async_subdev
+ * @notifier: pointer to managing notifier
+ */
+struct v4l2_async_subdev_list {
+ struct list_head list;
+ struct v4l2_async_subdev *asd;
+ struct v4l2_async_notifier *notifier;
+};
+
+/**
+ * v4l2_async_notifier - v4l2_device notifier data
+ * @num_subdevs:number of subdevices
+ * @subdev: array of pointers to subdevice descriptors
+ * @v4l2_dev: pointer to struct v4l2_device
+ * @waiting: list of struct v4l2_async_subdev, waiting for their drivers
+ * @done: list of struct v4l2_async_subdev_list, already probed
+ * @list: member in a global list of notifiers
+ * @bound: a subdevice driver has successfully probed one of subdevices
+ * @complete: all subdevices have been probed successfully
+ * @unbind: a subdevice is leaving
+ */
+struct v4l2_async_notifier {
+ unsigned int num_subdevs;
+ struct v4l2_async_subdev **subdev;
+ struct v4l2_device *v4l2_dev;
+ struct list_head waiting;
+ struct list_head done;
+ struct list_head list;
+ int (*bound)(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd);
+ int (*complete)(struct v4l2_async_notifier *notifier);
+ void (*unbind)(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd);
+};
+
+int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
+ struct v4l2_async_notifier *notifier);
+void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
+int v4l2_async_register_subdev(struct v4l2_subdev *sd);
+void v4l2_async_unregister_subdev(struct v4l2_subdev *sd);
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
deleted file mode 100644
index c259b36..0000000
--- a/include/media/v4l2-chip-ident.h
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- v4l2 chip identifiers header
-
- This header provides a list of chip identifiers that can be returned
- through the VIDIOC_DBG_G_CHIP_IDENT ioctl.
-
- Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- 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 V4L2_CHIP_IDENT_H_
-#define V4L2_CHIP_IDENT_H_
-
-/* VIDIOC_DBG_G_CHIP_IDENT: identifies the actual chip installed on the board */
-
-/* KEEP THIS LIST ORDERED BY ID!
- Otherwise it will be hard to see which ranges are already in use when
- adding support to a new chip family. */
-enum {
- /* general idents: reserved range 0-49 */
- V4L2_IDENT_NONE = 0, /* No chip matched */
- V4L2_IDENT_AMBIGUOUS = 1, /* Match too general, multiple chips matched */
- V4L2_IDENT_UNKNOWN = 2, /* Chip found, but cannot identify */
-
- /* module tvaudio: reserved range 50-99 */
- V4L2_IDENT_TVAUDIO = 50, /* A tvaudio chip, unknown which it is exactly */
-
- /* Sony IMX074 */
- V4L2_IDENT_IMX074 = 74,
-
- /* module saa7110: just ident 100 */
- V4L2_IDENT_SAA7110 = 100,
-
- /* module saa7115: reserved range 101-149 */
- V4L2_IDENT_SAA7111 = 101,
- V4L2_IDENT_SAA7111A = 102,
- V4L2_IDENT_SAA7113 = 103,
- V4L2_IDENT_SAA7114 = 104,
- V4L2_IDENT_SAA7115 = 105,
- V4L2_IDENT_SAA7118 = 108,
-
- /* module saa7127: reserved range 150-199 */
- V4L2_IDENT_SAA7127 = 157,
- V4L2_IDENT_SAA7129 = 159,
-
- /* module cx25840: reserved range 200-249 */
- V4L2_IDENT_CX25836 = 236,
- V4L2_IDENT_CX25837 = 237,
- V4L2_IDENT_CX25840 = 240,
- V4L2_IDENT_CX25841 = 241,
- V4L2_IDENT_CX25842 = 242,
- V4L2_IDENT_CX25843 = 243,
-
- /* OmniVision sensors: reserved range 250-299 */
- V4L2_IDENT_OV7670 = 250,
- V4L2_IDENT_OV7720 = 251,
- V4L2_IDENT_OV7725 = 252,
- V4L2_IDENT_OV7660 = 253,
- V4L2_IDENT_OV9650 = 254,
- V4L2_IDENT_OV9655 = 255,
- V4L2_IDENT_SOI968 = 256,
- V4L2_IDENT_OV9640 = 257,
- V4L2_IDENT_OV6650 = 258,
- V4L2_IDENT_OV2640 = 259,
- V4L2_IDENT_OV9740 = 260,
- V4L2_IDENT_OV5642 = 261,
-
- /* module saa7146: reserved range 300-309 */
- V4L2_IDENT_SAA7146 = 300,
-
- /* Conexant MPEG encoder/decoders: reserved range 400-420 */
- V4L2_IDENT_CX23418_843 = 403, /* Integrated A/V Decoder on the '418 */
- V4L2_IDENT_CX23415 = 415,
- V4L2_IDENT_CX23416 = 416,
- V4L2_IDENT_CX23417 = 417,
- V4L2_IDENT_CX23418 = 418,
-
- /* module bt819: reserved range 810-819 */
- V4L2_IDENT_BT815A = 815,
- V4L2_IDENT_BT817A = 817,
- V4L2_IDENT_BT819A = 819,
-
- /* module au0828 */
- V4L2_IDENT_AU0828 = 828,
-
- /* module bttv: ident 848 + 849 */
- V4L2_IDENT_BT848 = 848,
- V4L2_IDENT_BT849 = 849,
-
- /* module bt856: just ident 856 */
- V4L2_IDENT_BT856 = 856,
-
- /* module bt866: just ident 866 */
- V4L2_IDENT_BT866 = 866,
-
- /* module bttv: ident 878 + 879 */
- V4L2_IDENT_BT878 = 878,
- V4L2_IDENT_BT879 = 879,
-
- /* module ks0127: reserved range 1120-1129 */
- V4L2_IDENT_KS0122S = 1122,
- V4L2_IDENT_KS0127 = 1127,
- V4L2_IDENT_KS0127B = 1128,
-
- /* module indycam: just ident 2000 */
- V4L2_IDENT_INDYCAM = 2000,
-
- /* module vp27smpx: just ident 2700 */
- V4L2_IDENT_VP27SMPX = 2700,
-
- /* module vpx3220: reserved range: 3210-3229 */
- V4L2_IDENT_VPX3214C = 3214,
- V4L2_IDENT_VPX3216B = 3216,
- V4L2_IDENT_VPX3220A = 3220,
-
- /* VX855 just ident 3409 */
- /* Other via devs could use 3314, 3324, 3327, 3336, 3364, 3353 */
- V4L2_IDENT_VIA_VX855 = 3409,
-
- /* module tvp5150 */
- V4L2_IDENT_TVP5150 = 5150,
-
- /* module saa5246a: just ident 5246 */
- V4L2_IDENT_SAA5246A = 5246,
-
- /* module saa5249: just ident 5249 */
- V4L2_IDENT_SAA5249 = 5249,
-
- /* module cs5345: just ident 5345 */
- V4L2_IDENT_CS5345 = 5345,
-
- /* module tea6415c: just ident 6415 */
- V4L2_IDENT_TEA6415C = 6415,
-
- /* module tea6420: just ident 6420 */
- V4L2_IDENT_TEA6420 = 6420,
-
- /* module saa6588: just ident 6588 */
- V4L2_IDENT_SAA6588 = 6588,
-
- /* module vs6624: just ident 6624 */
- V4L2_IDENT_VS6624 = 6624,
-
- /* module saa6752hs: reserved range 6750-6759 */
- V4L2_IDENT_SAA6752HS = 6752,
- V4L2_IDENT_SAA6752HS_AC3 = 6753,
-
- /* modules tef6862: just ident 6862 */
- V4L2_IDENT_TEF6862 = 6862,
-
- /* module tvp7002: just ident 7002 */
- V4L2_IDENT_TVP7002 = 7002,
-
- /* module adv7170: just ident 7170 */
- V4L2_IDENT_ADV7170 = 7170,
-
- /* module adv7175: just ident 7175 */
- V4L2_IDENT_ADV7175 = 7175,
-
- /* module adv7180: just ident 7180 */
- V4L2_IDENT_ADV7180 = 7180,
-
- /* module adv7183: just ident 7183 */
- V4L2_IDENT_ADV7183 = 7183,
-
- /* module saa7185: just ident 7185 */
- V4L2_IDENT_SAA7185 = 7185,
-
- /* module saa7191: just ident 7191 */
- V4L2_IDENT_SAA7191 = 7191,
-
- /* module ths7303: just ident 7303 */
- V4L2_IDENT_THS7303 = 7303,
-
- /* module adv7343: just ident 7343 */
- V4L2_IDENT_ADV7343 = 7343,
-
- /* module ths7353: just ident 7353 */
- V4L2_IDENT_THS7353 = 7353,
-
- /* module adv7393: just ident 7393 */
- V4L2_IDENT_ADV7393 = 7393,
-
- /* module adv7604: just ident 7604 */
- V4L2_IDENT_ADV7604 = 7604,
-
- /* module saa7706h: just ident 7706 */
- V4L2_IDENT_SAA7706H = 7706,
-
- /* module mt9v011, just ident 8243 */
- V4L2_IDENT_MT9V011 = 8243,
-
- /* module wm8739: just ident 8739 */
- V4L2_IDENT_WM8739 = 8739,
-
- /* module wm8775: just ident 8775 */
- V4L2_IDENT_WM8775 = 8775,
-
- /* Marvell controllers starting at 8801 */
- V4L2_IDENT_CAFE = 8801,
- V4L2_IDENT_ARMADA610 = 8802,
-
- /* AKM AK8813/AK8814 */
- V4L2_IDENT_AK8813 = 8813,
- V4L2_IDENT_AK8814 = 8814,
-
- /* module cx23885 and cx25840 */
- V4L2_IDENT_CX23885 = 8850,
- V4L2_IDENT_CX23885_AV = 8851, /* Integrated A/V decoder */
- V4L2_IDENT_CX23887 = 8870,
- V4L2_IDENT_CX23887_AV = 8871, /* Integrated A/V decoder */
- V4L2_IDENT_CX23888 = 8880,
- V4L2_IDENT_CX23888_AV = 8881, /* Integrated A/V decoder */
- V4L2_IDENT_CX23888_IR = 8882, /* Integrated infrared controller */
-
- /* module ad9389b: just ident 9389 */
- V4L2_IDENT_AD9389B = 9389,
-
- /* module tda9840: just ident 9840 */
- V4L2_IDENT_TDA9840 = 9840,
-
- /* module tw9910: just ident 9910 */
- V4L2_IDENT_TW9910 = 9910,
-
- /* module sn9c20x: just ident 10000 */
- V4L2_IDENT_SN9C20X = 10000,
-
- /* module cx231xx and cx25840 */
- V4L2_IDENT_CX2310X_AV = 23099, /* Integrated A/V decoder; not in '100 */
- V4L2_IDENT_CX23100 = 23100,
- V4L2_IDENT_CX23101 = 23101,
- V4L2_IDENT_CX23102 = 23102,
-
- /* module msp3400: reserved range 34000-34999 for msp34xx */
- V4L2_IDENT_MSPX4XX = 34000, /* generic MSPX4XX identifier, only
- use internally (tveeprom.c). */
-
- V4L2_IDENT_MSP3400B = 34002,
- V4L2_IDENT_MSP3400C = 34003,
- V4L2_IDENT_MSP3400D = 34004,
- V4L2_IDENT_MSP3400G = 34007,
- V4L2_IDENT_MSP3401G = 34017,
- V4L2_IDENT_MSP3402G = 34027,
- V4L2_IDENT_MSP3405D = 34054,
- V4L2_IDENT_MSP3405G = 34057,
- V4L2_IDENT_MSP3407D = 34074,
- V4L2_IDENT_MSP3407G = 34077,
-
- V4L2_IDENT_MSP3410B = 34102,
- V4L2_IDENT_MSP3410C = 34103,
- V4L2_IDENT_MSP3410D = 34104,
- V4L2_IDENT_MSP3410G = 34107,
- V4L2_IDENT_MSP3411G = 34117,
- V4L2_IDENT_MSP3412G = 34127,
- V4L2_IDENT_MSP3415D = 34154,
- V4L2_IDENT_MSP3415G = 34157,
- V4L2_IDENT_MSP3417D = 34174,
- V4L2_IDENT_MSP3417G = 34177,
-
- V4L2_IDENT_MSP3420G = 34207,
- V4L2_IDENT_MSP3421G = 34217,
- V4L2_IDENT_MSP3422G = 34227,
- V4L2_IDENT_MSP3425G = 34257,
- V4L2_IDENT_MSP3427G = 34277,
-
- V4L2_IDENT_MSP3430G = 34307,
- V4L2_IDENT_MSP3431G = 34317,
- V4L2_IDENT_MSP3435G = 34357,
- V4L2_IDENT_MSP3437G = 34377,
-
- V4L2_IDENT_MSP3440G = 34407,
- V4L2_IDENT_MSP3441G = 34417,
- V4L2_IDENT_MSP3442G = 34427,
- V4L2_IDENT_MSP3445G = 34457,
- V4L2_IDENT_MSP3447G = 34477,
-
- V4L2_IDENT_MSP3450G = 34507,
- V4L2_IDENT_MSP3451G = 34517,
- V4L2_IDENT_MSP3452G = 34527,
- V4L2_IDENT_MSP3455G = 34557,
- V4L2_IDENT_MSP3457G = 34577,
-
- V4L2_IDENT_MSP3460G = 34607,
- V4L2_IDENT_MSP3461G = 34617,
- V4L2_IDENT_MSP3465G = 34657,
- V4L2_IDENT_MSP3467G = 34677,
-
- /* module msp3400: reserved range 44000-44999 for msp44xx */
- V4L2_IDENT_MSP4400G = 44007,
- V4L2_IDENT_MSP4408G = 44087,
- V4L2_IDENT_MSP4410G = 44107,
- V4L2_IDENT_MSP4418G = 44187,
- V4L2_IDENT_MSP4420G = 44207,
- V4L2_IDENT_MSP4428G = 44287,
- V4L2_IDENT_MSP4440G = 44407,
- V4L2_IDENT_MSP4448G = 44487,
- V4L2_IDENT_MSP4450G = 44507,
- V4L2_IDENT_MSP4458G = 44587,
-
- /* Micron CMOS sensor chips: 45000-45099 */
- V4L2_IDENT_MT9M001C12ST = 45000,
- V4L2_IDENT_MT9M001C12STM = 45005,
- V4L2_IDENT_MT9M111 = 45007,
- V4L2_IDENT_MT9M112 = 45008,
- V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */
- V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */
- V4L2_IDENT_MT9T031 = 45020,
- V4L2_IDENT_MT9T111 = 45021,
- V4L2_IDENT_MT9T112 = 45022,
- V4L2_IDENT_MT9V111 = 45031,
- V4L2_IDENT_MT9V112 = 45032,
-
- /* HV7131R CMOS sensor: just ident 46000 */
- V4L2_IDENT_HV7131R = 46000,
-
- /* Sharp RJ54N1CB0C, 0xCB0C = 51980 */
- V4L2_IDENT_RJ54N1CB0C = 51980,
-
- /* module m52790: just ident 52790 */
- V4L2_IDENT_M52790 = 52790,
-
- /* module cs53132a: just ident 53132 */
- V4L2_IDENT_CS53l32A = 53132,
-
- /* modules upd61151 MPEG2 encoder: just ident 54000 */
- V4L2_IDENT_UPD61161 = 54000,
- /* modules upd61152 MPEG2 encoder with AC3: just ident 54001 */
- V4L2_IDENT_UPD61162 = 54001,
-
- /* module upd64031a: just ident 64031 */
- V4L2_IDENT_UPD64031A = 64031,
-
- /* module upd64083: just ident 64083 */
- V4L2_IDENT_UPD64083 = 64083,
-
- /* Don't just add new IDs at the end: KEEP THIS LIST ORDERED BY ID! */
-};
-
-#endif
diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h
new file mode 100644
index 0000000..0503a90
--- /dev/null
+++ b/include/media/v4l2-clk.h
@@ -0,0 +1,54 @@
+/*
+ * V4L2 clock service
+ *
+ * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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.
+ *
+ * ATTENTION: This is a temporary API and it shall be replaced by the generic
+ * clock API, when the latter becomes widely available.
+ */
+
+#ifndef MEDIA_V4L2_CLK_H
+#define MEDIA_V4L2_CLK_H
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct module;
+struct device;
+
+struct v4l2_clk {
+ struct list_head list;
+ const struct v4l2_clk_ops *ops;
+ const char *dev_id;
+ const char *id;
+ int enable;
+ struct mutex lock; /* Protect the enable count */
+ atomic_t use_count;
+ void *priv;
+};
+
+struct v4l2_clk_ops {
+ struct module *owner;
+ int (*enable)(struct v4l2_clk *clk);
+ void (*disable)(struct v4l2_clk *clk);
+ unsigned long (*get_rate)(struct v4l2_clk *clk);
+ int (*set_rate)(struct v4l2_clk *clk, unsigned long);
+};
+
+struct v4l2_clk *v4l2_clk_register(const struct v4l2_clk_ops *ops,
+ const char *dev_name,
+ const char *name, void *priv);
+void v4l2_clk_unregister(struct v4l2_clk *clk);
+struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id);
+void v4l2_clk_put(struct v4l2_clk *clk);
+int v4l2_clk_enable(struct v4l2_clk *clk);
+void v4l2_clk_disable(struct v4l2_clk *clk);
+unsigned long v4l2_clk_get_rate(struct v4l2_clk *clk);
+int v4l2_clk_set_rate(struct v4l2_clk *clk, unsigned long rate);
+
+#endif
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 1d93c48..015ff82 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -100,16 +100,6 @@
/* ------------------------------------------------------------------------- */
-/* Register/chip ident helper function */
-
-struct i2c_client; /* forward reference */
-int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match);
-int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
- u32 ident, u32 revision);
-int v4l2_chip_match_host(const struct v4l2_dbg_match *match);
-
-/* ------------------------------------------------------------------------- */
-
/* I2C Helper functions */
struct i2c_driver;
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 95d1c91..c768c9f 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -96,9 +96,9 @@
struct device dev; /* v4l device */
struct cdev *cdev; /* character device */
- /* Set either parent or v4l2_dev if your driver uses v4l2_device */
- struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */
+ /* Only set parent if that can't be deduced from v4l2_dev */
+ struct device *dev_parent; /* device parent */
/* Control handler associated with this device node. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler;
@@ -129,7 +129,6 @@
/* Video standard vars */
v4l2_std_id tvnorms; /* Supported tv norms */
- v4l2_std_id current_norm; /* Current tvnorm */
/* callbacks */
void (*release)(struct video_device *vdev);
diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h
index e6aa231..0286c95 100644
--- a/include/media/v4l2-int-device.h
+++ b/include/media/v4l2-int-device.h
@@ -220,8 +220,6 @@
vidioc_int_reset_num,
/* VIDIOC_INT_INIT */
vidioc_int_init_num,
- /* VIDIOC_DBG_G_CHIP_IDENT */
- vidioc_int_g_chip_ident_num,
/*
*
@@ -303,6 +301,5 @@
V4L2_INT_WRAPPER_0(reset);
V4L2_INT_WRAPPER_0(init);
-V4L2_INT_WRAPPER_1(g_chip_ident, int, *);
#endif
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index 931652f..e0b74a4 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -247,8 +247,6 @@
int (*vidioc_g_chip_info) (struct file *file, void *fh,
struct v4l2_dbg_chip_info *chip);
#endif
- int (*vidioc_g_chip_ident) (struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip);
int (*vidioc_enum_framesizes) (struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 5298d67..3250cc5 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -24,6 +24,7 @@
#include <linux/types.h>
#include <linux/v4l2-subdev.h>
#include <media/media-entity.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
@@ -88,7 +89,6 @@
/* Core ops: it is highly recommended to implement at least these ops:
- g_chip_ident
log_status
g_register
s_register
@@ -145,7 +145,6 @@
performed later. It must not sleep. *Called from an IRQ context*.
*/
struct v4l2_subdev_core_ops {
- int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
struct v4l2_subdev_io_pin_config *pincfg);
@@ -585,8 +584,17 @@
void *host_priv;
/* subdev device node */
struct video_device *devnode;
+ /* pointer to the physical device, if any */
+ struct device *dev;
+ struct v4l2_async_subdev_list asdl;
};
+static inline struct v4l2_subdev *v4l2_async_to_subdev(
+ struct v4l2_async_subdev_list *asdl)
+{
+ return container_of(asdl, struct v4l2_subdev, asdl);
+}
+
#define media_entity_to_v4l2_subdev(ent) \
container_of(ent, struct v4l2_subdev, entity)
#define vdev_to_v4l2_subdev(vdev) \
@@ -660,7 +668,7 @@
/* Call an ops of a v4l2_subdev, doing the right checks against
NULL pointers.
- Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
+ Example: err = v4l2_subdev_call(sd, core, s_std, norm);
*/
#define v4l2_subdev_call(sd, o, f, args...) \
(!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \
diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h
index adcbb20..d9fa68f 100644
--- a/include/net/9p/transport.h
+++ b/include/net/9p/transport.h
@@ -26,6 +26,9 @@
#ifndef NET_9P_TRANSPORT_H
#define NET_9P_TRANSPORT_H
+#define P9_DEF_MIN_RESVPORT (665U)
+#define P9_DEF_MAX_RESVPORT (1023U)
+
/**
* struct p9_trans_module - transport module interface
* @list: used to maintain a list of currently available transports
@@ -37,6 +40,8 @@
* @close: member function to discard a connection on this transport
* @request: member function to issue a request to the transport
* @cancel: member function to cancel a request (if it hasn't been sent)
+ * @cancelled: member function to notify that a cancelled request will not
+ * not receive a reply
*
* This is the basic API for a transport module which is registered by the
* transport module with the 9P core network module and used by the client
@@ -55,6 +60,7 @@
void (*close) (struct p9_client *);
int (*request) (struct p9_client *, struct p9_req_t *req);
int (*cancel) (struct p9_client *, struct p9_req_t *req);
+ int (*cancelled)(struct p9_client *, struct p9_req_t *req);
int (*zc_request)(struct p9_client *, struct p9_req_t *,
char *, char *, int , int, int, int);
};
diff --git a/include/rdma/ib.h b/include/rdma/ib.h
new file mode 100644
index 0000000..cf8f9e7
--- /dev/null
+++ b/include/rdma/ib.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010 Intel Corporation. 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.
+ */
+
+#if !defined(_RDMA_IB_H)
+#define _RDMA_IB_H
+
+#include <linux/types.h>
+
+struct ib_addr {
+ union {
+ __u8 uib_addr8[16];
+ __be16 uib_addr16[8];
+ __be32 uib_addr32[4];
+ __be64 uib_addr64[2];
+ } ib_u;
+#define sib_addr8 ib_u.uib_addr8
+#define sib_addr16 ib_u.uib_addr16
+#define sib_addr32 ib_u.uib_addr32
+#define sib_addr64 ib_u.uib_addr64
+#define sib_raw ib_u.uib_addr8
+#define sib_subnet_prefix ib_u.uib_addr64[0]
+#define sib_interface_id ib_u.uib_addr64[1]
+};
+
+static inline int ib_addr_any(const struct ib_addr *a)
+{
+ return ((a->sib_addr64[0] | a->sib_addr64[1]) == 0);
+}
+
+static inline int ib_addr_loopback(const struct ib_addr *a)
+{
+ return ((a->sib_addr32[0] | a->sib_addr32[1] |
+ a->sib_addr32[2] | (a->sib_addr32[3] ^ htonl(1))) == 0);
+}
+
+static inline void ib_addr_set(struct ib_addr *addr,
+ __be32 w1, __be32 w2, __be32 w3, __be32 w4)
+{
+ addr->sib_addr32[0] = w1;
+ addr->sib_addr32[1] = w2;
+ addr->sib_addr32[2] = w3;
+ addr->sib_addr32[3] = w4;
+}
+
+static inline int ib_addr_cmp(const struct ib_addr *a1, const struct ib_addr *a2)
+{
+ return memcmp(a1, a2, sizeof(struct ib_addr));
+}
+
+struct sockaddr_ib {
+ unsigned short int sib_family; /* AF_IB */
+ __be16 sib_pkey;
+ __be32 sib_flowinfo;
+ struct ib_addr sib_addr;
+ __be64 sib_sid;
+ __be64 sib_sid_mask;
+ __u64 sib_scope_id;
+};
+
+#endif /* _RDMA_IB_H */
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index 9996539..f3ac0f2 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -102,11 +102,7 @@
int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
const unsigned char *dst_dev_addr);
-static inline int ip_addr_size(struct sockaddr *addr)
-{
- return addr->sa_family == AF_INET6 ?
- sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
-}
+int rdma_addr_size(struct sockaddr *addr);
static inline u16 ib_addr_get_pkey(struct rdma_dev_addr *dev_addr)
{
diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h
index 8275e53..125f871 100644
--- a/include/rdma/ib_sa.h
+++ b/include/rdma/ib_sa.h
@@ -402,6 +402,12 @@
struct ib_ah_attr *ah_attr);
/**
+ * ib_sa_pack_path - Conert a path record from struct ib_sa_path_rec
+ * to IB MAD wire format.
+ */
+void ib_sa_pack_path(struct ib_sa_path_rec *rec, void *attribute);
+
+/**
* ib_sa_unpack_path - Convert a path record from MAD format to struct
* ib_sa_path_rec.
*/
@@ -418,4 +424,5 @@
void *context),
void *context,
struct ib_sa_query **sa_query);
+
#endif /* IB_SA_H */
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 98cc4b2..645c3ce 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -610,7 +610,21 @@
IB_QPT_RAW_PACKET = 8,
IB_QPT_XRC_INI = 9,
IB_QPT_XRC_TGT,
- IB_QPT_MAX
+ IB_QPT_MAX,
+ /* Reserve a range for qp types internal to the low level driver.
+ * These qp types will not be visible at the IB core layer, so the
+ * IB_QPT_MAX usages should not be affected in the core layer
+ */
+ IB_QPT_RESERVED1 = 0x1000,
+ IB_QPT_RESERVED2,
+ IB_QPT_RESERVED3,
+ IB_QPT_RESERVED4,
+ IB_QPT_RESERVED5,
+ IB_QPT_RESERVED6,
+ IB_QPT_RESERVED7,
+ IB_QPT_RESERVED8,
+ IB_QPT_RESERVED9,
+ IB_QPT_RESERVED10,
};
enum ib_qp_create_flags {
@@ -766,6 +780,19 @@
IB_WR_MASKED_ATOMIC_CMP_AND_SWP,
IB_WR_MASKED_ATOMIC_FETCH_AND_ADD,
IB_WR_BIND_MW,
+ /* reserve values for low level drivers' internal use.
+ * These values will not be used at all in the ib core layer.
+ */
+ IB_WR_RESERVED1 = 0xf0,
+ IB_WR_RESERVED2,
+ IB_WR_RESERVED3,
+ IB_WR_RESERVED4,
+ IB_WR_RESERVED5,
+ IB_WR_RESERVED6,
+ IB_WR_RESERVED7,
+ IB_WR_RESERVED8,
+ IB_WR_RESERVED9,
+ IB_WR_RESERVED10,
};
enum ib_send_flags {
@@ -773,7 +800,11 @@
IB_SEND_SIGNALED = (1<<1),
IB_SEND_SOLICITED = (1<<2),
IB_SEND_INLINE = (1<<3),
- IB_SEND_IP_CSUM = (1<<4)
+ IB_SEND_IP_CSUM = (1<<4),
+
+ /* reserve bits 26-31 for low level drivers' internal use */
+ IB_SEND_RESERVED_START = (1 << 26),
+ IB_SEND_RESERVED_END = (1 << 31),
};
struct ib_sge {
diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h
index ad3a314..1ed2088 100644
--- a/include/rdma/rdma_cm.h
+++ b/include/rdma/rdma_cm.h
@@ -70,6 +70,11 @@
RDMA_PS_UDP = 0x0111,
};
+#define RDMA_IB_IP_PS_MASK 0xFFFFFFFFFFFF0000ULL
+#define RDMA_IB_IP_PS_TCP 0x0000000001060000ULL
+#define RDMA_IB_IP_PS_UDP 0x0000000001110000ULL
+#define RDMA_IB_IP_PS_IB 0x00000000013F0000ULL
+
struct rdma_addr {
struct sockaddr_storage src_addr;
struct sockaddr_storage dst_addr;
@@ -93,6 +98,7 @@
/* Fields below ignored if a QP is created on the rdma_cm_id. */
u8 srq;
u32 qp_num;
+ u32 qkey;
};
struct rdma_ud_param {
@@ -367,4 +373,11 @@
*/
int rdma_set_afonly(struct rdma_cm_id *id, int afonly);
+ /**
+ * rdma_get_service_id - Return the IB service ID for a specified address.
+ * @id: Communication identifier associated with the address.
+ * @addr: Address for the service ID.
+ */
+__be64 rdma_get_service_id(struct rdma_cm_id *id, struct sockaddr *addr);
+
#endif /* RDMA_CM_H */
diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h
index 23a87d0..e5d09d2 100644
--- a/include/target/iscsi/iscsi_transport.h
+++ b/include/target/iscsi/iscsi_transport.h
@@ -34,8 +34,6 @@
/*
* From iscsi_target.c
*/
-extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *,
- struct iscsi_cmd *);
extern int iscsit_setup_scsi_cmd(struct iscsi_conn *, struct iscsi_cmd *,
unsigned char *);
extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);
@@ -45,18 +43,26 @@
struct iscsi_cmd **);
extern int iscsit_check_dataout_payload(struct iscsi_cmd *, struct iscsi_data *,
bool);
-extern int iscsit_handle_nop_out(struct iscsi_conn *, struct iscsi_cmd *,
- unsigned char *);
+extern int iscsit_setup_nop_out(struct iscsi_conn *, struct iscsi_cmd *,
+ struct iscsi_nopout *);
+extern int iscsit_process_nop_out(struct iscsi_conn *, struct iscsi_cmd *,
+ struct iscsi_nopout *);
extern int iscsit_handle_logout_cmd(struct iscsi_conn *, struct iscsi_cmd *,
unsigned char *);
extern int iscsit_handle_task_mgt_cmd(struct iscsi_conn *, struct iscsi_cmd *,
unsigned char *);
+extern int iscsit_setup_text_cmd(struct iscsi_conn *, struct iscsi_cmd *,
+ struct iscsi_text *);
+extern int iscsit_process_text_cmd(struct iscsi_conn *, struct iscsi_cmd *,
+ struct iscsi_text *);
extern void iscsit_build_rsp_pdu(struct iscsi_cmd *, struct iscsi_conn *,
bool, struct iscsi_scsi_rsp *);
extern void iscsit_build_nopin_rsp(struct iscsi_cmd *, struct iscsi_conn *,
struct iscsi_nopin *, bool);
extern void iscsit_build_task_mgt_rsp(struct iscsi_cmd *, struct iscsi_conn *,
struct iscsi_tm_rsp *);
+extern int iscsit_build_text_rsp(struct iscsi_cmd *, struct iscsi_conn *,
+ struct iscsi_text_rsp *);
extern void iscsit_build_reject(struct iscsi_cmd *, struct iscsi_conn *,
struct iscsi_reject *);
extern int iscsit_build_logout_rsp(struct iscsi_cmd *, struct iscsi_conn *,
@@ -67,6 +73,10 @@
*/
extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *);
/*
+ * From iscsi_target_erl0.c
+ */
+extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int);
+/*
* From iscsi_target_erl1.c
*/
extern void iscsit_stop_dataout_timer(struct iscsi_cmd *);
@@ -80,4 +90,5 @@
* From iscsi_target_util.c
*/
extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t);
-extern int iscsit_sequence_cmd(struct iscsi_conn *, struct iscsi_cmd *, __be32);
+extern int iscsit_sequence_cmd(struct iscsi_conn *, struct iscsi_cmd *,
+ unsigned char *, __be32);
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 4ea4f98..e34fc90 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -218,14 +218,11 @@
/* fabric independent task management response values */
enum tcm_tmrsp_table {
- TMR_FUNCTION_COMPLETE = 0,
- TMR_TASK_DOES_NOT_EXIST = 1,
- TMR_LUN_DOES_NOT_EXIST = 2,
- TMR_TASK_STILL_ALLEGIANT = 3,
- TMR_TASK_FAILOVER_NOT_SUPPORTED = 4,
- TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED = 5,
- TMR_FUNCTION_AUTHORIZATION_FAILED = 6,
- TMR_FUNCTION_REJECTED = 255,
+ TMR_FUNCTION_COMPLETE = 1,
+ TMR_TASK_DOES_NOT_EXIST = 2,
+ TMR_LUN_DOES_NOT_EXIST = 3,
+ TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED = 4,
+ TMR_FUNCTION_REJECTED = 5,
};
/*
@@ -339,8 +336,6 @@
/* Used during APTPL metadata reading */
#define PR_APTPL_MAX_TPORT_LEN 256
unsigned char pr_tport[PR_APTPL_MAX_TPORT_LEN];
- /* For writing out live meta data */
- unsigned char *pr_aptpl_buf;
u16 pr_aptpl_rpti;
u16 pr_reg_tpgt;
/* Reservation effects all target ports */
@@ -374,9 +369,7 @@
/* Activate Persistence across Target Power Loss enabled
* for SCSI device */
int pr_aptpl_active;
- /* Used by struct t10_reservation->pr_aptpl_buf_len */
#define PR_APTPL_BUF_LEN 8192
- u32 pr_aptpl_buf_len;
u32 pr_generation;
spinlock_t registration_lock;
spinlock_t aptpl_reg_lock;
@@ -424,8 +417,6 @@
int sam_task_attr;
/* Transport protocol dependent state, see transport_state_table */
enum transport_state_table t_state;
- /* Used to signal cmd->se_tfo->check_release_cmd() usage per cmd */
- unsigned check_release:1;
unsigned cmd_wait_set:1;
unsigned unknown_data_length:1;
/* See se_cmd_flags_table */
@@ -458,7 +449,6 @@
unsigned char *t_task_cdb;
unsigned char __t_task_cdb[TCM_MAX_COMMAND_SIZE];
unsigned long long t_task_lba;
- atomic_t t_fe_count;
unsigned int transport_state;
#define CMD_T_ABORTED (1 << 0)
#define CMD_T_ACTIVE (1 << 1)
@@ -802,11 +792,12 @@
struct target_core_fabric_ops *se_tpg_tfo;
struct se_wwn *se_tpg_wwn;
struct config_group tpg_group;
- struct config_group *tpg_default_groups[6];
+ struct config_group *tpg_default_groups[7];
struct config_group tpg_lun_group;
struct config_group tpg_np_group;
struct config_group tpg_acl_group;
struct config_group tpg_attrib_group;
+ struct config_group tpg_auth_group;
struct config_group tpg_param_group;
};
diff --git a/include/target/target_core_configfs.h b/include/target/target_core_configfs.h
index 6125095..713c500 100644
--- a/include/target/target_core_configfs.h
+++ b/include/target/target_core_configfs.h
@@ -23,6 +23,7 @@
struct config_item_type tfc_tpg_np_cit;
struct config_item_type tfc_tpg_np_base_cit;
struct config_item_type tfc_tpg_attrib_cit;
+ struct config_item_type tfc_tpg_auth_cit;
struct config_item_type tfc_tpg_param_cit;
struct config_item_type tfc_tpg_nacl_cit;
struct config_item_type tfc_tpg_nacl_base_cit;
diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h
index 1dcce9c..7a16178 100644
--- a/include/target/target_core_fabric.h
+++ b/include/target/target_core_fabric.h
@@ -61,7 +61,7 @@
int (*get_cmd_state)(struct se_cmd *);
int (*queue_data_in)(struct se_cmd *);
int (*queue_status)(struct se_cmd *);
- int (*queue_tm_rsp)(struct se_cmd *);
+ void (*queue_tm_rsp)(struct se_cmd *);
/*
* fabric module calls for target_core_fabric_configfs.c
*/
diff --git a/include/target/target_core_fabric_configfs.h b/include/target/target_core_fabric_configfs.h
index a26fb75..b32a149 100644
--- a/include/target/target_core_fabric_configfs.h
+++ b/include/target/target_core_fabric_configfs.h
@@ -62,6 +62,17 @@
_fabric##_tpg_attrib_show_##_name, \
_fabric##_tpg_attrib_store_##_name);
+CONFIGFS_EATTR_STRUCT(target_fabric_tpg_auth, se_portal_group);
+#define TF_TPG_AUTH_ATTR(_fabric, _name, _mode) \
+static struct target_fabric_tpg_auth_attribute _fabric##_tpg_auth_##_name = \
+ __CONFIGFS_EATTR(_name, _mode, \
+ _fabric##_tpg_auth_show_##_name, \
+ _fabric##_tpg_auth_store_##_name);
+
+#define TF_TPG_AUTH_ATTR_RO(_fabric, _name) \
+static struct target_fabric_tpg_auth_attribute _fabric##_tpg_auth_##_name = \
+ __CONFIGFS_EATTR_RO(_name, \
+ _fabric##_tpg_auth_show_##_name);
CONFIGFS_EATTR_STRUCT(target_fabric_tpg_param, se_portal_group);
#define TF_TPG_PARAM_ATTR(_fabric, _name, _mode) \
diff --git a/include/trace/events/target.h b/include/trace/events/target.h
new file mode 100644
index 0000000..aef8fc3
--- /dev/null
+++ b/include/trace/events/target.h
@@ -0,0 +1,214 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM target
+
+#if !defined(_TRACE_TARGET_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TARGET_H
+
+#include <linux/tracepoint.h>
+#include <linux/trace_seq.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+
+/* cribbed verbatim from <trace/event/scsi.h> */
+#define scsi_opcode_name(opcode) { opcode, #opcode }
+#define show_opcode_name(val) \
+ __print_symbolic(val, \
+ scsi_opcode_name(TEST_UNIT_READY), \
+ scsi_opcode_name(REZERO_UNIT), \
+ scsi_opcode_name(REQUEST_SENSE), \
+ scsi_opcode_name(FORMAT_UNIT), \
+ scsi_opcode_name(READ_BLOCK_LIMITS), \
+ scsi_opcode_name(REASSIGN_BLOCKS), \
+ scsi_opcode_name(INITIALIZE_ELEMENT_STATUS), \
+ scsi_opcode_name(READ_6), \
+ scsi_opcode_name(WRITE_6), \
+ scsi_opcode_name(SEEK_6), \
+ scsi_opcode_name(READ_REVERSE), \
+ scsi_opcode_name(WRITE_FILEMARKS), \
+ scsi_opcode_name(SPACE), \
+ scsi_opcode_name(INQUIRY), \
+ scsi_opcode_name(RECOVER_BUFFERED_DATA), \
+ scsi_opcode_name(MODE_SELECT), \
+ scsi_opcode_name(RESERVE), \
+ scsi_opcode_name(RELEASE), \
+ scsi_opcode_name(COPY), \
+ scsi_opcode_name(ERASE), \
+ scsi_opcode_name(MODE_SENSE), \
+ scsi_opcode_name(START_STOP), \
+ scsi_opcode_name(RECEIVE_DIAGNOSTIC), \
+ scsi_opcode_name(SEND_DIAGNOSTIC), \
+ scsi_opcode_name(ALLOW_MEDIUM_REMOVAL), \
+ scsi_opcode_name(SET_WINDOW), \
+ scsi_opcode_name(READ_CAPACITY), \
+ scsi_opcode_name(READ_10), \
+ scsi_opcode_name(WRITE_10), \
+ scsi_opcode_name(SEEK_10), \
+ scsi_opcode_name(POSITION_TO_ELEMENT), \
+ scsi_opcode_name(WRITE_VERIFY), \
+ scsi_opcode_name(VERIFY), \
+ scsi_opcode_name(SEARCH_HIGH), \
+ scsi_opcode_name(SEARCH_EQUAL), \
+ scsi_opcode_name(SEARCH_LOW), \
+ scsi_opcode_name(SET_LIMITS), \
+ scsi_opcode_name(PRE_FETCH), \
+ scsi_opcode_name(READ_POSITION), \
+ scsi_opcode_name(SYNCHRONIZE_CACHE), \
+ scsi_opcode_name(LOCK_UNLOCK_CACHE), \
+ scsi_opcode_name(READ_DEFECT_DATA), \
+ scsi_opcode_name(MEDIUM_SCAN), \
+ scsi_opcode_name(COMPARE), \
+ scsi_opcode_name(COPY_VERIFY), \
+ scsi_opcode_name(WRITE_BUFFER), \
+ scsi_opcode_name(READ_BUFFER), \
+ scsi_opcode_name(UPDATE_BLOCK), \
+ scsi_opcode_name(READ_LONG), \
+ scsi_opcode_name(WRITE_LONG), \
+ scsi_opcode_name(CHANGE_DEFINITION), \
+ scsi_opcode_name(WRITE_SAME), \
+ scsi_opcode_name(UNMAP), \
+ scsi_opcode_name(READ_TOC), \
+ scsi_opcode_name(LOG_SELECT), \
+ scsi_opcode_name(LOG_SENSE), \
+ scsi_opcode_name(XDWRITEREAD_10), \
+ scsi_opcode_name(MODE_SELECT_10), \
+ scsi_opcode_name(RESERVE_10), \
+ scsi_opcode_name(RELEASE_10), \
+ scsi_opcode_name(MODE_SENSE_10), \
+ scsi_opcode_name(PERSISTENT_RESERVE_IN), \
+ scsi_opcode_name(PERSISTENT_RESERVE_OUT), \
+ scsi_opcode_name(VARIABLE_LENGTH_CMD), \
+ scsi_opcode_name(REPORT_LUNS), \
+ scsi_opcode_name(MAINTENANCE_IN), \
+ scsi_opcode_name(MAINTENANCE_OUT), \
+ scsi_opcode_name(MOVE_MEDIUM), \
+ scsi_opcode_name(EXCHANGE_MEDIUM), \
+ scsi_opcode_name(READ_12), \
+ scsi_opcode_name(WRITE_12), \
+ scsi_opcode_name(WRITE_VERIFY_12), \
+ scsi_opcode_name(SEARCH_HIGH_12), \
+ scsi_opcode_name(SEARCH_EQUAL_12), \
+ scsi_opcode_name(SEARCH_LOW_12), \
+ scsi_opcode_name(READ_ELEMENT_STATUS), \
+ scsi_opcode_name(SEND_VOLUME_TAG), \
+ scsi_opcode_name(WRITE_LONG_2), \
+ scsi_opcode_name(READ_16), \
+ scsi_opcode_name(WRITE_16), \
+ scsi_opcode_name(VERIFY_16), \
+ scsi_opcode_name(WRITE_SAME_16), \
+ scsi_opcode_name(SERVICE_ACTION_IN), \
+ scsi_opcode_name(SAI_READ_CAPACITY_16), \
+ scsi_opcode_name(SAI_GET_LBA_STATUS), \
+ scsi_opcode_name(MI_REPORT_TARGET_PGS), \
+ scsi_opcode_name(MO_SET_TARGET_PGS), \
+ scsi_opcode_name(READ_32), \
+ scsi_opcode_name(WRITE_32), \
+ scsi_opcode_name(WRITE_SAME_32), \
+ scsi_opcode_name(ATA_16), \
+ scsi_opcode_name(ATA_12))
+
+#define show_task_attribute_name(val) \
+ __print_symbolic(val, \
+ { MSG_SIMPLE_TAG, "SIMPLE" }, \
+ { MSG_HEAD_TAG, "HEAD" }, \
+ { MSG_ORDERED_TAG, "ORDERED" }, \
+ { MSG_ACA_TAG, "ACA" } )
+
+#define show_scsi_status_name(val) \
+ __print_symbolic(val, \
+ { SAM_STAT_GOOD, "GOOD" }, \
+ { SAM_STAT_CHECK_CONDITION, "CHECK CONDITION" }, \
+ { SAM_STAT_CONDITION_MET, "CONDITION MET" }, \
+ { SAM_STAT_BUSY, "BUSY" }, \
+ { SAM_STAT_INTERMEDIATE, "INTERMEDIATE" }, \
+ { SAM_STAT_INTERMEDIATE_CONDITION_MET, "INTERMEDIATE CONDITION MET" }, \
+ { SAM_STAT_RESERVATION_CONFLICT, "RESERVATION CONFLICT" }, \
+ { SAM_STAT_COMMAND_TERMINATED, "COMMAND TERMINATED" }, \
+ { SAM_STAT_TASK_SET_FULL, "TASK SET FULL" }, \
+ { SAM_STAT_ACA_ACTIVE, "ACA ACTIVE" }, \
+ { SAM_STAT_TASK_ABORTED, "TASK ABORTED" } )
+
+TRACE_EVENT(target_sequencer_start,
+
+ TP_PROTO(struct se_cmd *cmd),
+
+ TP_ARGS(cmd),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, unpacked_lun )
+ __field( unsigned int, opcode )
+ __field( unsigned int, data_length )
+ __field( unsigned int, task_attribute )
+ __array( unsigned char, cdb, TCM_MAX_COMMAND_SIZE )
+ __string( initiator, cmd->se_sess->se_node_acl->initiatorname )
+ ),
+
+ TP_fast_assign(
+ __entry->unpacked_lun = cmd->se_lun->unpacked_lun;
+ __entry->opcode = cmd->t_task_cdb[0];
+ __entry->data_length = cmd->data_length;
+ __entry->task_attribute = cmd->sam_task_attr;
+ memcpy(__entry->cdb, cmd->t_task_cdb, TCM_MAX_COMMAND_SIZE);
+ __assign_str(initiator, cmd->se_sess->se_node_acl->initiatorname);
+ ),
+
+ TP_printk("%s -> LUN %03u %s data_length %6u CDB %s (TA:%s C:%02x)",
+ __get_str(initiator), __entry->unpacked_lun,
+ show_opcode_name(__entry->opcode),
+ __entry->data_length, __print_hex(__entry->cdb, 16),
+ show_task_attribute_name(__entry->task_attribute),
+ scsi_command_size(__entry->cdb) <= 16 ?
+ __entry->cdb[scsi_command_size(__entry->cdb) - 1] :
+ __entry->cdb[1]
+ )
+);
+
+TRACE_EVENT(target_cmd_complete,
+
+ TP_PROTO(struct se_cmd *cmd),
+
+ TP_ARGS(cmd),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, unpacked_lun )
+ __field( unsigned int, opcode )
+ __field( unsigned int, data_length )
+ __field( unsigned int, task_attribute )
+ __field( unsigned char, scsi_status )
+ __field( unsigned char, sense_length )
+ __array( unsigned char, cdb, TCM_MAX_COMMAND_SIZE )
+ __array( unsigned char, sense_data, 18 )
+ __string(initiator, cmd->se_sess->se_node_acl->initiatorname)
+ ),
+
+ TP_fast_assign(
+ __entry->unpacked_lun = cmd->se_lun->unpacked_lun;
+ __entry->opcode = cmd->t_task_cdb[0];
+ __entry->data_length = cmd->data_length;
+ __entry->task_attribute = cmd->sam_task_attr;
+ __entry->scsi_status = cmd->scsi_status;
+ __entry->sense_length = cmd->scsi_status == SAM_STAT_CHECK_CONDITION ?
+ min(18, ((u8 *) cmd->sense_buffer)[SPC_ADD_SENSE_LEN_OFFSET] + 8) : 0;
+ memcpy(__entry->cdb, cmd->t_task_cdb, TCM_MAX_COMMAND_SIZE);
+ memcpy(__entry->sense_data, cmd->sense_buffer, __entry->sense_length);
+ __assign_str(initiator, cmd->se_sess->se_node_acl->initiatorname);
+ ),
+
+ TP_printk("%s <- LUN %03u status %s (sense len %d%s%s) %s data_length %6u CDB %s (TA:%s C:%02x)",
+ __get_str(initiator), __entry->unpacked_lun,
+ show_scsi_status_name(__entry->scsi_status),
+ __entry->sense_length, __entry->sense_length ? " / " : "",
+ __print_hex(__entry->sense_data, __entry->sense_length),
+ show_opcode_name(__entry->opcode),
+ __entry->data_length, __print_hex(__entry->cdb, 16),
+ show_task_attribute_name(__entry->task_attribute),
+ scsi_command_size(__entry->cdb) <= 16 ?
+ __entry->cdb[scsi_command_size(__entry->cdb) - 1] :
+ __entry->cdb[1]
+ )
+);
+
+#endif /* _TRACE_TARGET_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/syscall.h b/include/trace/syscall.h
index 84bc419..fed853f 100644
--- a/include/trace/syscall.h
+++ b/include/trace/syscall.h
@@ -16,6 +16,7 @@
* @nb_args: number of parameters it takes
* @types: list of types as strings
* @args: list of args as strings (args[i] matches types[i])
+ * @enter_fields: list of fields for syscall_enter trace event
* @enter_event: associated syscall_enter trace event
* @exit_event: associated syscall_exit trace event
*/
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index bdc6e87..997f9f2 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -62,6 +62,7 @@
header-y += ax25.h
header-y += b1lli.h
header-y += baycom.h
+header-y += bcm933xx_hcs.h
header-y += bfs_fs.h
header-y += binfmts.h
header-y += blkpg.h
diff --git a/include/uapi/linux/bcm933xx_hcs.h b/include/uapi/linux/bcm933xx_hcs.h
new file mode 100644
index 0000000..d228218
--- /dev/null
+++ b/include/uapi/linux/bcm933xx_hcs.h
@@ -0,0 +1,24 @@
+/*
+ * Broadcom Cable Modem firmware format
+ */
+
+#ifndef __BCM933XX_HCS_H
+#define __BCM933XX_HCS_H
+
+#include <linux/types.h>
+
+struct bcm_hcs {
+ __u16 magic;
+ __u16 control;
+ __u16 rev_maj;
+ __u16 rev_min;
+ __u32 build_date;
+ __u32 filelen;
+ __u32 ldaddress;
+ char filename[64];
+ __u16 hcs;
+ __u16 her_znaet_chto;
+ __u32 crc;
+};
+
+#endif /* __BCM933XX_HCS */
diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h
index 7e75b6f..afd0cbd 100644
--- a/include/uapi/linux/dm-ioctl.h
+++ b/include/uapi/linux/dm-ioctl.h
@@ -267,9 +267,9 @@
#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
#define DM_VERSION_MAJOR 4
-#define DM_VERSION_MINOR 24
+#define DM_VERSION_MINOR 25
#define DM_VERSION_PATCHLEVEL 0
-#define DM_VERSION_EXTRA "-ioctl (2013-01-15)"
+#define DM_VERSION_EXTRA "-ioctl (2013-06-26)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 69bd5bb..e90a88a 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -53,13 +53,13 @@
#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */
#define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class controls */
-#define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator control class */
+#define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator controls */
#define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */
#define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */
#define V4L2_CTRL_CLASS_IMAGE_SOURCE 0x009e0000 /* Image source controls */
#define V4L2_CTRL_CLASS_IMAGE_PROC 0x009f0000 /* Image processing controls */
#define V4L2_CTRL_CLASS_DV 0x00a00000 /* Digital Video controls */
-#define V4L2_CTRL_CLASS_FM_RX 0x00a10000 /* Digital Video controls */
+#define V4L2_CTRL_CLASS_FM_RX 0x00a10000 /* FM Receiver controls */
/* User-class control IDs */
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 87ee4f4..916e444 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -362,10 +362,14 @@
#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13)
/**
- * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap)
+ * VFIO_IOMMU_UNMAP_DMA - _IOWR(VFIO_TYPE, VFIO_BASE + 14,
+ * struct vfio_dma_unmap)
*
* Unmap IO virtual addresses using the provided struct vfio_dma_unmap.
- * Caller sets argsz.
+ * Caller sets argsz. The actual unmapped size is returned in the size
+ * field. No guarantee is made to the user that arbitrary unmaps of iova
+ * or size different from those used in the original mapping call will
+ * succeed.
*/
struct vfio_iommu_type1_dma_unmap {
__u32 argsz;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index f40b41c..95ef455 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -395,7 +395,7 @@
#define V4L2_PIX_FMT_H263 v4l2_fourcc('H', '2', '6', '3') /* H263 */
#define V4L2_PIX_FMT_MPEG1 v4l2_fourcc('M', 'P', 'G', '1') /* MPEG-1 ES */
#define V4L2_PIX_FMT_MPEG2 v4l2_fourcc('M', 'P', 'G', '2') /* MPEG-2 ES */
-#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG-4 ES */
+#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG-4 part 2 ES */
#define V4L2_PIX_FMT_XVID v4l2_fourcc('X', 'V', 'I', 'D') /* Xvid */
#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
@@ -555,7 +555,7 @@
__u32 jpeg_markers; /* Which markers should go into the JPEG
* output. Unless you exactly know what
* you do, leave them untouched.
- * Inluding less markers will make the
+ * Including less markers will make the
* resulting code smaller, but there will
* be fewer applications which can read it.
* The presence of the APP and COM marker
@@ -567,7 +567,7 @@
#define V4L2_JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
#define V4L2_JPEG_MARKER_COM (1<<6) /* Comment segment */
#define V4L2_JPEG_MARKER_APP (1<<7) /* App segment, driver will
- * allways use APP0 */
+ * always use APP0 */
};
/*
@@ -900,7 +900,7 @@
/*
* "Common" PAL - This macro is there to be compatible with the old
* V4L1 concept of "PAL": /BGDKHI.
- * Several PAL standards are mising here: /M, /N and /Nc
+ * Several PAL standards are missing here: /M, /N and /Nc
*/
#define V4L2_STD_PAL (V4L2_STD_PAL_BG |\
V4L2_STD_PAL_DK |\
@@ -1787,11 +1787,13 @@
/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */
#define V4L2_CHIP_MATCH_BRIDGE 0 /* Match against chip ID on the bridge (0 for the bridge) */
+#define V4L2_CHIP_MATCH_SUBDEV 4 /* Match against subdev index */
+
+/* The following four defines are no longer in use */
#define V4L2_CHIP_MATCH_HOST V4L2_CHIP_MATCH_BRIDGE
#define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver name */
#define V4L2_CHIP_MATCH_I2C_ADDR 2 /* Match against I2C 7-bit address */
-#define V4L2_CHIP_MATCH_AC97 3 /* Match against anciliary AC97 chip */
-#define V4L2_CHIP_MATCH_SUBDEV 4 /* Match against subdev index */
+#define V4L2_CHIP_MATCH_AC97 3 /* Match against ancillary AC97 chip */
struct v4l2_dbg_match {
__u32 type; /* Match type */
@@ -1808,13 +1810,6 @@
__u64 val;
} __attribute__ ((packed));
-/* VIDIOC_DBG_G_CHIP_IDENT */
-struct v4l2_dbg_chip_ident {
- struct v4l2_dbg_match match;
- __u32 ident; /* chip identifier as specified in <media/v4l2-chip-ident.h> */
- __u32 revision; /* chip revision, chip specific */
-} __attribute__ ((packed));
-
#define V4L2_CHIP_FL_READABLE (1 << 0)
#define V4L2_CHIP_FL_WRITABLE (1 << 1)
@@ -1915,12 +1910,6 @@
#define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_dbg_register)
#define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_dbg_register)
-/* Experimental, meant for debugging, testing and internal use.
- Never use this ioctl in applications!
- Note: this ioctl is deprecated in favor of VIDIOC_DBG_G_CHIP_INFO and
- will go away in the future. */
-#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident)
-
#define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek)
#define VIDIOC_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings)
diff --git a/include/uapi/linux/virtio_config.h b/include/uapi/linux/virtio_config.h
index b7cda39..3ce768c 100644
--- a/include/uapi/linux/virtio_config.h
+++ b/include/uapi/linux/virtio_config.h
@@ -51,4 +51,7 @@
* suppressed them? */
#define VIRTIO_F_NOTIFY_ON_EMPTY 24
+/* Can the device handle any descriptor layout? */
+#define VIRTIO_F_ANY_LAYOUT 27
+
#endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */
diff --git a/include/uapi/rdma/rdma_user_cm.h b/include/uapi/rdma/rdma_user_cm.h
index 1ee9239..99b80ab 100644
--- a/include/uapi/rdma/rdma_user_cm.h
+++ b/include/uapi/rdma/rdma_user_cm.h
@@ -45,8 +45,8 @@
enum {
RDMA_USER_CM_CMD_CREATE_ID,
RDMA_USER_CM_CMD_DESTROY_ID,
- RDMA_USER_CM_CMD_BIND_ADDR,
- RDMA_USER_CM_CMD_RESOLVE_ADDR,
+ RDMA_USER_CM_CMD_BIND_IP,
+ RDMA_USER_CM_CMD_RESOLVE_IP,
RDMA_USER_CM_CMD_RESOLVE_ROUTE,
RDMA_USER_CM_CMD_QUERY_ROUTE,
RDMA_USER_CM_CMD_CONNECT,
@@ -59,9 +59,13 @@
RDMA_USER_CM_CMD_GET_OPTION,
RDMA_USER_CM_CMD_SET_OPTION,
RDMA_USER_CM_CMD_NOTIFY,
- RDMA_USER_CM_CMD_JOIN_MCAST,
+ RDMA_USER_CM_CMD_JOIN_IP_MCAST,
RDMA_USER_CM_CMD_LEAVE_MCAST,
- RDMA_USER_CM_CMD_MIGRATE_ID
+ RDMA_USER_CM_CMD_MIGRATE_ID,
+ RDMA_USER_CM_CMD_QUERY,
+ RDMA_USER_CM_CMD_BIND,
+ RDMA_USER_CM_CMD_RESOLVE_ADDR,
+ RDMA_USER_CM_CMD_JOIN_MCAST
};
/*
@@ -95,28 +99,51 @@
__u32 events_reported;
};
-struct rdma_ucm_bind_addr {
+struct rdma_ucm_bind_ip {
__u64 response;
struct sockaddr_in6 addr;
__u32 id;
};
-struct rdma_ucm_resolve_addr {
+struct rdma_ucm_bind {
+ __u32 id;
+ __u16 addr_size;
+ __u16 reserved;
+ struct sockaddr_storage addr;
+};
+
+struct rdma_ucm_resolve_ip {
struct sockaddr_in6 src_addr;
struct sockaddr_in6 dst_addr;
__u32 id;
__u32 timeout_ms;
};
+struct rdma_ucm_resolve_addr {
+ __u32 id;
+ __u32 timeout_ms;
+ __u16 src_size;
+ __u16 dst_size;
+ __u32 reserved;
+ struct sockaddr_storage src_addr;
+ struct sockaddr_storage dst_addr;
+};
+
struct rdma_ucm_resolve_route {
__u32 id;
__u32 timeout_ms;
};
-struct rdma_ucm_query_route {
+enum {
+ RDMA_USER_CM_QUERY_ADDR,
+ RDMA_USER_CM_QUERY_PATH,
+ RDMA_USER_CM_QUERY_GID
+};
+
+struct rdma_ucm_query {
__u64 response;
__u32 id;
- __u32 reserved;
+ __u32 option;
};
struct rdma_ucm_query_route_resp {
@@ -129,9 +156,26 @@
__u8 reserved[3];
};
+struct rdma_ucm_query_addr_resp {
+ __u64 node_guid;
+ __u8 port_num;
+ __u8 reserved;
+ __u16 pkey;
+ __u16 src_size;
+ __u16 dst_size;
+ struct sockaddr_storage src_addr;
+ struct sockaddr_storage dst_addr;
+};
+
+struct rdma_ucm_query_path_resp {
+ __u32 num_paths;
+ __u32 reserved;
+ struct ib_path_rec_data path_data[0];
+};
+
struct rdma_ucm_conn_param {
__u32 qp_num;
- __u32 reserved;
+ __u32 qkey;
__u8 private_data[RDMA_MAX_PRIVATE_DATA];
__u8 private_data_len;
__u8 srq;
@@ -192,13 +236,22 @@
__u32 event;
};
-struct rdma_ucm_join_mcast {
+struct rdma_ucm_join_ip_mcast {
__u64 response; /* rdma_ucm_create_id_resp */
__u64 uid;
struct sockaddr_in6 addr;
__u32 id;
};
+struct rdma_ucm_join_mcast {
+ __u64 response; /* rdma_ucma_create_id_resp */
+ __u64 uid;
+ __u32 id;
+ __u16 addr_size;
+ __u16 reserved;
+ struct sockaddr_storage addr;
+};
+
struct rdma_ucm_get_event {
__u64 response;
};
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 1db3af9..eba8fb5 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -182,7 +182,7 @@
u64 tmp = perf_sample_period_ns;
tmp *= sysctl_perf_cpu_time_max_percent;
- tmp = do_div(tmp, 100);
+ do_div(tmp, 100);
atomic_set(&perf_sample_allowed_ns, tmp);
}
@@ -232,7 +232,7 @@
void perf_sample_event_took(u64 sample_len_ns)
{
u64 avg_local_sample_len;
- u64 local_samples_len = __get_cpu_var(running_sample_length);
+ u64 local_samples_len;
if (atomic_read(&perf_sample_allowed_ns) == 0)
return;
@@ -947,8 +947,18 @@
{
struct perf_event_context *ctx;
- rcu_read_lock();
retry:
+ /*
+ * One of the few rules of preemptible RCU is that one cannot do
+ * rcu_read_unlock() while holding a scheduler (or nested) lock when
+ * part of the read side critical section was preemptible -- see
+ * rcu_read_unlock_special().
+ *
+ * Since ctx->lock nests under rq->lock we must ensure the entire read
+ * side critical section is non-preemptible.
+ */
+ preempt_disable();
+ rcu_read_lock();
ctx = rcu_dereference(task->perf_event_ctxp[ctxn]);
if (ctx) {
/*
@@ -964,6 +974,8 @@
raw_spin_lock_irqsave(&ctx->lock, *flags);
if (ctx != rcu_dereference(task->perf_event_ctxp[ctxn])) {
raw_spin_unlock_irqrestore(&ctx->lock, *flags);
+ rcu_read_unlock();
+ preempt_enable();
goto retry;
}
@@ -973,6 +985,7 @@
}
}
rcu_read_unlock();
+ preempt_enable();
return ctx;
}
@@ -1950,7 +1963,16 @@
struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
int err;
- if (WARN_ON_ONCE(!ctx->is_active))
+ /*
+ * There's a time window between 'ctx->is_active' check
+ * in perf_event_enable function and this place having:
+ * - IRQs on
+ * - ctx->lock unlocked
+ *
+ * where the task could be killed and 'ctx' deactivated
+ * by perf_event_exit_task.
+ */
+ if (!ctx->is_active)
return -EINVAL;
raw_spin_lock(&ctx->lock);
@@ -7465,7 +7487,7 @@
* child.
*/
- child_ctx = alloc_perf_context(event->pmu, child);
+ child_ctx = alloc_perf_context(parent_ctx->pmu, child);
if (!child_ctx)
return -ENOMEM;
diff --git a/kernel/fork.c b/kernel/fork.c
index 6e6a1c1..66635c8 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -365,8 +365,6 @@
mm->locked_vm = 0;
mm->mmap = NULL;
mm->mmap_cache = NULL;
- mm->free_area_cache = oldmm->mmap_base;
- mm->cached_hole_size = ~0UL;
mm->map_count = 0;
cpumask_clear(mm_cpumask(mm));
mm->mm_rb = RB_ROOT;
@@ -540,8 +538,6 @@
mm->nr_ptes = 0;
memset(&mm->rss_stat, 0, sizeof(mm->rss_stat));
spin_lock_init(&mm->page_table_lock);
- mm->free_area_cache = TASK_UNMAPPED_BASE;
- mm->cached_hole_size = ~0UL;
mm_init_aio(mm);
mm_init_owner(mm, p);
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index 10e663a..452d6f2 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -275,7 +275,7 @@
if (d->gc)
return -EBUSY;
- numchips = d->revmap_size / irqs_per_chip;
+ numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip);
if (!numchips)
return -EINVAL;
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 2d7cd34..706724e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -475,18 +475,6 @@
domain = controller ? irq_find_host(controller) : irq_default_domain;
if (!domain) {
-#ifdef CONFIG_MIPS
- /*
- * Workaround to avoid breaking interrupt controller drivers
- * that don't yet register an irq_domain. This is temporary
- * code. ~~~gcl, Feb 24, 2012
- *
- * Scheduled for removal in Linux v3.6. That should be enough
- * time.
- */
- if (intsize > 0)
- return intspec[0];
-#endif
pr_warn("no irq domain found for %s !\n",
of_node_full_name(controller));
return 0;
diff --git a/kernel/module.c b/kernel/module.c
index cab4bce..2069158 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -455,7 +455,7 @@
EXPORT_SYMBOL_GPL(find_symbol);
/* Search for module by name: must hold module_mutex. */
-static struct module *find_module_all(const char *name,
+static struct module *find_module_all(const char *name, size_t len,
bool even_unformed)
{
struct module *mod;
@@ -463,7 +463,7 @@
list_for_each_entry(mod, &modules, list) {
if (!even_unformed && mod->state == MODULE_STATE_UNFORMED)
continue;
- if (strcmp(mod->name, name) == 0)
+ if (strlen(mod->name) == len && !memcmp(mod->name, name, len))
return mod;
}
return NULL;
@@ -471,7 +471,7 @@
struct module *find_module(const char *name)
{
- return find_module_all(name, false);
+ return find_module_all(name, strlen(name), false);
}
EXPORT_SYMBOL_GPL(find_module);
@@ -482,23 +482,28 @@
return mod->percpu;
}
-static int percpu_modalloc(struct module *mod,
- unsigned long size, unsigned long align)
+static int percpu_modalloc(struct module *mod, struct load_info *info)
{
+ Elf_Shdr *pcpusec = &info->sechdrs[info->index.pcpu];
+ unsigned long align = pcpusec->sh_addralign;
+
+ if (!pcpusec->sh_size)
+ return 0;
+
if (align > PAGE_SIZE) {
printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n",
mod->name, align, PAGE_SIZE);
align = PAGE_SIZE;
}
- mod->percpu = __alloc_reserved_percpu(size, align);
+ mod->percpu = __alloc_reserved_percpu(pcpusec->sh_size, align);
if (!mod->percpu) {
printk(KERN_WARNING
"%s: Could not allocate %lu bytes percpu data\n",
- mod->name, size);
+ mod->name, (unsigned long)pcpusec->sh_size);
return -ENOMEM;
}
- mod->percpu_size = size;
+ mod->percpu_size = pcpusec->sh_size;
return 0;
}
@@ -563,10 +568,12 @@
{
return NULL;
}
-static inline int percpu_modalloc(struct module *mod,
- unsigned long size, unsigned long align)
+static int percpu_modalloc(struct module *mod, struct load_info *info)
{
- return -ENOMEM;
+ /* UP modules shouldn't have this section: ENOMEM isn't quite right */
+ if (info->sechdrs[info->index.pcpu].sh_size != 0)
+ return -ENOMEM;
+ return 0;
}
static inline void percpu_modfree(struct module *mod)
{
@@ -2927,7 +2934,6 @@
{
/* Module within temporary copy. */
struct module *mod;
- Elf_Shdr *pcpusec;
int err;
mod = setup_load_info(info, flags);
@@ -2942,17 +2948,10 @@
err = module_frob_arch_sections(info->hdr, info->sechdrs,
info->secstrings, mod);
if (err < 0)
- goto out;
+ return ERR_PTR(err);
- pcpusec = &info->sechdrs[info->index.pcpu];
- if (pcpusec->sh_size) {
- /* We have a special allocation for this section. */
- err = percpu_modalloc(mod,
- pcpusec->sh_size, pcpusec->sh_addralign);
- if (err)
- goto out;
- pcpusec->sh_flags &= ~(unsigned long)SHF_ALLOC;
- }
+ /* We will do a special allocation for per-cpu sections later. */
+ info->sechdrs[info->index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC;
/* Determine total sizes, and put offsets in sh_entsize. For now
this is done generically; there doesn't appear to be any
@@ -2963,17 +2962,12 @@
/* Allocate and move to the final place */
err = move_module(mod, info);
if (err)
- goto free_percpu;
+ return ERR_PTR(err);
/* Module has been copied to its final place now: return it. */
mod = (void *)info->sechdrs[info->index.mod].sh_addr;
kmemleak_load_module(mod, info);
return mod;
-
-free_percpu:
- percpu_modfree(mod);
-out:
- return ERR_PTR(err);
}
/* mod is no longer valid after this! */
@@ -3014,7 +3008,7 @@
bool ret;
mutex_lock(&module_mutex);
- mod = find_module_all(name, true);
+ mod = find_module_all(name, strlen(name), true);
ret = !mod || mod->state == MODULE_STATE_LIVE
|| mod->state == MODULE_STATE_GOING;
mutex_unlock(&module_mutex);
@@ -3152,7 +3146,8 @@
again:
mutex_lock(&module_mutex);
- if ((old = find_module_all(mod->name, true)) != NULL) {
+ old = find_module_all(mod->name, strlen(mod->name), true);
+ if (old != NULL) {
if (old->state == MODULE_STATE_COMING
|| old->state == MODULE_STATE_UNFORMED) {
/* Wait in case it fails to load. */
@@ -3198,6 +3193,17 @@
return err;
}
+static int unknown_module_param_cb(char *param, char *val, const char *modname)
+{
+ /* Check for magic 'dyndbg' arg */
+ int ret = ddebug_dyndbg_module_param_cb(param, val, modname);
+ if (ret != 0) {
+ printk(KERN_WARNING "%s: unknown parameter '%s' ignored\n",
+ modname, param);
+ }
+ return 0;
+}
+
/* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */
static int load_module(struct load_info *info, const char __user *uargs,
@@ -3237,6 +3243,11 @@
}
#endif
+ /* To avoid stressing percpu allocator, do this once we're unique. */
+ err = percpu_modalloc(mod, info);
+ if (err)
+ goto unlink_mod;
+
/* Now module is in final location, initialize linked lists, etc. */
err = module_unload_init(mod);
if (err)
@@ -3284,7 +3295,7 @@
/* Module is ready to execute: parsing args may do that. */
err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp,
- -32768, 32767, &ddebug_dyndbg_module_param_cb);
+ -32768, 32767, unknown_module_param_cb);
if (err < 0)
goto bug_cleanup;
@@ -3563,10 +3574,8 @@
/* Don't lock: we're in enough trouble already. */
preempt_disable();
if ((colon = strchr(name, ':')) != NULL) {
- *colon = '\0';
- if ((mod = find_module(name)) != NULL)
+ if ((mod = find_module_all(name, colon - name, false)) != NULL)
ret = mod_find_symname(mod, colon+1);
- *colon = ':';
} else {
list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
diff --git a/kernel/mutex.c b/kernel/mutex.c
index e581ada..ff05f4b 100644
--- a/kernel/mutex.c
+++ b/kernel/mutex.c
@@ -18,6 +18,7 @@
* Also see Documentation/mutex-design.txt.
*/
#include <linux/mutex.h>
+#include <linux/ww_mutex.h>
#include <linux/sched.h>
#include <linux/sched/rt.h>
#include <linux/export.h>
diff --git a/kernel/panic.c b/kernel/panic.c
index 9771231..8018646 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -15,6 +15,7 @@
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/random.h>
+#include <linux/ftrace.h>
#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/kexec.h>
@@ -399,6 +400,8 @@
static void warn_slowpath_common(const char *file, int line, void *caller,
unsigned taint, struct slowpath_args *args)
{
+ disable_trace_on_warning();
+
pr_warn("------------[ cut here ]------------\n");
pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS()\n",
raw_smp_processor_id(), current->pid, file, line, caller);
diff --git a/kernel/params.c b/kernel/params.c
index 53b958f..440e65d 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -787,7 +787,7 @@
}
/*
- * param_sysfs_builtin - add contents in /sys/parameters for built-in modules
+ * param_sysfs_builtin - add sysfs parameters for built-in modules
*
* Add module_parameters to sysfs for "modules" built into the kernel.
*
diff --git a/kernel/printk.c b/kernel/printk.c
index 8212c1a..d37d45c 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1369,9 +1369,9 @@
}
}
logbuf_cpu = UINT_MAX;
+ raw_spin_unlock(&logbuf_lock);
if (wake)
up(&console_sem);
- raw_spin_unlock(&logbuf_lock);
return retval;
}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9b1f2e5..0d8eb45 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -370,13 +370,6 @@
#ifdef CONFIG_SCHED_HRTICK
/*
* Use HR-timers to deliver accurate preemption points.
- *
- * Its all a bit involved since we cannot program an hrt while holding the
- * rq->lock. So what we do is store a state in in rq->hrtick_* and ask for a
- * reschedule event.
- *
- * When we get rescheduled we reprogram the hrtick_timer outside of the
- * rq->lock.
*/
static void hrtick_clear(struct rq *rq)
@@ -404,6 +397,15 @@
}
#ifdef CONFIG_SMP
+
+static int __hrtick_restart(struct rq *rq)
+{
+ struct hrtimer *timer = &rq->hrtick_timer;
+ ktime_t time = hrtimer_get_softexpires(timer);
+
+ return __hrtimer_start_range_ns(timer, time, 0, HRTIMER_MODE_ABS_PINNED, 0);
+}
+
/*
* called from hardirq (IPI) context
*/
@@ -412,7 +414,7 @@
struct rq *rq = arg;
raw_spin_lock(&rq->lock);
- hrtimer_restart(&rq->hrtick_timer);
+ __hrtick_restart(rq);
rq->hrtick_csd_pending = 0;
raw_spin_unlock(&rq->lock);
}
@@ -430,7 +432,7 @@
hrtimer_set_expires(timer, time);
if (rq == this_rq()) {
- hrtimer_restart(timer);
+ __hrtick_restart(rq);
} else if (!rq->hrtick_csd_pending) {
__smp_call_function_single(cpu_of(rq), &rq->hrtick_csd, 0);
rq->hrtick_csd_pending = 1;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 4ce13c3..ac09d98 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -599,6 +599,13 @@
.mode = 0644,
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "traceoff_on_warning",
+ .data = &__disable_trace_on_warning,
+ .maxlen = sizeof(__disable_trace_on_warning),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
#endif
#ifdef CONFIG_MODULES
{
@@ -800,7 +807,7 @@
#if defined(CONFIG_LOCKUP_DETECTOR)
{
.procname = "watchdog",
- .data = &watchdog_enabled,
+ .data = &watchdog_user_enabled,
.maxlen = sizeof (int),
.mode = 0644,
.proc_handler = proc_dowatchdog,
@@ -827,7 +834,7 @@
},
{
.procname = "nmi_watchdog",
- .data = &watchdog_enabled,
+ .data = &watchdog_user_enabled,
.maxlen = sizeof (int),
.mode = 0644,
.proc_handler = proc_dowatchdog,
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 6d3f916..218bcb5 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -157,7 +157,10 @@
dev->event_handler = tick_handle_periodic;
tick_device_setup_broadcast_func(dev);
cpumask_set_cpu(cpu, tick_broadcast_mask);
- tick_broadcast_start_periodic(bc);
+ if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
+ tick_broadcast_start_periodic(bc);
+ else
+ tick_broadcast_setup_oneshot(bc);
ret = 1;
} else {
/*
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 0cf1c14..6960172 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -178,6 +178,11 @@
*/
if (!sched_clock_stable) {
trace_tick_stop(0, "unstable sched clock\n");
+ /*
+ * Don't allow the user to think they can get
+ * full NO_HZ with this machine.
+ */
+ WARN_ONCE(1, "NO_HZ FULL will not work with unstable sched clock");
return false;
}
#endif
@@ -346,16 +351,6 @@
}
cpu_notifier(tick_nohz_cpu_down_callback, 0);
-
- /* Make sure full dynticks CPU are also RCU nocbs */
- for_each_cpu(cpu, nohz_full_mask) {
- if (!rcu_is_nocb_cpu(cpu)) {
- pr_warning("NO_HZ: CPU %d is not RCU nocb: "
- "cleared from nohz_full range", cpu);
- cpumask_clear_cpu(cpu, nohz_full_mask);
- }
- }
-
cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), nohz_full_mask);
pr_info("NO_HZ: Full dynticks CPUs: %s.\n", nohz_full_buf);
}
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 6c508ff..67708f4 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -413,6 +413,17 @@
return 0;
}
+static void ftrace_sync(struct work_struct *work)
+{
+ /*
+ * This function is just a stub to implement a hard force
+ * of synchronize_sched(). This requires synchronizing
+ * tasks even in userspace and idle.
+ *
+ * Yes, function tracing is rude.
+ */
+}
+
static int __unregister_ftrace_function(struct ftrace_ops *ops)
{
int ret;
@@ -440,8 +451,12 @@
* so there'll be no new users. We must ensure
* all current users are done before we free
* the control data.
+ * Note synchronize_sched() is not enough, as we
+ * use preempt_disable() to do RCU, but the function
+ * tracer can be called where RCU is not active
+ * (before user_exit()).
*/
- synchronize_sched();
+ schedule_on_each_cpu(ftrace_sync);
control_ops_free(ops);
}
} else
@@ -456,9 +471,13 @@
/*
* Dynamic ops may be freed, we must make sure that all
* callers are done before leaving this function.
+ *
+ * Again, normal synchronize_sched() is not good enough.
+ * We need to do a hard force of sched synchronization.
*/
if (ops->flags & FTRACE_OPS_FL_DYNAMIC)
- synchronize_sched();
+ schedule_on_each_cpu(ftrace_sync);
+
return 0;
}
@@ -622,12 +641,18 @@
if (rec->counter <= 1)
stddev = 0;
else {
- stddev = rec->time_squared - rec->counter * avg * avg;
+ /*
+ * Apply Welford's method:
+ * s^2 = 1 / (n * (n-1)) * (n * \Sum (x_i)^2 - (\Sum x_i)^2)
+ */
+ stddev = rec->counter * rec->time_squared -
+ rec->time * rec->time;
+
/*
* Divide only 1000 for ns^2 -> us^2 conversion.
* trace_print_graph_duration will divide 1000 again.
*/
- do_div(stddev, (rec->counter - 1) * 1000);
+ do_div(stddev, rec->counter * (rec->counter - 1) * 1000);
}
trace_seq_init(&s);
@@ -3512,8 +3537,12 @@
static char ftrace_notrace_buf[FTRACE_FILTER_SIZE] __initdata;
static char ftrace_filter_buf[FTRACE_FILTER_SIZE] __initdata;
+/* Used by function selftest to not test if filter is set */
+bool ftrace_filter_param __initdata;
+
static int __init set_ftrace_notrace(char *str)
{
+ ftrace_filter_param = true;
strlcpy(ftrace_notrace_buf, str, FTRACE_FILTER_SIZE);
return 1;
}
@@ -3521,6 +3550,7 @@
static int __init set_ftrace_filter(char *str)
{
+ ftrace_filter_param = true;
strlcpy(ftrace_filter_buf, str, FTRACE_FILTER_SIZE);
return 1;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index e71a8be..0cd500b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -115,6 +115,9 @@
enum ftrace_dump_mode ftrace_dump_on_oops;
+/* When set, tracing will stop when a WARN*() is hit */
+int __disable_trace_on_warning;
+
static int tracing_set_tracer(const char *buf);
#define MAX_TRACER_SIZE 100
@@ -149,6 +152,13 @@
}
__setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops);
+static int __init stop_trace_on_warning(char *str)
+{
+ __disable_trace_on_warning = 1;
+ return 1;
+}
+__setup("traceoff_on_warning=", stop_trace_on_warning);
+
static int __init boot_alloc_snapshot(char *str)
{
allocate_snapshot = true;
@@ -170,6 +180,7 @@
}
__setup("trace_options=", set_trace_boot_options);
+
unsigned long long ns2usecs(cycle_t nsec)
{
nsec += 500;
@@ -193,6 +204,37 @@
LIST_HEAD(ftrace_trace_arrays);
+int trace_array_get(struct trace_array *this_tr)
+{
+ struct trace_array *tr;
+ int ret = -ENODEV;
+
+ mutex_lock(&trace_types_lock);
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+ if (tr == this_tr) {
+ tr->ref++;
+ ret = 0;
+ break;
+ }
+ }
+ mutex_unlock(&trace_types_lock);
+
+ return ret;
+}
+
+static void __trace_array_put(struct trace_array *this_tr)
+{
+ WARN_ON(!this_tr->ref);
+ this_tr->ref--;
+}
+
+void trace_array_put(struct trace_array *this_tr)
+{
+ mutex_lock(&trace_types_lock);
+ __trace_array_put(this_tr);
+ mutex_unlock(&trace_types_lock);
+}
+
int filter_current_check_discard(struct ring_buffer *buffer,
struct ftrace_event_call *call, void *rec,
struct ring_buffer_event *event)
@@ -215,9 +257,24 @@
return ts;
}
+/**
+ * tracing_is_enabled - Show if global_trace has been disabled
+ *
+ * Shows if the global trace has been enabled or not. It uses the
+ * mirror flag "buffer_disabled" to be used in fast paths such as for
+ * the irqsoff tracer. But it may be inaccurate due to races. If you
+ * need to know the accurate state, use tracing_is_on() which is a little
+ * slower, but accurate.
+ */
int tracing_is_enabled(void)
{
- return tracing_is_on();
+ /*
+ * For quick access (irqsoff uses this in fast path), just
+ * return the mirror variable of the state of the ring buffer.
+ * It's a little racy, but we don't really care.
+ */
+ smp_rmb();
+ return !global_trace.buffer_disabled;
}
/*
@@ -240,7 +297,7 @@
/*
* trace_types_lock is used to protect the trace_types list.
*/
-static DEFINE_MUTEX(trace_types_lock);
+DEFINE_MUTEX(trace_types_lock);
/*
* serialize the access of the ring buffer
@@ -330,6 +387,23 @@
TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE |
TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS | TRACE_ITER_FUNCTION;
+static void tracer_tracing_on(struct trace_array *tr)
+{
+ if (tr->trace_buffer.buffer)
+ ring_buffer_record_on(tr->trace_buffer.buffer);
+ /*
+ * This flag is looked at when buffers haven't been allocated
+ * yet, or by some tracers (like irqsoff), that just want to
+ * know if the ring buffer has been disabled, but it can handle
+ * races of where it gets disabled but we still do a record.
+ * As the check is in the fast path of the tracers, it is more
+ * important to be fast than accurate.
+ */
+ tr->buffer_disabled = 0;
+ /* Make the flag seen by readers */
+ smp_wmb();
+}
+
/**
* tracing_on - enable tracing buffers
*
@@ -338,15 +412,7 @@
*/
void tracing_on(void)
{
- if (global_trace.trace_buffer.buffer)
- ring_buffer_record_on(global_trace.trace_buffer.buffer);
- /*
- * This flag is only looked at when buffers haven't been
- * allocated yet. We don't really care about the race
- * between setting this flag and actually turning
- * on the buffer.
- */
- global_trace.buffer_disabled = 0;
+ tracer_tracing_on(&global_trace);
}
EXPORT_SYMBOL_GPL(tracing_on);
@@ -540,6 +606,23 @@
EXPORT_SYMBOL_GPL(tracing_snapshot_alloc);
#endif /* CONFIG_TRACER_SNAPSHOT */
+static void tracer_tracing_off(struct trace_array *tr)
+{
+ if (tr->trace_buffer.buffer)
+ ring_buffer_record_off(tr->trace_buffer.buffer);
+ /*
+ * This flag is looked at when buffers haven't been allocated
+ * yet, or by some tracers (like irqsoff), that just want to
+ * know if the ring buffer has been disabled, but it can handle
+ * races of where it gets disabled but we still do a record.
+ * As the check is in the fast path of the tracers, it is more
+ * important to be fast than accurate.
+ */
+ tr->buffer_disabled = 1;
+ /* Make the flag seen by readers */
+ smp_wmb();
+}
+
/**
* tracing_off - turn off tracing buffers
*
@@ -550,26 +633,35 @@
*/
void tracing_off(void)
{
- if (global_trace.trace_buffer.buffer)
- ring_buffer_record_off(global_trace.trace_buffer.buffer);
- /*
- * This flag is only looked at when buffers haven't been
- * allocated yet. We don't really care about the race
- * between setting this flag and actually turning
- * on the buffer.
- */
- global_trace.buffer_disabled = 1;
+ tracer_tracing_off(&global_trace);
}
EXPORT_SYMBOL_GPL(tracing_off);
+void disable_trace_on_warning(void)
+{
+ if (__disable_trace_on_warning)
+ tracing_off();
+}
+
+/**
+ * tracer_tracing_is_on - show real state of ring buffer enabled
+ * @tr : the trace array to know if ring buffer is enabled
+ *
+ * Shows real state of the ring buffer if it is enabled or not.
+ */
+static int tracer_tracing_is_on(struct trace_array *tr)
+{
+ if (tr->trace_buffer.buffer)
+ return ring_buffer_record_is_on(tr->trace_buffer.buffer);
+ return !tr->buffer_disabled;
+}
+
/**
* tracing_is_on - show state of ring buffers enabled
*/
int tracing_is_on(void)
{
- if (global_trace.trace_buffer.buffer)
- return ring_buffer_record_is_on(global_trace.trace_buffer.buffer);
- return !global_trace.buffer_disabled;
+ return tracer_tracing_is_on(&global_trace);
}
EXPORT_SYMBOL_GPL(tracing_is_on);
@@ -1543,15 +1635,6 @@
__buffer_unlock_commit(buffer, event);
}
-void
-ftrace(struct trace_array *tr, struct trace_array_cpu *data,
- unsigned long ip, unsigned long parent_ip, unsigned long flags,
- int pc)
-{
- if (likely(!atomic_read(&data->disabled)))
- trace_function(tr, ip, parent_ip, flags, pc);
-}
-
#ifdef CONFIG_STACKTRACE
#define FTRACE_STACK_MAX_ENTRIES (PAGE_SIZE / sizeof(unsigned long))
@@ -2768,10 +2851,9 @@
};
static struct trace_iterator *
-__tracing_open(struct inode *inode, struct file *file, bool snapshot)
+__tracing_open(struct trace_array *tr, struct trace_cpu *tc,
+ struct inode *inode, struct file *file, bool snapshot)
{
- struct trace_cpu *tc = inode->i_private;
- struct trace_array *tr = tc->tr;
struct trace_iterator *iter;
int cpu;
@@ -2850,8 +2932,6 @@
tracing_iter_reset(iter, cpu);
}
- tr->ref++;
-
mutex_unlock(&trace_types_lock);
return iter;
@@ -2874,6 +2954,43 @@
return 0;
}
+/*
+ * Open and update trace_array ref count.
+ * Must have the current trace_array passed to it.
+ */
+static int tracing_open_generic_tr(struct inode *inode, struct file *filp)
+{
+ struct trace_array *tr = inode->i_private;
+
+ if (tracing_disabled)
+ return -ENODEV;
+
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
+ filp->private_data = inode->i_private;
+
+ return 0;
+
+}
+
+static int tracing_open_generic_tc(struct inode *inode, struct file *filp)
+{
+ struct trace_cpu *tc = inode->i_private;
+ struct trace_array *tr = tc->tr;
+
+ if (tracing_disabled)
+ return -ENODEV;
+
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
+ filp->private_data = inode->i_private;
+
+ return 0;
+
+}
+
static int tracing_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
@@ -2881,17 +2998,20 @@
struct trace_array *tr;
int cpu;
- if (!(file->f_mode & FMODE_READ))
+ /* Writes do not use seq_file, need to grab tr from inode */
+ if (!(file->f_mode & FMODE_READ)) {
+ struct trace_cpu *tc = inode->i_private;
+
+ trace_array_put(tc->tr);
return 0;
+ }
iter = m->private;
tr = iter->tr;
+ trace_array_put(tr);
mutex_lock(&trace_types_lock);
- WARN_ON(!tr->ref);
- tr->ref--;
-
for_each_tracing_cpu(cpu) {
if (iter->buffer_iter[cpu])
ring_buffer_read_finish(iter->buffer_iter[cpu]);
@@ -2910,20 +3030,49 @@
kfree(iter->trace);
kfree(iter->buffer_iter);
seq_release_private(inode, file);
+
return 0;
}
+static int tracing_release_generic_tr(struct inode *inode, struct file *file)
+{
+ struct trace_array *tr = inode->i_private;
+
+ trace_array_put(tr);
+ return 0;
+}
+
+static int tracing_release_generic_tc(struct inode *inode, struct file *file)
+{
+ struct trace_cpu *tc = inode->i_private;
+ struct trace_array *tr = tc->tr;
+
+ trace_array_put(tr);
+ return 0;
+}
+
+static int tracing_single_release_tr(struct inode *inode, struct file *file)
+{
+ struct trace_array *tr = inode->i_private;
+
+ trace_array_put(tr);
+
+ return single_release(inode, file);
+}
+
static int tracing_open(struct inode *inode, struct file *file)
{
+ struct trace_cpu *tc = inode->i_private;
+ struct trace_array *tr = tc->tr;
struct trace_iterator *iter;
int ret = 0;
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
/* If this file was open for write, then erase contents */
if ((file->f_mode & FMODE_WRITE) &&
(file->f_flags & O_TRUNC)) {
- struct trace_cpu *tc = inode->i_private;
- struct trace_array *tr = tc->tr;
-
if (tc->cpu == RING_BUFFER_ALL_CPUS)
tracing_reset_online_cpus(&tr->trace_buffer);
else
@@ -2931,12 +3080,16 @@
}
if (file->f_mode & FMODE_READ) {
- iter = __tracing_open(inode, file, false);
+ iter = __tracing_open(tr, tc, inode, file, false);
if (IS_ERR(iter))
ret = PTR_ERR(iter);
else if (trace_flags & TRACE_ITER_LATENCY_FMT)
iter->iter_flags |= TRACE_FILE_LAT_FMT;
}
+
+ if (ret < 0)
+ trace_array_put(tr);
+
return ret;
}
@@ -3293,9 +3446,14 @@
static int tracing_trace_options_open(struct inode *inode, struct file *file)
{
+ struct trace_array *tr = inode->i_private;
+
if (tracing_disabled)
return -ENODEV;
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
return single_open(file, tracing_trace_options_show, inode->i_private);
}
@@ -3303,7 +3461,7 @@
.open = tracing_trace_options_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = single_release,
+ .release = tracing_single_release_tr,
.write = tracing_trace_options_write,
};
@@ -3791,6 +3949,9 @@
if (tracing_disabled)
return -ENODEV;
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
mutex_lock(&trace_types_lock);
/* create a buffer to store the information to pass to userspace */
@@ -3843,6 +4004,7 @@
fail:
kfree(iter->trace);
kfree(iter);
+ __trace_array_put(tr);
mutex_unlock(&trace_types_lock);
return ret;
}
@@ -3850,6 +4012,8 @@
static int tracing_release_pipe(struct inode *inode, struct file *file)
{
struct trace_iterator *iter = file->private_data;
+ struct trace_cpu *tc = inode->i_private;
+ struct trace_array *tr = tc->tr;
mutex_lock(&trace_types_lock);
@@ -3863,6 +4027,8 @@
kfree(iter->trace);
kfree(iter);
+ trace_array_put(tr);
+
return 0;
}
@@ -3939,7 +4105,7 @@
*
* iter->pos will be 0 if we haven't read anything.
*/
- if (!tracing_is_enabled() && iter->pos)
+ if (!tracing_is_on() && iter->pos)
break;
}
@@ -4320,6 +4486,8 @@
/* resize the ring buffer to 0 */
tracing_resize_ring_buffer(tr, 0, RING_BUFFER_ALL_CPUS);
+ trace_array_put(tr);
+
return 0;
}
@@ -4328,6 +4496,7 @@
size_t cnt, loff_t *fpos)
{
unsigned long addr = (unsigned long)ubuf;
+ struct trace_array *tr = filp->private_data;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
struct print_entry *entry;
@@ -4387,7 +4556,7 @@
local_save_flags(irq_flags);
size = sizeof(*entry) + cnt + 2; /* possible \n added */
- buffer = global_trace.trace_buffer.buffer;
+ buffer = tr->trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
irq_flags, preempt_count());
if (!event) {
@@ -4495,10 +4664,20 @@
static int tracing_clock_open(struct inode *inode, struct file *file)
{
+ struct trace_array *tr = inode->i_private;
+ int ret;
+
if (tracing_disabled)
return -ENODEV;
- return single_open(file, tracing_clock_show, inode->i_private);
+ if (trace_array_get(tr))
+ return -ENODEV;
+
+ ret = single_open(file, tracing_clock_show, inode->i_private);
+ if (ret < 0)
+ trace_array_put(tr);
+
+ return ret;
}
struct ftrace_buffer_info {
@@ -4511,12 +4690,16 @@
static int tracing_snapshot_open(struct inode *inode, struct file *file)
{
struct trace_cpu *tc = inode->i_private;
+ struct trace_array *tr = tc->tr;
struct trace_iterator *iter;
struct seq_file *m;
int ret = 0;
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
if (file->f_mode & FMODE_READ) {
- iter = __tracing_open(inode, file, true);
+ iter = __tracing_open(tr, tc, inode, file, true);
if (IS_ERR(iter))
ret = PTR_ERR(iter);
} else {
@@ -4529,13 +4712,16 @@
kfree(m);
return -ENOMEM;
}
- iter->tr = tc->tr;
+ iter->tr = tr;
iter->trace_buffer = &tc->tr->max_buffer;
iter->cpu_file = tc->cpu;
m->private = iter;
file->private_data = m;
}
+ if (ret < 0)
+ trace_array_put(tr);
+
return ret;
}
@@ -4616,9 +4802,12 @@
static int tracing_snapshot_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
+ int ret;
+
+ ret = tracing_release(inode, file);
if (file->f_mode & FMODE_READ)
- return tracing_release(inode, file);
+ return ret;
/* If write only, the seq_file is just a stub */
if (m)
@@ -4684,34 +4873,38 @@
};
static const struct file_operations tracing_entries_fops = {
- .open = tracing_open_generic,
+ .open = tracing_open_generic_tc,
.read = tracing_entries_read,
.write = tracing_entries_write,
.llseek = generic_file_llseek,
+ .release = tracing_release_generic_tc,
};
static const struct file_operations tracing_total_entries_fops = {
- .open = tracing_open_generic,
+ .open = tracing_open_generic_tr,
.read = tracing_total_entries_read,
.llseek = generic_file_llseek,
+ .release = tracing_release_generic_tr,
};
static const struct file_operations tracing_free_buffer_fops = {
+ .open = tracing_open_generic_tr,
.write = tracing_free_buffer_write,
.release = tracing_free_buffer_release,
};
static const struct file_operations tracing_mark_fops = {
- .open = tracing_open_generic,
+ .open = tracing_open_generic_tr,
.write = tracing_mark_write,
.llseek = generic_file_llseek,
+ .release = tracing_release_generic_tr,
};
static const struct file_operations trace_clock_fops = {
.open = tracing_clock_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = single_release,
+ .release = tracing_single_release_tr,
.write = tracing_clock_write,
};
@@ -4739,13 +4932,19 @@
struct trace_cpu *tc = inode->i_private;
struct trace_array *tr = tc->tr;
struct ftrace_buffer_info *info;
+ int ret;
if (tracing_disabled)
return -ENODEV;
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
+ if (!info) {
+ trace_array_put(tr);
return -ENOMEM;
+ }
mutex_lock(&trace_types_lock);
@@ -4763,7 +4962,11 @@
mutex_unlock(&trace_types_lock);
- return nonseekable_open(inode, filp);
+ ret = nonseekable_open(inode, filp);
+ if (ret < 0)
+ trace_array_put(tr);
+
+ return ret;
}
static unsigned int
@@ -4863,8 +5066,7 @@
mutex_lock(&trace_types_lock);
- WARN_ON(!iter->tr->ref);
- iter->tr->ref--;
+ __trace_array_put(iter->tr);
if (info->spare)
ring_buffer_free_read_page(iter->trace_buffer->buffer, info->spare);
@@ -5612,15 +5814,10 @@
size_t cnt, loff_t *ppos)
{
struct trace_array *tr = filp->private_data;
- struct ring_buffer *buffer = tr->trace_buffer.buffer;
char buf[64];
int r;
- if (buffer)
- r = ring_buffer_record_is_on(buffer);
- else
- r = 0;
-
+ r = tracer_tracing_is_on(tr);
r = sprintf(buf, "%d\n", r);
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
@@ -5642,11 +5839,11 @@
if (buffer) {
mutex_lock(&trace_types_lock);
if (val) {
- ring_buffer_record_on(buffer);
+ tracer_tracing_on(tr);
if (tr->current_trace->start)
tr->current_trace->start(tr);
} else {
- ring_buffer_record_off(buffer);
+ tracer_tracing_off(tr);
if (tr->current_trace->stop)
tr->current_trace->stop(tr);
}
@@ -5659,9 +5856,10 @@
}
static const struct file_operations rb_simple_fops = {
- .open = tracing_open_generic,
+ .open = tracing_open_generic_tr,
.read = rb_simple_read,
.write = rb_simple_write,
+ .release = tracing_release_generic_tr,
.llseek = default_llseek,
};
@@ -5933,7 +6131,7 @@
trace_create_file("buffer_total_size_kb", 0444, d_tracer,
tr, &tracing_total_entries_fops);
- trace_create_file("free_buffer", 0644, d_tracer,
+ trace_create_file("free_buffer", 0200, d_tracer,
tr, &tracing_free_buffer_fops);
trace_create_file("trace_marker", 0220, d_tracer,
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 20572ed..4a4f6e1 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -224,6 +224,11 @@
extern struct list_head ftrace_trace_arrays;
+extern struct mutex trace_types_lock;
+
+extern int trace_array_get(struct trace_array *tr);
+extern void trace_array_put(struct trace_array *tr);
+
/*
* The global tracer (top) should be the first trace array added,
* but we check the flag anyway.
@@ -554,11 +559,6 @@
void poll_wait_pipe(struct trace_iterator *iter);
-void ftrace(struct trace_array *tr,
- struct trace_array_cpu *data,
- unsigned long ip,
- unsigned long parent_ip,
- unsigned long flags, int pc);
void tracing_sched_switch_trace(struct trace_array *tr,
struct task_struct *prev,
struct task_struct *next,
@@ -774,6 +774,7 @@
extern struct list_head ftrace_pids;
#ifdef CONFIG_FUNCTION_TRACER
+extern bool ftrace_filter_param __initdata;
static inline int ftrace_trace_task(struct task_struct *task)
{
if (list_empty(&ftrace_pids))
@@ -899,12 +900,6 @@
/* set ring buffers to default size if not already done so */
int tracing_update_buffers(void);
-/* trace event type bit fields, not numeric */
-enum {
- TRACE_EVENT_TYPE_PRINTF = 1,
- TRACE_EVENT_TYPE_RAW = 2,
-};
-
struct ftrace_event_field {
struct list_head link;
const char *name;
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 27963e2..7d85429 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -41,6 +41,23 @@
static struct kmem_cache *field_cachep;
static struct kmem_cache *file_cachep;
+#define SYSTEM_FL_FREE_NAME (1 << 31)
+
+static inline int system_refcount(struct event_subsystem *system)
+{
+ return system->ref_count & ~SYSTEM_FL_FREE_NAME;
+}
+
+static int system_refcount_inc(struct event_subsystem *system)
+{
+ return (system->ref_count++) & ~SYSTEM_FL_FREE_NAME;
+}
+
+static int system_refcount_dec(struct event_subsystem *system)
+{
+ return (--system->ref_count) & ~SYSTEM_FL_FREE_NAME;
+}
+
/* Double loops, do not use break, only goto's work */
#define do_for_each_event_file(tr, file) \
list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
@@ -97,7 +114,7 @@
field = kmem_cache_alloc(field_cachep, GFP_TRACE);
if (!field)
- goto err;
+ return -ENOMEM;
field->name = name;
field->type = type;
@@ -114,11 +131,6 @@
list_add(&field->link, head);
return 0;
-
-err:
- kmem_cache_free(field_cachep, field);
-
- return -ENOMEM;
}
int trace_define_field(struct ftrace_event_call *call, const char *type,
@@ -279,9 +291,11 @@
}
call->class->reg(call, TRACE_REG_UNREGISTER, file);
}
- /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT */
+ /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT, else clear it */
if (file->flags & FTRACE_EVENT_FL_SOFT_MODE)
set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);
+ else
+ clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);
break;
case 1:
/*
@@ -349,8 +363,8 @@
{
struct event_filter *filter = system->filter;
- WARN_ON_ONCE(system->ref_count == 0);
- if (--system->ref_count)
+ WARN_ON_ONCE(system_refcount(system) == 0);
+ if (system_refcount_dec(system))
return;
list_del(&system->list);
@@ -359,13 +373,15 @@
kfree(filter->filter_string);
kfree(filter);
}
+ if (system->ref_count & SYSTEM_FL_FREE_NAME)
+ kfree(system->name);
kfree(system);
}
static void __get_system(struct event_subsystem *system)
{
- WARN_ON_ONCE(system->ref_count == 0);
- system->ref_count++;
+ WARN_ON_ONCE(system_refcount(system) == 0);
+ system_refcount_inc(system);
}
static void __get_system_dir(struct ftrace_subsystem_dir *dir)
@@ -379,7 +395,7 @@
{
WARN_ON_ONCE(dir->ref_count == 0);
/* If the subsystem is about to be freed, the dir must be too */
- WARN_ON_ONCE(dir->subsystem->ref_count == 1 && dir->ref_count != 1);
+ WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1);
__put_system(dir->subsystem);
if (!--dir->ref_count)
@@ -394,16 +410,45 @@
}
/*
+ * Open and update trace_array ref count.
+ * Must have the current trace_array passed to it.
+ */
+static int tracing_open_generic_file(struct inode *inode, struct file *filp)
+{
+ struct ftrace_event_file *file = inode->i_private;
+ struct trace_array *tr = file->tr;
+ int ret;
+
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
+ ret = tracing_open_generic(inode, filp);
+ if (ret < 0)
+ trace_array_put(tr);
+ return ret;
+}
+
+static int tracing_release_generic_file(struct inode *inode, struct file *filp)
+{
+ struct ftrace_event_file *file = inode->i_private;
+ struct trace_array *tr = file->tr;
+
+ trace_array_put(tr);
+
+ return 0;
+}
+
+/*
* __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
*/
-static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
- const char *sub, const char *event, int set)
+static int
+__ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
+ const char *sub, const char *event, int set)
{
struct ftrace_event_file *file;
struct ftrace_event_call *call;
int ret = -EINVAL;
- mutex_lock(&event_mutex);
list_for_each_entry(file, &tr->events, list) {
call = file->event_call;
@@ -429,6 +474,17 @@
ret = 0;
}
+
+ return ret;
+}
+
+static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
+ const char *sub, const char *event, int set)
+{
+ int ret;
+
+ mutex_lock(&event_mutex);
+ ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set);
mutex_unlock(&event_mutex);
return ret;
@@ -624,17 +680,17 @@
loff_t *ppos)
{
struct ftrace_event_file *file = filp->private_data;
- char *buf;
+ char buf[4] = "0";
- if (file->flags & FTRACE_EVENT_FL_ENABLED) {
- if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED)
- buf = "0*\n";
- else if (file->flags & FTRACE_EVENT_FL_SOFT_MODE)
- buf = "1*\n";
- else
- buf = "1\n";
- } else
- buf = "0\n";
+ if (file->flags & FTRACE_EVENT_FL_ENABLED &&
+ !(file->flags & FTRACE_EVENT_FL_SOFT_DISABLED))
+ strcpy(buf, "1");
+
+ if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED ||
+ file->flags & FTRACE_EVENT_FL_SOFT_MODE)
+ strcat(buf, "*");
+
+ strcat(buf, "\n");
return simple_read_from_buffer(ubuf, cnt, ppos, buf, strlen(buf));
}
@@ -992,6 +1048,7 @@
int ret;
/* Make sure the system still exists */
+ mutex_lock(&trace_types_lock);
mutex_lock(&event_mutex);
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
list_for_each_entry(dir, &tr->systems, list) {
@@ -1007,6 +1064,7 @@
}
exit_loop:
mutex_unlock(&event_mutex);
+ mutex_unlock(&trace_types_lock);
if (!system)
return -ENODEV;
@@ -1014,9 +1072,17 @@
/* Some versions of gcc think dir can be uninitialized here */
WARN_ON(!dir);
- ret = tracing_open_generic(inode, filp);
- if (ret < 0)
+ /* Still need to increment the ref count of the system */
+ if (trace_array_get(tr) < 0) {
put_system(dir);
+ return -ENODEV;
+ }
+
+ ret = tracing_open_generic(inode, filp);
+ if (ret < 0) {
+ trace_array_put(tr);
+ put_system(dir);
+ }
return ret;
}
@@ -1027,16 +1093,23 @@
struct trace_array *tr = inode->i_private;
int ret;
+ if (trace_array_get(tr) < 0)
+ return -ENODEV;
+
/* Make a temporary dir that has no system but points to tr */
dir = kzalloc(sizeof(*dir), GFP_KERNEL);
- if (!dir)
+ if (!dir) {
+ trace_array_put(tr);
return -ENOMEM;
+ }
dir->tr = tr;
ret = tracing_open_generic(inode, filp);
- if (ret < 0)
+ if (ret < 0) {
+ trace_array_put(tr);
kfree(dir);
+ }
filp->private_data = dir;
@@ -1047,6 +1120,8 @@
{
struct ftrace_subsystem_dir *dir = file->private_data;
+ trace_array_put(dir->tr);
+
/*
* If dir->subsystem is NULL, then this is a temporary
* descriptor that was made for a trace_array to enable
@@ -1174,9 +1249,10 @@
};
static const struct file_operations ftrace_enable_fops = {
- .open = tracing_open_generic,
+ .open = tracing_open_generic_file,
.read = event_enable_read,
.write = event_enable_write,
+ .release = tracing_release_generic_file,
.llseek = default_llseek,
};
@@ -1279,7 +1355,15 @@
return NULL;
system->ref_count = 1;
- system->name = name;
+
+ /* Only allocate if dynamic (kprobes and modules) */
+ if (!core_kernel_data((unsigned long)name)) {
+ system->ref_count |= SYSTEM_FL_FREE_NAME;
+ system->name = kstrdup(name, GFP_KERNEL);
+ if (!system->name)
+ goto out_free;
+ } else
+ system->name = name;
system->filter = NULL;
@@ -1292,6 +1376,8 @@
return system;
out_free:
+ if (system->ref_count & SYSTEM_FL_FREE_NAME)
+ kfree(system->name);
kfree(system);
return NULL;
}
@@ -1591,6 +1677,7 @@
int trace_add_event_call(struct ftrace_event_call *call)
{
int ret;
+ mutex_lock(&trace_types_lock);
mutex_lock(&event_mutex);
ret = __register_event(call, NULL);
@@ -1598,11 +1685,13 @@
__add_event_to_tracers(call, NULL);
mutex_unlock(&event_mutex);
+ mutex_unlock(&trace_types_lock);
return ret;
}
/*
- * Must be called under locking both of event_mutex and trace_event_sem.
+ * Must be called under locking of trace_types_lock, event_mutex and
+ * trace_event_sem.
*/
static void __trace_remove_event_call(struct ftrace_event_call *call)
{
@@ -1614,11 +1703,13 @@
/* Remove an event_call */
void trace_remove_event_call(struct ftrace_event_call *call)
{
+ mutex_lock(&trace_types_lock);
mutex_lock(&event_mutex);
down_write(&trace_event_sem);
__trace_remove_event_call(call);
up_write(&trace_event_sem);
mutex_unlock(&event_mutex);
+ mutex_unlock(&trace_types_lock);
}
#define for_each_event(event, start, end) \
@@ -1762,6 +1853,7 @@
{
struct module *mod = data;
+ mutex_lock(&trace_types_lock);
mutex_lock(&event_mutex);
switch (val) {
case MODULE_STATE_COMING:
@@ -1772,6 +1864,7 @@
break;
}
mutex_unlock(&event_mutex);
+ mutex_unlock(&trace_types_lock);
return 0;
}
@@ -2011,10 +2104,7 @@
int ret;
/* hash funcs only work with set_ftrace_filter */
- if (!enabled)
- return -EINVAL;
-
- if (!param)
+ if (!enabled || !param)
return -EINVAL;
system = strsep(¶m, ":");
@@ -2329,11 +2419,11 @@
int event_trace_del_tracer(struct trace_array *tr)
{
- /* Disable any running events */
- __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0);
-
mutex_lock(&event_mutex);
+ /* Disable any running events */
+ __ftrace_set_clr_event_nolock(tr, NULL, NULL, NULL, 0);
+
down_write(&trace_event_sem);
__trace_remove_event_dirs(tr);
debugfs_remove_recursive(tr->event_dir);
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index e1b653f..0d883dc 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -44,6 +44,7 @@
OP_LE,
OP_GT,
OP_GE,
+ OP_BAND,
OP_NONE,
OP_OPEN_PAREN,
};
@@ -54,6 +55,7 @@
int precedence;
};
+/* Order must be the same as enum filter_op_ids above */
static struct filter_op filter_ops[] = {
{ OP_OR, "||", 1 },
{ OP_AND, "&&", 2 },
@@ -64,6 +66,7 @@
{ OP_LE, "<=", 5 },
{ OP_GT, ">", 5 },
{ OP_GE, ">=", 5 },
+ { OP_BAND, "&", 6 },
{ OP_NONE, "OP_NONE", 0 },
{ OP_OPEN_PAREN, "(", 0 },
};
@@ -156,6 +159,9 @@
case OP_GE: \
match = (*addr >= val); \
break; \
+ case OP_BAND: \
+ match = (*addr & val); \
+ break; \
default: \
break; \
} \
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index c4d6d71..b863f93 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -290,6 +290,21 @@
trace_dump_stack(STACK_SKIP);
}
+static void
+ftrace_dump_probe(unsigned long ip, unsigned long parent_ip, void **data)
+{
+ if (update_count(data))
+ ftrace_dump(DUMP_ALL);
+}
+
+/* Only dump the current CPU buffer. */
+static void
+ftrace_cpudump_probe(unsigned long ip, unsigned long parent_ip, void **data)
+{
+ if (update_count(data))
+ ftrace_dump(DUMP_ORIG);
+}
+
static int
ftrace_probe_print(const char *name, struct seq_file *m,
unsigned long ip, void *data)
@@ -327,6 +342,20 @@
return ftrace_probe_print("stacktrace", m, ip, data);
}
+static int
+ftrace_dump_print(struct seq_file *m, unsigned long ip,
+ struct ftrace_probe_ops *ops, void *data)
+{
+ return ftrace_probe_print("dump", m, ip, data);
+}
+
+static int
+ftrace_cpudump_print(struct seq_file *m, unsigned long ip,
+ struct ftrace_probe_ops *ops, void *data)
+{
+ return ftrace_probe_print("cpudump", m, ip, data);
+}
+
static struct ftrace_probe_ops traceon_count_probe_ops = {
.func = ftrace_traceon_count,
.print = ftrace_traceon_print,
@@ -342,6 +371,16 @@
.print = ftrace_stacktrace_print,
};
+static struct ftrace_probe_ops dump_probe_ops = {
+ .func = ftrace_dump_probe,
+ .print = ftrace_dump_print,
+};
+
+static struct ftrace_probe_ops cpudump_probe_ops = {
+ .func = ftrace_cpudump_probe,
+ .print = ftrace_cpudump_print,
+};
+
static struct ftrace_probe_ops traceon_probe_ops = {
.func = ftrace_traceon,
.print = ftrace_traceon_print,
@@ -425,6 +464,32 @@
param, enable);
}
+static int
+ftrace_dump_callback(struct ftrace_hash *hash,
+ char *glob, char *cmd, char *param, int enable)
+{
+ struct ftrace_probe_ops *ops;
+
+ ops = &dump_probe_ops;
+
+ /* Only dump once. */
+ return ftrace_trace_probe_callback(ops, hash, glob, cmd,
+ "1", enable);
+}
+
+static int
+ftrace_cpudump_callback(struct ftrace_hash *hash,
+ char *glob, char *cmd, char *param, int enable)
+{
+ struct ftrace_probe_ops *ops;
+
+ ops = &cpudump_probe_ops;
+
+ /* Only dump once. */
+ return ftrace_trace_probe_callback(ops, hash, glob, cmd,
+ "1", enable);
+}
+
static struct ftrace_func_command ftrace_traceon_cmd = {
.name = "traceon",
.func = ftrace_trace_onoff_callback,
@@ -440,6 +505,16 @@
.func = ftrace_stacktrace_callback,
};
+static struct ftrace_func_command ftrace_dump_cmd = {
+ .name = "dump",
+ .func = ftrace_dump_callback,
+};
+
+static struct ftrace_func_command ftrace_cpudump_cmd = {
+ .name = "cpudump",
+ .func = ftrace_cpudump_callback,
+};
+
static int __init init_func_cmd_traceon(void)
{
int ret;
@@ -450,13 +525,31 @@
ret = register_ftrace_command(&ftrace_traceon_cmd);
if (ret)
- unregister_ftrace_command(&ftrace_traceoff_cmd);
+ goto out_free_traceoff;
ret = register_ftrace_command(&ftrace_stacktrace_cmd);
- if (ret) {
- unregister_ftrace_command(&ftrace_traceoff_cmd);
- unregister_ftrace_command(&ftrace_traceon_cmd);
- }
+ if (ret)
+ goto out_free_traceon;
+
+ ret = register_ftrace_command(&ftrace_dump_cmd);
+ if (ret)
+ goto out_free_stacktrace;
+
+ ret = register_ftrace_command(&ftrace_cpudump_cmd);
+ if (ret)
+ goto out_free_dump;
+
+ return 0;
+
+ out_free_dump:
+ unregister_ftrace_command(&ftrace_dump_cmd);
+ out_free_stacktrace:
+ unregister_ftrace_command(&ftrace_stacktrace_cmd);
+ out_free_traceon:
+ unregister_ftrace_command(&ftrace_traceon_cmd);
+ out_free_traceoff:
+ unregister_ftrace_command(&ftrace_traceoff_cmd);
+
return ret;
}
#else
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index b19d065..2aefbee 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -373,7 +373,7 @@
struct trace_array_cpu *data;
unsigned long flags;
- if (likely(!tracer_enabled))
+ if (!tracer_enabled || !tracing_is_enabled())
return;
cpu = raw_smp_processor_id();
@@ -416,7 +416,7 @@
else
return;
- if (!tracer_enabled)
+ if (!tracer_enabled || !tracing_is_enabled())
return;
data = per_cpu_ptr(tr->trace_buffer.data, cpu);
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 9f46e98..7ed6976 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -35,12 +35,17 @@
const char *symbol; /* symbol name */
struct ftrace_event_class class;
struct ftrace_event_call call;
- struct ftrace_event_file * __rcu *files;
+ struct list_head files;
ssize_t size; /* trace entry size */
unsigned int nr_args;
struct probe_arg args[];
};
+struct event_file_link {
+ struct ftrace_event_file *file;
+ struct list_head list;
+};
+
#define SIZEOF_TRACE_PROBE(n) \
(offsetof(struct trace_probe, args) + \
(sizeof(struct probe_arg) * (n)))
@@ -150,6 +155,7 @@
goto error;
INIT_LIST_HEAD(&tp->list);
+ INIT_LIST_HEAD(&tp->files);
return tp;
error:
kfree(tp->call.name);
@@ -183,25 +189,6 @@
return NULL;
}
-static int trace_probe_nr_files(struct trace_probe *tp)
-{
- struct ftrace_event_file **file;
- int ret = 0;
-
- /*
- * Since all tp->files updater is protected by probe_enable_lock,
- * we don't need to lock an rcu_read_lock.
- */
- file = rcu_dereference_raw(tp->files);
- if (file)
- while (*(file++))
- ret++;
-
- return ret;
-}
-
-static DEFINE_MUTEX(probe_enable_lock);
-
/*
* Enable trace_probe
* if the file is NULL, enable "perf" handler, or enable "trace" handler.
@@ -211,67 +198,42 @@
{
int ret = 0;
- mutex_lock(&probe_enable_lock);
-
if (file) {
- struct ftrace_event_file **new, **old;
- int n = trace_probe_nr_files(tp);
+ struct event_file_link *link;
- old = rcu_dereference_raw(tp->files);
- /* 1 is for new one and 1 is for stopper */
- new = kzalloc((n + 2) * sizeof(struct ftrace_event_file *),
- GFP_KERNEL);
- if (!new) {
+ link = kmalloc(sizeof(*link), GFP_KERNEL);
+ if (!link) {
ret = -ENOMEM;
- goto out_unlock;
+ goto out;
}
- memcpy(new, old, n * sizeof(struct ftrace_event_file *));
- new[n] = file;
- /* The last one keeps a NULL */
- rcu_assign_pointer(tp->files, new);
+ link->file = file;
+ list_add_tail_rcu(&link->list, &tp->files);
+
tp->flags |= TP_FLAG_TRACE;
-
- if (old) {
- /* Make sure the probe is done with old files */
- synchronize_sched();
- kfree(old);
- }
} else
tp->flags |= TP_FLAG_PROFILE;
- if (trace_probe_is_enabled(tp) && trace_probe_is_registered(tp) &&
- !trace_probe_has_gone(tp)) {
+ if (trace_probe_is_registered(tp) && !trace_probe_has_gone(tp)) {
if (trace_probe_is_return(tp))
ret = enable_kretprobe(&tp->rp);
else
ret = enable_kprobe(&tp->rp.kp);
}
-
- out_unlock:
- mutex_unlock(&probe_enable_lock);
-
+ out:
return ret;
}
-static int
-trace_probe_file_index(struct trace_probe *tp, struct ftrace_event_file *file)
+static struct event_file_link *
+find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file)
{
- struct ftrace_event_file **files;
- int i;
+ struct event_file_link *link;
- /*
- * Since all tp->files updater is protected by probe_enable_lock,
- * we don't need to lock an rcu_read_lock.
- */
- files = rcu_dereference_raw(tp->files);
- if (files) {
- for (i = 0; files[i]; i++)
- if (files[i] == file)
- return i;
- }
+ list_for_each_entry(link, &tp->files, list)
+ if (link->file == file)
+ return link;
- return -1;
+ return NULL;
}
/*
@@ -283,41 +245,24 @@
{
int ret = 0;
- mutex_lock(&probe_enable_lock);
-
if (file) {
- struct ftrace_event_file **new, **old;
- int n = trace_probe_nr_files(tp);
- int i, j;
+ struct event_file_link *link;
- old = rcu_dereference_raw(tp->files);
- if (n == 0 || trace_probe_file_index(tp, file) < 0) {
+ link = find_event_file_link(tp, file);
+ if (!link) {
ret = -EINVAL;
- goto out_unlock;
+ goto out;
}
- if (n == 1) { /* Remove the last file */
- tp->flags &= ~TP_FLAG_TRACE;
- new = NULL;
- } else {
- new = kzalloc(n * sizeof(struct ftrace_event_file *),
- GFP_KERNEL);
- if (!new) {
- ret = -ENOMEM;
- goto out_unlock;
- }
-
- /* This copy & check loop copies the NULL stopper too */
- for (i = 0, j = 0; j < n && i < n + 1; i++)
- if (old[i] != file)
- new[j++] = old[i];
- }
-
- rcu_assign_pointer(tp->files, new);
-
- /* Make sure the probe is done with old files */
+ list_del_rcu(&link->list);
+ /* synchronize with kprobe_trace_func/kretprobe_trace_func */
synchronize_sched();
- kfree(old);
+ kfree(link);
+
+ if (!list_empty(&tp->files))
+ goto out;
+
+ tp->flags &= ~TP_FLAG_TRACE;
} else
tp->flags &= ~TP_FLAG_PROFILE;
@@ -327,10 +272,7 @@
else
disable_kprobe(&tp->rp.kp);
}
-
- out_unlock:
- mutex_unlock(&probe_enable_lock);
-
+ out:
return ret;
}
@@ -885,20 +827,10 @@
static __kprobes void
kprobe_trace_func(struct trace_probe *tp, struct pt_regs *regs)
{
- /*
- * Note: preempt is already disabled around the kprobe handler.
- * However, we still need an smp_read_barrier_depends() corresponding
- * to smp_wmb() in rcu_assign_pointer() to access the pointer.
- */
- struct ftrace_event_file **file = rcu_dereference_raw(tp->files);
+ struct event_file_link *link;
- if (unlikely(!file))
- return;
-
- while (*file) {
- __kprobe_trace_func(tp, regs, *file);
- file++;
- }
+ list_for_each_entry_rcu(link, &tp->files, list)
+ __kprobe_trace_func(tp, regs, link->file);
}
/* Kretprobe handler */
@@ -945,20 +877,10 @@
kretprobe_trace_func(struct trace_probe *tp, struct kretprobe_instance *ri,
struct pt_regs *regs)
{
- /*
- * Note: preempt is already disabled around the kprobe handler.
- * However, we still need an smp_read_barrier_depends() corresponding
- * to smp_wmb() in rcu_assign_pointer() to access the pointer.
- */
- struct ftrace_event_file **file = rcu_dereference_raw(tp->files);
+ struct event_file_link *link;
- if (unlikely(!file))
- return;
-
- while (*file) {
- __kretprobe_trace_func(tp, ri, regs, *file);
- file++;
- }
+ list_for_each_entry_rcu(link, &tp->files, list)
+ __kretprobe_trace_func(tp, ri, regs, link->file);
}
/* Event entry printers */
@@ -1157,6 +1079,10 @@
int size, __size, dsize;
int rctx;
+ head = this_cpu_ptr(call->perf_events);
+ if (hlist_empty(head))
+ return;
+
dsize = __get_data_size(tp, regs);
__size = sizeof(*entry) + tp->size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
@@ -1172,10 +1098,7 @@
entry->ip = (unsigned long)tp->rp.kp.addr;
memset(&entry[1], 0, dsize);
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
-
- head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx,
- entry->ip, 1, regs, head, NULL);
+ perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
}
/* Kretprobe profile handler */
@@ -1189,6 +1112,10 @@
int size, __size, dsize;
int rctx;
+ head = this_cpu_ptr(call->perf_events);
+ if (hlist_empty(head))
+ return;
+
dsize = __get_data_size(tp, regs);
__size = sizeof(*entry) + tp->size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
@@ -1204,13 +1131,16 @@
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
-
- head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx,
- entry->ret_ip, 1, regs, head, NULL);
+ perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
}
#endif /* CONFIG_PERF_EVENTS */
+/*
+ * called by perf_trace_init() or __ftrace_set_clr_event() under event_mutex.
+ *
+ * kprobe_trace_self_tests_init() does enable_trace_probe/disable_trace_probe
+ * lockless, but we can't race with this __init function.
+ */
static __kprobes
int kprobe_register(struct ftrace_event_call *event,
enum trace_reg type, void *data)
@@ -1376,6 +1306,10 @@
return NULL;
}
+/*
+ * Nobody but us can call enable_trace_probe/disable_trace_probe at this
+ * stage, we can do this lockless.
+ */
static __init int kprobe_trace_self_tests_init(void)
{
int ret, warn = 0;
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index 2901e3b..a7329b7 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -640,13 +640,20 @@
* Enable ftrace, sleep 1/10 second, and then read the trace
* buffer to see if all is in order.
*/
-int
+__init int
trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
{
int save_ftrace_enabled = ftrace_enabled;
unsigned long count;
int ret;
+#ifdef CONFIG_DYNAMIC_FTRACE
+ if (ftrace_filter_param) {
+ printk(KERN_CONT " ... kernel command line filter set: force PASS ... ");
+ return 0;
+ }
+#endif
+
/* make sure msleep has been recorded */
msleep(1);
@@ -727,13 +734,20 @@
* Pretty much the same than for the function tracer from which the selftest
* has been borrowed.
*/
-int
+__init int
trace_selftest_startup_function_graph(struct tracer *trace,
struct trace_array *tr)
{
int ret;
unsigned long count;
+#ifdef CONFIG_DYNAMIC_FTRACE
+ if (ftrace_filter_param) {
+ printk(KERN_CONT " ... kernel command line filter set: force PASS ... ");
+ return 0;
+ }
+#endif
+
/*
* Simulate the init() callback but we attach a watchdog callback
* to detect and recover from possible hangs
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 8f2ac73..322e164 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -306,6 +306,8 @@
struct syscall_metadata *sys_data;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
+ unsigned long irq_flags;
+ int pc;
int syscall_nr;
int size;
@@ -321,9 +323,12 @@
size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
+ local_save_flags(irq_flags);
+ pc = preempt_count();
+
buffer = tr->trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer,
- sys_data->enter_event->event.type, size, 0, 0);
+ sys_data->enter_event->event.type, size, irq_flags, pc);
if (!event)
return;
@@ -333,7 +338,8 @@
if (!filter_current_check_discard(buffer, sys_data->enter_event,
entry, event))
- trace_current_buffer_unlock_commit(buffer, event, 0, 0);
+ trace_current_buffer_unlock_commit(buffer, event,
+ irq_flags, pc);
}
static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
@@ -343,6 +349,8 @@
struct syscall_metadata *sys_data;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
+ unsigned long irq_flags;
+ int pc;
int syscall_nr;
syscall_nr = trace_get_syscall_nr(current, regs);
@@ -355,9 +363,13 @@
if (!sys_data)
return;
+ local_save_flags(irq_flags);
+ pc = preempt_count();
+
buffer = tr->trace_buffer.buffer;
event = trace_buffer_lock_reserve(buffer,
- sys_data->exit_event->event.type, sizeof(*entry), 0, 0);
+ sys_data->exit_event->event.type, sizeof(*entry),
+ irq_flags, pc);
if (!event)
return;
@@ -367,7 +379,8 @@
if (!filter_current_check_discard(buffer, sys_data->exit_event,
entry, event))
- trace_current_buffer_unlock_commit(buffer, event, 0, 0);
+ trace_current_buffer_unlock_commit(buffer, event,
+ irq_flags, pc);
}
static int reg_event_syscall_enter(struct ftrace_event_file *file,
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 32494fb0..d5d0cd3 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -283,8 +283,10 @@
return -EINVAL;
}
arg = strchr(argv[1], ':');
- if (!arg)
+ if (!arg) {
+ ret = -EINVAL;
goto fail_address_parse;
+ }
*arg++ = '\0';
filename = argv[1];
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 05039e3..1241d8c 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -29,9 +29,9 @@
#include <linux/kvm_para.h>
#include <linux/perf_event.h>
-int watchdog_enabled = 1;
+int watchdog_user_enabled = 1;
int __read_mostly watchdog_thresh = 10;
-static int __read_mostly watchdog_disabled;
+static int __read_mostly watchdog_running;
static u64 __read_mostly sample_period;
static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts);
@@ -63,7 +63,7 @@
else if (!strncmp(str, "nopanic", 7))
hardlockup_panic = 0;
else if (!strncmp(str, "0", 1))
- watchdog_enabled = 0;
+ watchdog_user_enabled = 0;
return 1;
}
__setup("nmi_watchdog=", hardlockup_panic_setup);
@@ -82,7 +82,7 @@
static int __init nowatchdog_setup(char *str)
{
- watchdog_enabled = 0;
+ watchdog_user_enabled = 0;
return 1;
}
__setup("nowatchdog", nowatchdog_setup);
@@ -90,7 +90,7 @@
/* deprecated */
static int __init nosoftlockup_setup(char *str)
{
- watchdog_enabled = 0;
+ watchdog_user_enabled = 0;
return 1;
}
__setup("nosoftlockup", nosoftlockup_setup);
@@ -158,7 +158,7 @@
#ifdef CONFIG_HARDLOCKUP_DETECTOR
void touch_nmi_watchdog(void)
{
- if (watchdog_enabled) {
+ if (watchdog_user_enabled) {
unsigned cpu;
for_each_present_cpu(cpu) {
@@ -347,11 +347,6 @@
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer->function = watchdog_timer_fn;
- if (!watchdog_enabled) {
- kthread_park(current);
- return;
- }
-
/* Enable the perf event */
watchdog_nmi_enable(cpu);
@@ -374,6 +369,11 @@
watchdog_nmi_disable(cpu);
}
+static void watchdog_cleanup(unsigned int cpu, bool online)
+{
+ watchdog_disable(cpu);
+}
+
static int watchdog_should_run(unsigned int cpu)
{
return __this_cpu_read(hrtimer_interrupts) !=
@@ -475,28 +475,40 @@
static void watchdog_nmi_disable(unsigned int cpu) { return; }
#endif /* CONFIG_HARDLOCKUP_DETECTOR */
+static struct smp_hotplug_thread watchdog_threads = {
+ .store = &softlockup_watchdog,
+ .thread_should_run = watchdog_should_run,
+ .thread_fn = watchdog,
+ .thread_comm = "watchdog/%u",
+ .setup = watchdog_enable,
+ .cleanup = watchdog_cleanup,
+ .park = watchdog_disable,
+ .unpark = watchdog_enable,
+};
+
+static int watchdog_enable_all_cpus(void)
+{
+ int err = 0;
+
+ if (!watchdog_running) {
+ err = smpboot_register_percpu_thread(&watchdog_threads);
+ if (err)
+ pr_err("Failed to create watchdog threads, disabled\n");
+ else
+ watchdog_running = 1;
+ }
+
+ return err;
+}
+
/* prepare/enable/disable routines */
/* sysctl functions */
#ifdef CONFIG_SYSCTL
-static void watchdog_enable_all_cpus(void)
-{
- unsigned int cpu;
-
- if (watchdog_disabled) {
- watchdog_disabled = 0;
- for_each_online_cpu(cpu)
- kthread_unpark(per_cpu(softlockup_watchdog, cpu));
- }
-}
-
static void watchdog_disable_all_cpus(void)
{
- unsigned int cpu;
-
- if (!watchdog_disabled) {
- watchdog_disabled = 1;
- for_each_online_cpu(cpu)
- kthread_park(per_cpu(softlockup_watchdog, cpu));
+ if (watchdog_running) {
+ watchdog_running = 0;
+ smpboot_unregister_percpu_thread(&watchdog_threads);
}
}
@@ -507,45 +519,48 @@
int proc_dowatchdog(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
- int ret;
+ int err, old_thresh, old_enabled;
- if (watchdog_disabled < 0)
- return -ENODEV;
+ old_thresh = ACCESS_ONCE(watchdog_thresh);
+ old_enabled = ACCESS_ONCE(watchdog_user_enabled);
- ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- if (ret || !write)
- return ret;
+ err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ if (err || !write)
+ return err;
set_sample_period();
/*
* Watchdog threads shouldn't be enabled if they are
- * disabled. The 'watchdog_disabled' variable check in
+ * disabled. The 'watchdog_running' variable check in
* watchdog_*_all_cpus() function takes care of this.
*/
- if (watchdog_enabled && watchdog_thresh)
- watchdog_enable_all_cpus();
+ if (watchdog_user_enabled && watchdog_thresh)
+ err = watchdog_enable_all_cpus();
else
watchdog_disable_all_cpus();
- return ret;
+ /* Restore old values on failure */
+ if (err) {
+ watchdog_thresh = old_thresh;
+ watchdog_user_enabled = old_enabled;
+ }
+
+ return err;
}
#endif /* CONFIG_SYSCTL */
-static struct smp_hotplug_thread watchdog_threads = {
- .store = &softlockup_watchdog,
- .thread_should_run = watchdog_should_run,
- .thread_fn = watchdog,
- .thread_comm = "watchdog/%u",
- .setup = watchdog_enable,
- .park = watchdog_disable,
- .unpark = watchdog_enable,
-};
-
void __init lockup_detector_init(void)
{
set_sample_period();
- if (smpboot_register_percpu_thread(&watchdog_threads)) {
- pr_err("Failed to create watchdog threads, disabled\n");
- watchdog_disabled = -ENODEV;
+
+#ifdef CONFIG_NO_HZ_FULL
+ if (watchdog_user_enabled) {
+ watchdog_user_enabled = 0;
+ pr_warning("Disabled lockup detectors by default for full dynticks\n");
+ pr_warning("You can reactivate it with 'sysctl -w kernel.watchdog=1'\n");
}
+#endif
+
+ if (watchdog_user_enabled)
+ watchdog_enable_all_cpus();
}
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 88c8d98..98ac17e 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1347,7 +1347,7 @@
depends on FAULT_INJECTION_DEBUG_FS && STACKTRACE_SUPPORT
depends on !X86_64
select STACKTRACE
- select FRAME_POINTER if !PPC && !S390 && !MICROBLAZE && !ARM_UNWIND
+ select FRAME_POINTER if !MIPS && !PPC && !S390 && !MICROBLAZE && !ARM_UNWIND
help
Provide stacktrace filter for fault-injection capabilities
diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c
index aad024d..6dc09d8 100644
--- a/lib/locking-selftest.c
+++ b/lib/locking-selftest.c
@@ -12,6 +12,7 @@
*/
#include <linux/rwsem.h>
#include <linux/mutex.h>
+#include <linux/ww_mutex.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/lockdep.h>
diff --git a/mm/Kconfig b/mm/Kconfig
index 7e28ecf..8028dcc 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -478,6 +478,36 @@
If unsure, say Y to enable frontswap.
+config ZBUD
+ tristate
+ default n
+ help
+ A special purpose allocator for storing compressed pages.
+ It is designed to store up to two compressed pages per physical
+ page. While this design limits storage density, it has simple and
+ deterministic reclaim properties that make it preferable to a higher
+ density approach when reclaim will be used.
+
+config ZSWAP
+ bool "Compressed cache for swap pages (EXPERIMENTAL)"
+ depends on FRONTSWAP && CRYPTO=y
+ select CRYPTO_LZO
+ select ZBUD
+ default n
+ help
+ A lightweight compressed cache for swap pages. It takes
+ pages that are in the process of being swapped out and attempts to
+ compress them into a dynamically allocated RAM-based memory pool.
+ This can result in a significant I/O reduction on swap device and,
+ in the case where decompressing from RAM is faster that swap device
+ reads, can also improve workload performance.
+
+ This is marked experimental because it is a new feature (as of
+ v3.11) that interacts heavily with memory reclaim. While these
+ interactions don't cause any known issues on simple memory setups,
+ they have not be fully explored on the large set of potential
+ configurations and workloads that exist.
+
config MEM_SOFT_DIRTY
bool "Track memory changes"
depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY
diff --git a/mm/Makefile b/mm/Makefile
index 72c5acb..f008033 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -32,6 +32,7 @@
obj-$(CONFIG_BOUNCE) += bounce.o
obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o
obj-$(CONFIG_FRONTSWAP) += frontswap.o
+obj-$(CONFIG_ZSWAP) += zswap.o
obj-$(CONFIG_HAS_DMA) += dmapool.o
obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o
@@ -58,3 +59,4 @@
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
obj-$(CONFIG_CLEANCACHE) += cleancache.o
obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
+obj-$(CONFIG_ZBUD) += zbud.o
diff --git a/mm/mmap.c b/mm/mmap.c
index f813111..fbad7b0 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1878,15 +1878,6 @@
}
#endif
-void arch_unmap_area(struct mm_struct *mm, unsigned long addr)
-{
- /*
- * Is this a new hole at the lowest possible address?
- */
- if (addr >= TASK_UNMAPPED_BASE && addr < mm->free_area_cache)
- mm->free_area_cache = addr;
-}
-
/*
* This mmap-allocator allocates new areas top-down from below the
* stack's low limit (the base):
@@ -1943,19 +1934,6 @@
}
#endif
-void arch_unmap_area_topdown(struct mm_struct *mm, unsigned long addr)
-{
- /*
- * Is this a new hole at the highest possible address?
- */
- if (addr > mm->free_area_cache)
- mm->free_area_cache = addr;
-
- /* dont allow allocations above current base */
- if (mm->free_area_cache > mm->mmap_base)
- mm->free_area_cache = mm->mmap_base;
-}
-
unsigned long
get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
@@ -2376,7 +2354,6 @@
{
struct vm_area_struct **insertion_point;
struct vm_area_struct *tail_vma = NULL;
- unsigned long addr;
insertion_point = (prev ? &prev->vm_next : &mm->mmap);
vma->vm_prev = NULL;
@@ -2393,11 +2370,6 @@
} else
mm->highest_vm_end = prev ? prev->vm_end : 0;
tail_vma->vm_next = NULL;
- if (mm->unmap_area == arch_unmap_area)
- addr = prev ? prev->vm_end : mm->mmap_base;
- else
- addr = vma ? vma->vm_start : mm->mmap_base;
- mm->unmap_area(mm, addr);
mm->mmap_cache = NULL; /* Kill the cache. */
}
diff --git a/mm/nommu.c b/mm/nommu.c
index e44e6e0..ecd1f15 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1871,10 +1871,6 @@
return -ENOMEM;
}
-void arch_unmap_area(struct mm_struct *mm, unsigned long addr)
-{
-}
-
void unmap_mapping_range(struct address_space *mapping,
loff_t const holebegin, loff_t const holelen,
int even_cows)
diff --git a/mm/util.c b/mm/util.c
index ab1424d..7441c41 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -295,7 +295,6 @@
{
mm->mmap_base = TASK_UNMAPPED_BASE;
mm->get_unmapped_area = arch_get_unmapped_area;
- mm->unmap_area = arch_unmap_area;
}
#endif
diff --git a/mm/zbud.c b/mm/zbud.c
new file mode 100644
index 0000000..9bb4710
--- /dev/null
+++ b/mm/zbud.c
@@ -0,0 +1,527 @@
+/*
+ * zbud.c
+ *
+ * Copyright (C) 2013, Seth Jennings, IBM
+ *
+ * Concepts based on zcache internal zbud allocator by Dan Magenheimer.
+ *
+ * zbud is an special purpose allocator for storing compressed pages. Contrary
+ * to what its name may suggest, zbud is not a buddy allocator, but rather an
+ * allocator that "buddies" two compressed pages together in a single memory
+ * page.
+ *
+ * While this design limits storage density, it has simple and deterministic
+ * reclaim properties that make it preferable to a higher density approach when
+ * reclaim will be used.
+ *
+ * zbud works by storing compressed pages, or "zpages", together in pairs in a
+ * single memory page called a "zbud page". The first buddy is "left
+ * justifed" at the beginning of the zbud page, and the last buddy is "right
+ * justified" at the end of the zbud page. The benefit is that if either
+ * buddy is freed, the freed buddy space, coalesced with whatever slack space
+ * that existed between the buddies, results in the largest possible free region
+ * within the zbud page.
+ *
+ * zbud also provides an attractive lower bound on density. The ratio of zpages
+ * to zbud pages can not be less than 1. This ensures that zbud can never "do
+ * harm" by using more pages to store zpages than the uncompressed zpages would
+ * have used on their own.
+ *
+ * zbud pages are divided into "chunks". The size of the chunks is fixed at
+ * compile time and determined by NCHUNKS_ORDER below. Dividing zbud pages
+ * into chunks allows organizing unbuddied zbud pages into a manageable number
+ * of unbuddied lists according to the number of free chunks available in the
+ * zbud page.
+ *
+ * The zbud API differs from that of conventional allocators in that the
+ * allocation function, zbud_alloc(), returns an opaque handle to the user,
+ * not a dereferenceable pointer. The user must map the handle using
+ * zbud_map() in order to get a usable pointer by which to access the
+ * allocation data and unmap the handle with zbud_unmap() when operations
+ * on the allocation data are complete.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/zbud.h>
+
+/*****************
+ * Structures
+*****************/
+/*
+ * NCHUNKS_ORDER determines the internal allocation granularity, effectively
+ * adjusting internal fragmentation. It also determines the number of
+ * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
+ * allocation granularity will be in chunks of size PAGE_SIZE/64, and there
+ * will be 64 freelists per pool.
+ */
+#define NCHUNKS_ORDER 6
+
+#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER)
+#define CHUNK_SIZE (1 << CHUNK_SHIFT)
+#define NCHUNKS (PAGE_SIZE >> CHUNK_SHIFT)
+#define ZHDR_SIZE_ALIGNED CHUNK_SIZE
+
+/**
+ * struct zbud_pool - stores metadata for each zbud pool
+ * @lock: protects all pool fields and first|last_chunk fields of any
+ * zbud page in the pool
+ * @unbuddied: array of lists tracking zbud pages that only contain one buddy;
+ * the lists each zbud page is added to depends on the size of
+ * its free region.
+ * @buddied: list tracking the zbud pages that contain two buddies;
+ * these zbud pages are full
+ * @lru: list tracking the zbud pages in LRU order by most recently
+ * added buddy.
+ * @pages_nr: number of zbud pages in the pool.
+ * @ops: pointer to a structure of user defined operations specified at
+ * pool creation time.
+ *
+ * This structure is allocated at pool creation time and maintains metadata
+ * pertaining to a particular zbud pool.
+ */
+struct zbud_pool {
+ spinlock_t lock;
+ struct list_head unbuddied[NCHUNKS];
+ struct list_head buddied;
+ struct list_head lru;
+ u64 pages_nr;
+ struct zbud_ops *ops;
+};
+
+/*
+ * struct zbud_header - zbud page metadata occupying the first chunk of each
+ * zbud page.
+ * @buddy: links the zbud page into the unbuddied/buddied lists in the pool
+ * @lru: links the zbud page into the lru list in the pool
+ * @first_chunks: the size of the first buddy in chunks, 0 if free
+ * @last_chunks: the size of the last buddy in chunks, 0 if free
+ */
+struct zbud_header {
+ struct list_head buddy;
+ struct list_head lru;
+ unsigned int first_chunks;
+ unsigned int last_chunks;
+ bool under_reclaim;
+};
+
+/*****************
+ * Helpers
+*****************/
+/* Just to make the code easier to read */
+enum buddy {
+ FIRST,
+ LAST
+};
+
+/* Converts an allocation size in bytes to size in zbud chunks */
+static int size_to_chunks(int size)
+{
+ return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
+}
+
+#define for_each_unbuddied_list(_iter, _begin) \
+ for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
+
+/* Initializes the zbud header of a newly allocated zbud page */
+static struct zbud_header *init_zbud_page(struct page *page)
+{
+ struct zbud_header *zhdr = page_address(page);
+ zhdr->first_chunks = 0;
+ zhdr->last_chunks = 0;
+ INIT_LIST_HEAD(&zhdr->buddy);
+ INIT_LIST_HEAD(&zhdr->lru);
+ zhdr->under_reclaim = 0;
+ return zhdr;
+}
+
+/* Resets the struct page fields and frees the page */
+static void free_zbud_page(struct zbud_header *zhdr)
+{
+ __free_page(virt_to_page(zhdr));
+}
+
+/*
+ * Encodes the handle of a particular buddy within a zbud page
+ * Pool lock should be held as this function accesses first|last_chunks
+ */
+static unsigned long encode_handle(struct zbud_header *zhdr, enum buddy bud)
+{
+ unsigned long handle;
+
+ /*
+ * For now, the encoded handle is actually just the pointer to the data
+ * but this might not always be the case. A little information hiding.
+ * Add CHUNK_SIZE to the handle if it is the first allocation to jump
+ * over the zbud header in the first chunk.
+ */
+ handle = (unsigned long)zhdr;
+ if (bud == FIRST)
+ /* skip over zbud header */
+ handle += ZHDR_SIZE_ALIGNED;
+ else /* bud == LAST */
+ handle += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT);
+ return handle;
+}
+
+/* Returns the zbud page where a given handle is stored */
+static struct zbud_header *handle_to_zbud_header(unsigned long handle)
+{
+ return (struct zbud_header *)(handle & PAGE_MASK);
+}
+
+/* Returns the number of free chunks in a zbud page */
+static int num_free_chunks(struct zbud_header *zhdr)
+{
+ /*
+ * Rather than branch for different situations, just use the fact that
+ * free buddies have a length of zero to simplify everything. -1 at the
+ * end for the zbud header.
+ */
+ return NCHUNKS - zhdr->first_chunks - zhdr->last_chunks - 1;
+}
+
+/*****************
+ * API Functions
+*****************/
+/**
+ * zbud_create_pool() - create a new zbud pool
+ * @gfp: gfp flags when allocating the zbud pool structure
+ * @ops: user-defined operations for the zbud pool
+ *
+ * Return: pointer to the new zbud pool or NULL if the metadata allocation
+ * failed.
+ */
+struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
+{
+ struct zbud_pool *pool;
+ int i;
+
+ pool = kmalloc(sizeof(struct zbud_pool), gfp);
+ if (!pool)
+ return NULL;
+ spin_lock_init(&pool->lock);
+ for_each_unbuddied_list(i, 0)
+ INIT_LIST_HEAD(&pool->unbuddied[i]);
+ INIT_LIST_HEAD(&pool->buddied);
+ INIT_LIST_HEAD(&pool->lru);
+ pool->pages_nr = 0;
+ pool->ops = ops;
+ return pool;
+}
+
+/**
+ * zbud_destroy_pool() - destroys an existing zbud pool
+ * @pool: the zbud pool to be destroyed
+ *
+ * The pool should be emptied before this function is called.
+ */
+void zbud_destroy_pool(struct zbud_pool *pool)
+{
+ kfree(pool);
+}
+
+/**
+ * zbud_alloc() - allocates a region of a given size
+ * @pool: zbud pool from which to allocate
+ * @size: size in bytes of the desired allocation
+ * @gfp: gfp flags used if the pool needs to grow
+ * @handle: handle of the new allocation
+ *
+ * This function will attempt to find a free region in the pool large enough to
+ * satisfy the allocation request. A search of the unbuddied lists is
+ * performed first. If no suitable free region is found, then a new page is
+ * allocated and added to the pool to satisfy the request.
+ *
+ * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
+ * as zbud pool pages.
+ *
+ * Return: 0 if success and handle is set, otherwise -EINVAL is the size or
+ * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
+ * a new page.
+ */
+int zbud_alloc(struct zbud_pool *pool, int size, gfp_t gfp,
+ unsigned long *handle)
+{
+ int chunks, i, freechunks;
+ struct zbud_header *zhdr = NULL;
+ enum buddy bud;
+ struct page *page;
+
+ if (size <= 0 || gfp & __GFP_HIGHMEM)
+ return -EINVAL;
+ if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED)
+ return -ENOSPC;
+ chunks = size_to_chunks(size);
+ spin_lock(&pool->lock);
+
+ /* First, try to find an unbuddied zbud page. */
+ zhdr = NULL;
+ for_each_unbuddied_list(i, chunks) {
+ if (!list_empty(&pool->unbuddied[i])) {
+ zhdr = list_first_entry(&pool->unbuddied[i],
+ struct zbud_header, buddy);
+ list_del(&zhdr->buddy);
+ if (zhdr->first_chunks == 0)
+ bud = FIRST;
+ else
+ bud = LAST;
+ goto found;
+ }
+ }
+
+ /* Couldn't find unbuddied zbud page, create new one */
+ spin_unlock(&pool->lock);
+ page = alloc_page(gfp);
+ if (!page)
+ return -ENOMEM;
+ spin_lock(&pool->lock);
+ pool->pages_nr++;
+ zhdr = init_zbud_page(page);
+ bud = FIRST;
+
+found:
+ if (bud == FIRST)
+ zhdr->first_chunks = chunks;
+ else
+ zhdr->last_chunks = chunks;
+
+ if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0) {
+ /* Add to unbuddied list */
+ freechunks = num_free_chunks(zhdr);
+ list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
+ } else {
+ /* Add to buddied list */
+ list_add(&zhdr->buddy, &pool->buddied);
+ }
+
+ /* Add/move zbud page to beginning of LRU */
+ if (!list_empty(&zhdr->lru))
+ list_del(&zhdr->lru);
+ list_add(&zhdr->lru, &pool->lru);
+
+ *handle = encode_handle(zhdr, bud);
+ spin_unlock(&pool->lock);
+
+ return 0;
+}
+
+/**
+ * zbud_free() - frees the allocation associated with the given handle
+ * @pool: pool in which the allocation resided
+ * @handle: handle associated with the allocation returned by zbud_alloc()
+ *
+ * In the case that the zbud page in which the allocation resides is under
+ * reclaim, as indicated by the PG_reclaim flag being set, this function
+ * only sets the first|last_chunks to 0. The page is actually freed
+ * once both buddies are evicted (see zbud_reclaim_page() below).
+ */
+void zbud_free(struct zbud_pool *pool, unsigned long handle)
+{
+ struct zbud_header *zhdr;
+ int freechunks;
+
+ spin_lock(&pool->lock);
+ zhdr = handle_to_zbud_header(handle);
+
+ /* If first buddy, handle will be page aligned */
+ if ((handle - ZHDR_SIZE_ALIGNED) & ~PAGE_MASK)
+ zhdr->last_chunks = 0;
+ else
+ zhdr->first_chunks = 0;
+
+ if (zhdr->under_reclaim) {
+ /* zbud page is under reclaim, reclaim will free */
+ spin_unlock(&pool->lock);
+ return;
+ }
+
+ /* Remove from existing buddy list */
+ list_del(&zhdr->buddy);
+
+ if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
+ /* zbud page is empty, free */
+ list_del(&zhdr->lru);
+ free_zbud_page(zhdr);
+ pool->pages_nr--;
+ } else {
+ /* Add to unbuddied list */
+ freechunks = num_free_chunks(zhdr);
+ list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
+ }
+
+ spin_unlock(&pool->lock);
+}
+
+#define list_tail_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+
+/**
+ * zbud_reclaim_page() - evicts allocations from a pool page and frees it
+ * @pool: pool from which a page will attempt to be evicted
+ * @retires: number of pages on the LRU list for which eviction will
+ * be attempted before failing
+ *
+ * zbud reclaim is different from normal system reclaim in that the reclaim is
+ * done from the bottom, up. This is because only the bottom layer, zbud, has
+ * information on how the allocations are organized within each zbud page. This
+ * has the potential to create interesting locking situations between zbud and
+ * the user, however.
+ *
+ * To avoid these, this is how zbud_reclaim_page() should be called:
+
+ * The user detects a page should be reclaimed and calls zbud_reclaim_page().
+ * zbud_reclaim_page() will remove a zbud page from the pool LRU list and call
+ * the user-defined eviction handler with the pool and handle as arguments.
+ *
+ * If the handle can not be evicted, the eviction handler should return
+ * non-zero. zbud_reclaim_page() will add the zbud page back to the
+ * appropriate list and try the next zbud page on the LRU up to
+ * a user defined number of retries.
+ *
+ * If the handle is successfully evicted, the eviction handler should
+ * return 0 _and_ should have called zbud_free() on the handle. zbud_free()
+ * contains logic to delay freeing the page if the page is under reclaim,
+ * as indicated by the setting of the PG_reclaim flag on the underlying page.
+ *
+ * If all buddies in the zbud page are successfully evicted, then the
+ * zbud page can be freed.
+ *
+ * Returns: 0 if page is successfully freed, otherwise -EINVAL if there are
+ * no pages to evict or an eviction handler is not registered, -EAGAIN if
+ * the retry limit was hit.
+ */
+int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
+{
+ int i, ret, freechunks;
+ struct zbud_header *zhdr;
+ unsigned long first_handle = 0, last_handle = 0;
+
+ spin_lock(&pool->lock);
+ if (!pool->ops || !pool->ops->evict || list_empty(&pool->lru) ||
+ retries == 0) {
+ spin_unlock(&pool->lock);
+ return -EINVAL;
+ }
+ for (i = 0; i < retries; i++) {
+ zhdr = list_tail_entry(&pool->lru, struct zbud_header, lru);
+ list_del(&zhdr->lru);
+ list_del(&zhdr->buddy);
+ /* Protect zbud page against free */
+ zhdr->under_reclaim = true;
+ /*
+ * We need encode the handles before unlocking, since we can
+ * race with free that will set (first|last)_chunks to 0
+ */
+ first_handle = 0;
+ last_handle = 0;
+ if (zhdr->first_chunks)
+ first_handle = encode_handle(zhdr, FIRST);
+ if (zhdr->last_chunks)
+ last_handle = encode_handle(zhdr, LAST);
+ spin_unlock(&pool->lock);
+
+ /* Issue the eviction callback(s) */
+ if (first_handle) {
+ ret = pool->ops->evict(pool, first_handle);
+ if (ret)
+ goto next;
+ }
+ if (last_handle) {
+ ret = pool->ops->evict(pool, last_handle);
+ if (ret)
+ goto next;
+ }
+next:
+ spin_lock(&pool->lock);
+ zhdr->under_reclaim = false;
+ if (zhdr->first_chunks == 0 && zhdr->last_chunks == 0) {
+ /*
+ * Both buddies are now free, free the zbud page and
+ * return success.
+ */
+ free_zbud_page(zhdr);
+ pool->pages_nr--;
+ spin_unlock(&pool->lock);
+ return 0;
+ } else if (zhdr->first_chunks == 0 ||
+ zhdr->last_chunks == 0) {
+ /* add to unbuddied list */
+ freechunks = num_free_chunks(zhdr);
+ list_add(&zhdr->buddy, &pool->unbuddied[freechunks]);
+ } else {
+ /* add to buddied list */
+ list_add(&zhdr->buddy, &pool->buddied);
+ }
+
+ /* add to beginning of LRU */
+ list_add(&zhdr->lru, &pool->lru);
+ }
+ spin_unlock(&pool->lock);
+ return -EAGAIN;
+}
+
+/**
+ * zbud_map() - maps the allocation associated with the given handle
+ * @pool: pool in which the allocation resides
+ * @handle: handle associated with the allocation to be mapped
+ *
+ * While trivial for zbud, the mapping functions for others allocators
+ * implementing this allocation API could have more complex information encoded
+ * in the handle and could create temporary mappings to make the data
+ * accessible to the user.
+ *
+ * Returns: a pointer to the mapped allocation
+ */
+void *zbud_map(struct zbud_pool *pool, unsigned long handle)
+{
+ return (void *)(handle);
+}
+
+/**
+ * zbud_unmap() - maps the allocation associated with the given handle
+ * @pool: pool in which the allocation resides
+ * @handle: handle associated with the allocation to be unmapped
+ */
+void zbud_unmap(struct zbud_pool *pool, unsigned long handle)
+{
+}
+
+/**
+ * zbud_get_pool_size() - gets the zbud pool size in pages
+ * @pool: pool whose size is being queried
+ *
+ * Returns: size in pages of the given pool. The pool lock need not be
+ * taken to access pages_nr.
+ */
+u64 zbud_get_pool_size(struct zbud_pool *pool)
+{
+ return pool->pages_nr;
+}
+
+static int __init init_zbud(void)
+{
+ /* Make sure the zbud header will fit in one chunk */
+ BUILD_BUG_ON(sizeof(struct zbud_header) > ZHDR_SIZE_ALIGNED);
+ pr_info("loaded\n");
+ return 0;
+}
+
+static void __exit exit_zbud(void)
+{
+ pr_info("unloaded\n");
+}
+
+module_init(init_zbud);
+module_exit(exit_zbud);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Seth Jennings <sjenning@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("Buddy Allocator for Compressed Pages");
diff --git a/mm/zswap.c b/mm/zswap.c
new file mode 100644
index 0000000..deda2b6
--- /dev/null
+++ b/mm/zswap.c
@@ -0,0 +1,943 @@
+/*
+ * zswap.c - zswap driver file
+ *
+ * zswap is a backend for frontswap that takes pages that are in the process
+ * of being swapped out and attempts to compress and store them in a
+ * RAM-based memory pool. This can result in a significant I/O reduction on
+ * the swap device and, in the case where decompressing from RAM is faster
+ * than reading from the swap device, can also improve workload performance.
+ *
+ * Copyright (C) 2012 Seth Jennings <sjenning@linux.vnet.ibm.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.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/frontswap.h>
+#include <linux/rbtree.h>
+#include <linux/swap.h>
+#include <linux/crypto.h>
+#include <linux/mempool.h>
+#include <linux/zbud.h>
+
+#include <linux/mm_types.h>
+#include <linux/page-flags.h>
+#include <linux/swapops.h>
+#include <linux/writeback.h>
+#include <linux/pagemap.h>
+
+/*********************************
+* statistics
+**********************************/
+/* Number of memory pages used by the compressed pool */
+static u64 zswap_pool_pages;
+/* The number of compressed pages currently stored in zswap */
+static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
+
+/*
+ * The statistics below are not protected from concurrent access for
+ * performance reasons so they may not be a 100% accurate. However,
+ * they do provide useful information on roughly how many times a
+ * certain event is occurring.
+*/
+
+/* Pool limit was hit (see zswap_max_pool_percent) */
+static u64 zswap_pool_limit_hit;
+/* Pages written back when pool limit was reached */
+static u64 zswap_written_back_pages;
+/* Store failed due to a reclaim failure after pool limit was reached */
+static u64 zswap_reject_reclaim_fail;
+/* Compressed page was too big for the allocator to (optimally) store */
+static u64 zswap_reject_compress_poor;
+/* Store failed because underlying allocator could not get memory */
+static u64 zswap_reject_alloc_fail;
+/* Store failed because the entry metadata could not be allocated (rare) */
+static u64 zswap_reject_kmemcache_fail;
+/* Duplicate store was encountered (rare) */
+static u64 zswap_duplicate_entry;
+
+/*********************************
+* tunables
+**********************************/
+/* Enable/disable zswap (disabled by default, fixed at boot for now) */
+static bool zswap_enabled __read_mostly;
+module_param_named(enabled, zswap_enabled, bool, 0);
+
+/* Compressor to be used by zswap (fixed at boot for now) */
+#define ZSWAP_COMPRESSOR_DEFAULT "lzo"
+static char *zswap_compressor = ZSWAP_COMPRESSOR_DEFAULT;
+module_param_named(compressor, zswap_compressor, charp, 0);
+
+/* The maximum percentage of memory that the compressed pool can occupy */
+static unsigned int zswap_max_pool_percent = 20;
+module_param_named(max_pool_percent,
+ zswap_max_pool_percent, uint, 0644);
+
+/*********************************
+* compression functions
+**********************************/
+/* per-cpu compression transforms */
+static struct crypto_comp * __percpu *zswap_comp_pcpu_tfms;
+
+enum comp_op {
+ ZSWAP_COMPOP_COMPRESS,
+ ZSWAP_COMPOP_DECOMPRESS
+};
+
+static int zswap_comp_op(enum comp_op op, const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
+{
+ struct crypto_comp *tfm;
+ int ret;
+
+ tfm = *per_cpu_ptr(zswap_comp_pcpu_tfms, get_cpu());
+ switch (op) {
+ case ZSWAP_COMPOP_COMPRESS:
+ ret = crypto_comp_compress(tfm, src, slen, dst, dlen);
+ break;
+ case ZSWAP_COMPOP_DECOMPRESS:
+ ret = crypto_comp_decompress(tfm, src, slen, dst, dlen);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ put_cpu();
+ return ret;
+}
+
+static int __init zswap_comp_init(void)
+{
+ if (!crypto_has_comp(zswap_compressor, 0, 0)) {
+ pr_info("%s compressor not available\n", zswap_compressor);
+ /* fall back to default compressor */
+ zswap_compressor = ZSWAP_COMPRESSOR_DEFAULT;
+ if (!crypto_has_comp(zswap_compressor, 0, 0))
+ /* can't even load the default compressor */
+ return -ENODEV;
+ }
+ pr_info("using %s compressor\n", zswap_compressor);
+
+ /* alloc percpu transforms */
+ zswap_comp_pcpu_tfms = alloc_percpu(struct crypto_comp *);
+ if (!zswap_comp_pcpu_tfms)
+ return -ENOMEM;
+ return 0;
+}
+
+static void zswap_comp_exit(void)
+{
+ /* free percpu transforms */
+ if (zswap_comp_pcpu_tfms)
+ free_percpu(zswap_comp_pcpu_tfms);
+}
+
+/*********************************
+* data structures
+**********************************/
+/*
+ * struct zswap_entry
+ *
+ * This structure contains the metadata for tracking a single compressed
+ * page within zswap.
+ *
+ * rbnode - links the entry into red-black tree for the appropriate swap type
+ * refcount - the number of outstanding reference to the entry. This is needed
+ * to protect against premature freeing of the entry by code
+ * concurent calls to load, invalidate, and writeback. The lock
+ * for the zswap_tree structure that contains the entry must
+ * be held while changing the refcount. Since the lock must
+ * be held, there is no reason to also make refcount atomic.
+ * offset - the swap offset for the entry. Index into the red-black tree.
+ * handle - zsmalloc allocation handle that stores the compressed page data
+ * length - the length in bytes of the compressed page data. Needed during
+ * decompression
+ */
+struct zswap_entry {
+ struct rb_node rbnode;
+ pgoff_t offset;
+ int refcount;
+ unsigned int length;
+ unsigned long handle;
+};
+
+struct zswap_header {
+ swp_entry_t swpentry;
+};
+
+/*
+ * The tree lock in the zswap_tree struct protects a few things:
+ * - the rbtree
+ * - the refcount field of each entry in the tree
+ */
+struct zswap_tree {
+ struct rb_root rbroot;
+ spinlock_t lock;
+ struct zbud_pool *pool;
+};
+
+static struct zswap_tree *zswap_trees[MAX_SWAPFILES];
+
+/*********************************
+* zswap entry functions
+**********************************/
+static struct kmem_cache *zswap_entry_cache;
+
+static int zswap_entry_cache_create(void)
+{
+ zswap_entry_cache = KMEM_CACHE(zswap_entry, 0);
+ return (zswap_entry_cache == NULL);
+}
+
+static void zswap_entry_cache_destory(void)
+{
+ kmem_cache_destroy(zswap_entry_cache);
+}
+
+static struct zswap_entry *zswap_entry_cache_alloc(gfp_t gfp)
+{
+ struct zswap_entry *entry;
+ entry = kmem_cache_alloc(zswap_entry_cache, gfp);
+ if (!entry)
+ return NULL;
+ entry->refcount = 1;
+ return entry;
+}
+
+static void zswap_entry_cache_free(struct zswap_entry *entry)
+{
+ kmem_cache_free(zswap_entry_cache, entry);
+}
+
+/* caller must hold the tree lock */
+static void zswap_entry_get(struct zswap_entry *entry)
+{
+ entry->refcount++;
+}
+
+/* caller must hold the tree lock */
+static int zswap_entry_put(struct zswap_entry *entry)
+{
+ entry->refcount--;
+ return entry->refcount;
+}
+
+/*********************************
+* rbtree functions
+**********************************/
+static struct zswap_entry *zswap_rb_search(struct rb_root *root, pgoff_t offset)
+{
+ struct rb_node *node = root->rb_node;
+ struct zswap_entry *entry;
+
+ while (node) {
+ entry = rb_entry(node, struct zswap_entry, rbnode);
+ if (entry->offset > offset)
+ node = node->rb_left;
+ else if (entry->offset < offset)
+ node = node->rb_right;
+ else
+ return entry;
+ }
+ return NULL;
+}
+
+/*
+ * In the case that a entry with the same offset is found, a pointer to
+ * the existing entry is stored in dupentry and the function returns -EEXIST
+ */
+static int zswap_rb_insert(struct rb_root *root, struct zswap_entry *entry,
+ struct zswap_entry **dupentry)
+{
+ struct rb_node **link = &root->rb_node, *parent = NULL;
+ struct zswap_entry *myentry;
+
+ while (*link) {
+ parent = *link;
+ myentry = rb_entry(parent, struct zswap_entry, rbnode);
+ if (myentry->offset > entry->offset)
+ link = &(*link)->rb_left;
+ else if (myentry->offset < entry->offset)
+ link = &(*link)->rb_right;
+ else {
+ *dupentry = myentry;
+ return -EEXIST;
+ }
+ }
+ rb_link_node(&entry->rbnode, parent, link);
+ rb_insert_color(&entry->rbnode, root);
+ return 0;
+}
+
+/*********************************
+* per-cpu code
+**********************************/
+static DEFINE_PER_CPU(u8 *, zswap_dstmem);
+
+static int __zswap_cpu_notifier(unsigned long action, unsigned long cpu)
+{
+ struct crypto_comp *tfm;
+ u8 *dst;
+
+ switch (action) {
+ case CPU_UP_PREPARE:
+ tfm = crypto_alloc_comp(zswap_compressor, 0, 0);
+ if (IS_ERR(tfm)) {
+ pr_err("can't allocate compressor transform\n");
+ return NOTIFY_BAD;
+ }
+ *per_cpu_ptr(zswap_comp_pcpu_tfms, cpu) = tfm;
+ dst = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
+ if (!dst) {
+ pr_err("can't allocate compressor buffer\n");
+ crypto_free_comp(tfm);
+ *per_cpu_ptr(zswap_comp_pcpu_tfms, cpu) = NULL;
+ return NOTIFY_BAD;
+ }
+ per_cpu(zswap_dstmem, cpu) = dst;
+ break;
+ case CPU_DEAD:
+ case CPU_UP_CANCELED:
+ tfm = *per_cpu_ptr(zswap_comp_pcpu_tfms, cpu);
+ if (tfm) {
+ crypto_free_comp(tfm);
+ *per_cpu_ptr(zswap_comp_pcpu_tfms, cpu) = NULL;
+ }
+ dst = per_cpu(zswap_dstmem, cpu);
+ kfree(dst);
+ per_cpu(zswap_dstmem, cpu) = NULL;
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static int zswap_cpu_notifier(struct notifier_block *nb,
+ unsigned long action, void *pcpu)
+{
+ unsigned long cpu = (unsigned long)pcpu;
+ return __zswap_cpu_notifier(action, cpu);
+}
+
+static struct notifier_block zswap_cpu_notifier_block = {
+ .notifier_call = zswap_cpu_notifier
+};
+
+static int zswap_cpu_init(void)
+{
+ unsigned long cpu;
+
+ get_online_cpus();
+ for_each_online_cpu(cpu)
+ if (__zswap_cpu_notifier(CPU_UP_PREPARE, cpu) != NOTIFY_OK)
+ goto cleanup;
+ register_cpu_notifier(&zswap_cpu_notifier_block);
+ put_online_cpus();
+ return 0;
+
+cleanup:
+ for_each_online_cpu(cpu)
+ __zswap_cpu_notifier(CPU_UP_CANCELED, cpu);
+ put_online_cpus();
+ return -ENOMEM;
+}
+
+/*********************************
+* helpers
+**********************************/
+static bool zswap_is_full(void)
+{
+ return (totalram_pages * zswap_max_pool_percent / 100 <
+ zswap_pool_pages);
+}
+
+/*
+ * Carries out the common pattern of freeing and entry's zsmalloc allocation,
+ * freeing the entry itself, and decrementing the number of stored pages.
+ */
+static void zswap_free_entry(struct zswap_tree *tree, struct zswap_entry *entry)
+{
+ zbud_free(tree->pool, entry->handle);
+ zswap_entry_cache_free(entry);
+ atomic_dec(&zswap_stored_pages);
+ zswap_pool_pages = zbud_get_pool_size(tree->pool);
+}
+
+/*********************************
+* writeback code
+**********************************/
+/* return enum for zswap_get_swap_cache_page */
+enum zswap_get_swap_ret {
+ ZSWAP_SWAPCACHE_NEW,
+ ZSWAP_SWAPCACHE_EXIST,
+ ZSWAP_SWAPCACHE_NOMEM
+};
+
+/*
+ * zswap_get_swap_cache_page
+ *
+ * This is an adaption of read_swap_cache_async()
+ *
+ * This function tries to find a page with the given swap entry
+ * in the swapper_space address space (the swap cache). If the page
+ * is found, it is returned in retpage. Otherwise, a page is allocated,
+ * added to the swap cache, and returned in retpage.
+ *
+ * If success, the swap cache page is returned in retpage
+ * Returns 0 if page was already in the swap cache, page is not locked
+ * Returns 1 if the new page needs to be populated, page is locked
+ * Returns <0 on error
+ */
+static int zswap_get_swap_cache_page(swp_entry_t entry,
+ struct page **retpage)
+{
+ struct page *found_page, *new_page = NULL;
+ struct address_space *swapper_space = &swapper_spaces[swp_type(entry)];
+ int err;
+
+ *retpage = NULL;
+ do {
+ /*
+ * First check the swap cache. Since this is normally
+ * called after lookup_swap_cache() failed, re-calling
+ * that would confuse statistics.
+ */
+ found_page = find_get_page(swapper_space, entry.val);
+ if (found_page)
+ break;
+
+ /*
+ * Get a new page to read into from swap.
+ */
+ if (!new_page) {
+ new_page = alloc_page(GFP_KERNEL);
+ if (!new_page)
+ break; /* Out of memory */
+ }
+
+ /*
+ * call radix_tree_preload() while we can wait.
+ */
+ err = radix_tree_preload(GFP_KERNEL);
+ if (err)
+ break;
+
+ /*
+ * Swap entry may have been freed since our caller observed it.
+ */
+ err = swapcache_prepare(entry);
+ if (err == -EEXIST) { /* seems racy */
+ radix_tree_preload_end();
+ continue;
+ }
+ if (err) { /* swp entry is obsolete ? */
+ radix_tree_preload_end();
+ break;
+ }
+
+ /* May fail (-ENOMEM) if radix-tree node allocation failed. */
+ __set_page_locked(new_page);
+ SetPageSwapBacked(new_page);
+ err = __add_to_swap_cache(new_page, entry);
+ if (likely(!err)) {
+ radix_tree_preload_end();
+ lru_cache_add_anon(new_page);
+ *retpage = new_page;
+ return ZSWAP_SWAPCACHE_NEW;
+ }
+ radix_tree_preload_end();
+ ClearPageSwapBacked(new_page);
+ __clear_page_locked(new_page);
+ /*
+ * add_to_swap_cache() doesn't return -EEXIST, so we can safely
+ * clear SWAP_HAS_CACHE flag.
+ */
+ swapcache_free(entry, NULL);
+ } while (err != -ENOMEM);
+
+ if (new_page)
+ page_cache_release(new_page);
+ if (!found_page)
+ return ZSWAP_SWAPCACHE_NOMEM;
+ *retpage = found_page;
+ return ZSWAP_SWAPCACHE_EXIST;
+}
+
+/*
+ * Attempts to free an entry by adding a page to the swap cache,
+ * decompressing the entry data into the page, and issuing a
+ * bio write to write the page back to the swap device.
+ *
+ * This can be thought of as a "resumed writeback" of the page
+ * to the swap device. We are basically resuming the same swap
+ * writeback path that was intercepted with the frontswap_store()
+ * in the first place. After the page has been decompressed into
+ * the swap cache, the compressed version stored by zswap can be
+ * freed.
+ */
+static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
+{
+ struct zswap_header *zhdr;
+ swp_entry_t swpentry;
+ struct zswap_tree *tree;
+ pgoff_t offset;
+ struct zswap_entry *entry;
+ struct page *page;
+ u8 *src, *dst;
+ unsigned int dlen;
+ int ret, refcount;
+ struct writeback_control wbc = {
+ .sync_mode = WB_SYNC_NONE,
+ };
+
+ /* extract swpentry from data */
+ zhdr = zbud_map(pool, handle);
+ swpentry = zhdr->swpentry; /* here */
+ zbud_unmap(pool, handle);
+ tree = zswap_trees[swp_type(swpentry)];
+ offset = swp_offset(swpentry);
+ BUG_ON(pool != tree->pool);
+
+ /* find and ref zswap entry */
+ spin_lock(&tree->lock);
+ entry = zswap_rb_search(&tree->rbroot, offset);
+ if (!entry) {
+ /* entry was invalidated */
+ spin_unlock(&tree->lock);
+ return 0;
+ }
+ zswap_entry_get(entry);
+ spin_unlock(&tree->lock);
+ BUG_ON(offset != entry->offset);
+
+ /* try to allocate swap cache page */
+ switch (zswap_get_swap_cache_page(swpentry, &page)) {
+ case ZSWAP_SWAPCACHE_NOMEM: /* no memory */
+ ret = -ENOMEM;
+ goto fail;
+
+ case ZSWAP_SWAPCACHE_EXIST: /* page is unlocked */
+ /* page is already in the swap cache, ignore for now */
+ page_cache_release(page);
+ ret = -EEXIST;
+ goto fail;
+
+ case ZSWAP_SWAPCACHE_NEW: /* page is locked */
+ /* decompress */
+ dlen = PAGE_SIZE;
+ src = (u8 *)zbud_map(tree->pool, entry->handle) +
+ sizeof(struct zswap_header);
+ dst = kmap_atomic(page);
+ ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src,
+ entry->length, dst, &dlen);
+ kunmap_atomic(dst);
+ zbud_unmap(tree->pool, entry->handle);
+ BUG_ON(ret);
+ BUG_ON(dlen != PAGE_SIZE);
+
+ /* page is up to date */
+ SetPageUptodate(page);
+ }
+
+ /* start writeback */
+ __swap_writepage(page, &wbc, end_swap_bio_write);
+ page_cache_release(page);
+ zswap_written_back_pages++;
+
+ spin_lock(&tree->lock);
+
+ /* drop local reference */
+ zswap_entry_put(entry);
+ /* drop the initial reference from entry creation */
+ refcount = zswap_entry_put(entry);
+
+ /*
+ * There are three possible values for refcount here:
+ * (1) refcount is 1, load is in progress, unlink from rbtree,
+ * load will free
+ * (2) refcount is 0, (normal case) entry is valid,
+ * remove from rbtree and free entry
+ * (3) refcount is -1, invalidate happened during writeback,
+ * free entry
+ */
+ if (refcount >= 0) {
+ /* no invalidate yet, remove from rbtree */
+ rb_erase(&entry->rbnode, &tree->rbroot);
+ }
+ spin_unlock(&tree->lock);
+ if (refcount <= 0) {
+ /* free the entry */
+ zswap_free_entry(tree, entry);
+ return 0;
+ }
+ return -EAGAIN;
+
+fail:
+ spin_lock(&tree->lock);
+ zswap_entry_put(entry);
+ spin_unlock(&tree->lock);
+ return ret;
+}
+
+/*********************************
+* frontswap hooks
+**********************************/
+/* attempts to compress and store an single page */
+static int zswap_frontswap_store(unsigned type, pgoff_t offset,
+ struct page *page)
+{
+ struct zswap_tree *tree = zswap_trees[type];
+ struct zswap_entry *entry, *dupentry;
+ int ret;
+ unsigned int dlen = PAGE_SIZE, len;
+ unsigned long handle;
+ char *buf;
+ u8 *src, *dst;
+ struct zswap_header *zhdr;
+
+ if (!tree) {
+ ret = -ENODEV;
+ goto reject;
+ }
+
+ /* reclaim space if needed */
+ if (zswap_is_full()) {
+ zswap_pool_limit_hit++;
+ if (zbud_reclaim_page(tree->pool, 8)) {
+ zswap_reject_reclaim_fail++;
+ ret = -ENOMEM;
+ goto reject;
+ }
+ }
+
+ /* allocate entry */
+ entry = zswap_entry_cache_alloc(GFP_KERNEL);
+ if (!entry) {
+ zswap_reject_kmemcache_fail++;
+ ret = -ENOMEM;
+ goto reject;
+ }
+
+ /* compress */
+ dst = get_cpu_var(zswap_dstmem);
+ src = kmap_atomic(page);
+ ret = zswap_comp_op(ZSWAP_COMPOP_COMPRESS, src, PAGE_SIZE, dst, &dlen);
+ kunmap_atomic(src);
+ if (ret) {
+ ret = -EINVAL;
+ goto freepage;
+ }
+
+ /* store */
+ len = dlen + sizeof(struct zswap_header);
+ ret = zbud_alloc(tree->pool, len, __GFP_NORETRY | __GFP_NOWARN,
+ &handle);
+ if (ret == -ENOSPC) {
+ zswap_reject_compress_poor++;
+ goto freepage;
+ }
+ if (ret) {
+ zswap_reject_alloc_fail++;
+ goto freepage;
+ }
+ zhdr = zbud_map(tree->pool, handle);
+ zhdr->swpentry = swp_entry(type, offset);
+ buf = (u8 *)(zhdr + 1);
+ memcpy(buf, dst, dlen);
+ zbud_unmap(tree->pool, handle);
+ put_cpu_var(zswap_dstmem);
+
+ /* populate entry */
+ entry->offset = offset;
+ entry->handle = handle;
+ entry->length = dlen;
+
+ /* map */
+ spin_lock(&tree->lock);
+ do {
+ ret = zswap_rb_insert(&tree->rbroot, entry, &dupentry);
+ if (ret == -EEXIST) {
+ zswap_duplicate_entry++;
+ /* remove from rbtree */
+ rb_erase(&dupentry->rbnode, &tree->rbroot);
+ if (!zswap_entry_put(dupentry)) {
+ /* free */
+ zswap_free_entry(tree, dupentry);
+ }
+ }
+ } while (ret == -EEXIST);
+ spin_unlock(&tree->lock);
+
+ /* update stats */
+ atomic_inc(&zswap_stored_pages);
+ zswap_pool_pages = zbud_get_pool_size(tree->pool);
+
+ return 0;
+
+freepage:
+ put_cpu_var(zswap_dstmem);
+ zswap_entry_cache_free(entry);
+reject:
+ return ret;
+}
+
+/*
+ * returns 0 if the page was successfully decompressed
+ * return -1 on entry not found or error
+*/
+static int zswap_frontswap_load(unsigned type, pgoff_t offset,
+ struct page *page)
+{
+ struct zswap_tree *tree = zswap_trees[type];
+ struct zswap_entry *entry;
+ u8 *src, *dst;
+ unsigned int dlen;
+ int refcount, ret;
+
+ /* find */
+ spin_lock(&tree->lock);
+ entry = zswap_rb_search(&tree->rbroot, offset);
+ if (!entry) {
+ /* entry was written back */
+ spin_unlock(&tree->lock);
+ return -1;
+ }
+ zswap_entry_get(entry);
+ spin_unlock(&tree->lock);
+
+ /* decompress */
+ dlen = PAGE_SIZE;
+ src = (u8 *)zbud_map(tree->pool, entry->handle) +
+ sizeof(struct zswap_header);
+ dst = kmap_atomic(page);
+ ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src, entry->length,
+ dst, &dlen);
+ kunmap_atomic(dst);
+ zbud_unmap(tree->pool, entry->handle);
+ BUG_ON(ret);
+
+ spin_lock(&tree->lock);
+ refcount = zswap_entry_put(entry);
+ if (likely(refcount)) {
+ spin_unlock(&tree->lock);
+ return 0;
+ }
+ spin_unlock(&tree->lock);
+
+ /*
+ * We don't have to unlink from the rbtree because
+ * zswap_writeback_entry() or zswap_frontswap_invalidate page()
+ * has already done this for us if we are the last reference.
+ */
+ /* free */
+
+ zswap_free_entry(tree, entry);
+
+ return 0;
+}
+
+/* frees an entry in zswap */
+static void zswap_frontswap_invalidate_page(unsigned type, pgoff_t offset)
+{
+ struct zswap_tree *tree = zswap_trees[type];
+ struct zswap_entry *entry;
+ int refcount;
+
+ /* find */
+ spin_lock(&tree->lock);
+ entry = zswap_rb_search(&tree->rbroot, offset);
+ if (!entry) {
+ /* entry was written back */
+ spin_unlock(&tree->lock);
+ return;
+ }
+
+ /* remove from rbtree */
+ rb_erase(&entry->rbnode, &tree->rbroot);
+
+ /* drop the initial reference from entry creation */
+ refcount = zswap_entry_put(entry);
+
+ spin_unlock(&tree->lock);
+
+ if (refcount) {
+ /* writeback in progress, writeback will free */
+ return;
+ }
+
+ /* free */
+ zswap_free_entry(tree, entry);
+}
+
+/* frees all zswap entries for the given swap type */
+static void zswap_frontswap_invalidate_area(unsigned type)
+{
+ struct zswap_tree *tree = zswap_trees[type];
+ struct rb_node *node;
+ struct zswap_entry *entry;
+
+ if (!tree)
+ return;
+
+ /* walk the tree and free everything */
+ spin_lock(&tree->lock);
+ /*
+ * TODO: Even though this code should not be executed because
+ * the try_to_unuse() in swapoff should have emptied the tree,
+ * it is very wasteful to rebalance the tree after every
+ * removal when we are freeing the whole tree.
+ *
+ * If post-order traversal code is ever added to the rbtree
+ * implementation, it should be used here.
+ */
+ while ((node = rb_first(&tree->rbroot))) {
+ entry = rb_entry(node, struct zswap_entry, rbnode);
+ rb_erase(&entry->rbnode, &tree->rbroot);
+ zbud_free(tree->pool, entry->handle);
+ zswap_entry_cache_free(entry);
+ atomic_dec(&zswap_stored_pages);
+ }
+ tree->rbroot = RB_ROOT;
+ spin_unlock(&tree->lock);
+}
+
+static struct zbud_ops zswap_zbud_ops = {
+ .evict = zswap_writeback_entry
+};
+
+static void zswap_frontswap_init(unsigned type)
+{
+ struct zswap_tree *tree;
+
+ tree = kzalloc(sizeof(struct zswap_tree), GFP_KERNEL);
+ if (!tree)
+ goto err;
+ tree->pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
+ if (!tree->pool)
+ goto freetree;
+ tree->rbroot = RB_ROOT;
+ spin_lock_init(&tree->lock);
+ zswap_trees[type] = tree;
+ return;
+
+freetree:
+ kfree(tree);
+err:
+ pr_err("alloc failed, zswap disabled for swap type %d\n", type);
+}
+
+static struct frontswap_ops zswap_frontswap_ops = {
+ .store = zswap_frontswap_store,
+ .load = zswap_frontswap_load,
+ .invalidate_page = zswap_frontswap_invalidate_page,
+ .invalidate_area = zswap_frontswap_invalidate_area,
+ .init = zswap_frontswap_init
+};
+
+/*********************************
+* debugfs functions
+**********************************/
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+static struct dentry *zswap_debugfs_root;
+
+static int __init zswap_debugfs_init(void)
+{
+ if (!debugfs_initialized())
+ return -ENODEV;
+
+ zswap_debugfs_root = debugfs_create_dir("zswap", NULL);
+ if (!zswap_debugfs_root)
+ return -ENOMEM;
+
+ debugfs_create_u64("pool_limit_hit", S_IRUGO,
+ zswap_debugfs_root, &zswap_pool_limit_hit);
+ debugfs_create_u64("reject_reclaim_fail", S_IRUGO,
+ zswap_debugfs_root, &zswap_reject_reclaim_fail);
+ debugfs_create_u64("reject_alloc_fail", S_IRUGO,
+ zswap_debugfs_root, &zswap_reject_alloc_fail);
+ debugfs_create_u64("reject_kmemcache_fail", S_IRUGO,
+ zswap_debugfs_root, &zswap_reject_kmemcache_fail);
+ debugfs_create_u64("reject_compress_poor", S_IRUGO,
+ zswap_debugfs_root, &zswap_reject_compress_poor);
+ debugfs_create_u64("written_back_pages", S_IRUGO,
+ zswap_debugfs_root, &zswap_written_back_pages);
+ debugfs_create_u64("duplicate_entry", S_IRUGO,
+ zswap_debugfs_root, &zswap_duplicate_entry);
+ debugfs_create_u64("pool_pages", S_IRUGO,
+ zswap_debugfs_root, &zswap_pool_pages);
+ debugfs_create_atomic_t("stored_pages", S_IRUGO,
+ zswap_debugfs_root, &zswap_stored_pages);
+
+ return 0;
+}
+
+static void __exit zswap_debugfs_exit(void)
+{
+ debugfs_remove_recursive(zswap_debugfs_root);
+}
+#else
+static int __init zswap_debugfs_init(void)
+{
+ return 0;
+}
+
+static void __exit zswap_debugfs_exit(void) { }
+#endif
+
+/*********************************
+* module init and exit
+**********************************/
+static int __init init_zswap(void)
+{
+ if (!zswap_enabled)
+ return 0;
+
+ pr_info("loading zswap\n");
+ if (zswap_entry_cache_create()) {
+ pr_err("entry cache creation failed\n");
+ goto error;
+ }
+ if (zswap_comp_init()) {
+ pr_err("compressor initialization failed\n");
+ goto compfail;
+ }
+ if (zswap_cpu_init()) {
+ pr_err("per-cpu initialization failed\n");
+ goto pcpufail;
+ }
+ frontswap_register_ops(&zswap_frontswap_ops);
+ if (zswap_debugfs_init())
+ pr_warn("debugfs initialization failed\n");
+ return 0;
+pcpufail:
+ zswap_comp_exit();
+compfail:
+ zswap_entry_cache_destory();
+error:
+ return -ENOMEM;
+}
+/* must be late so crypto has time to come up */
+late_initcall(init_zswap);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Seth Jennings <sjenning@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("Compressed cache for swap pages");
diff --git a/net/9p/client.c b/net/9p/client.c
index 01f1779..8b93cae 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -204,6 +204,17 @@
return ret;
}
+struct p9_fcall *p9_fcall_alloc(int alloc_msize)
+{
+ struct p9_fcall *fc;
+ fc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, GFP_NOFS);
+ if (!fc)
+ return NULL;
+ fc->capacity = alloc_msize;
+ fc->sdata = (char *) fc + sizeof(struct p9_fcall);
+ return fc;
+}
+
/**
* p9_tag_alloc - lookup/allocate a request by tag
* @c: client session to lookup tag within
@@ -256,39 +267,36 @@
col = tag % P9_ROW_MAXTAG;
req = &c->reqs[row][col];
- if (!req->tc) {
+ if (!req->wq) {
req->wq = kmalloc(sizeof(wait_queue_head_t), GFP_NOFS);
- if (!req->wq) {
- pr_err("Couldn't grow tag array\n");
- return ERR_PTR(-ENOMEM);
- }
+ if (!req->wq)
+ goto grow_failed;
init_waitqueue_head(req->wq);
- req->tc = kmalloc(sizeof(struct p9_fcall) + alloc_msize,
- GFP_NOFS);
- req->rc = kmalloc(sizeof(struct p9_fcall) + alloc_msize,
- GFP_NOFS);
- if ((!req->tc) || (!req->rc)) {
- pr_err("Couldn't grow tag array\n");
- kfree(req->tc);
- kfree(req->rc);
- kfree(req->wq);
- req->tc = req->rc = NULL;
- req->wq = NULL;
- return ERR_PTR(-ENOMEM);
- }
- req->tc->capacity = alloc_msize;
- req->rc->capacity = alloc_msize;
- req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall);
- req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall);
}
+ if (!req->tc)
+ req->tc = p9_fcall_alloc(alloc_msize);
+ if (!req->rc)
+ req->rc = p9_fcall_alloc(alloc_msize);
+ if (!req->tc || !req->rc)
+ goto grow_failed;
+
p9pdu_reset(req->tc);
p9pdu_reset(req->rc);
req->tc->tag = tag-1;
req->status = REQ_STATUS_ALLOC;
- return &c->reqs[row][col];
+ return req;
+
+grow_failed:
+ pr_err("Couldn't grow tag array\n");
+ kfree(req->tc);
+ kfree(req->rc);
+ kfree(req->wq);
+ req->tc = req->rc = NULL;
+ req->wq = NULL;
+ return ERR_PTR(-ENOMEM);
}
/**
@@ -648,12 +656,20 @@
return PTR_ERR(req);
- /* if we haven't received a response for oldreq,
- remove it from the list. */
+ /*
+ * if we haven't received a response for oldreq,
+ * remove it from the list, and notify the transport
+ * layer that the reply will never arrive.
+ */
spin_lock(&c->lock);
- if (oldreq->status == REQ_STATUS_FLSH)
+ if (oldreq->status == REQ_STATUS_FLSH) {
list_del(&oldreq->req_list);
- spin_unlock(&c->lock);
+ spin_unlock(&c->lock);
+ if (c->trans_mod->cancelled)
+ c->trans_mod->cancelled(c, req);
+ } else {
+ spin_unlock(&c->lock);
+ }
p9_free_req(c, req);
return 0;
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index 02efb25..3ffda1b 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -63,6 +63,7 @@
int rfd;
int wfd;
u16 port;
+ int privport;
};
/**
@@ -87,12 +88,15 @@
enum {
/* Options that take integer arguments */
Opt_port, Opt_rfdno, Opt_wfdno, Opt_err,
+ /* Options that take no arguments */
+ Opt_privport,
};
static const match_table_t tokens = {
{Opt_port, "port=%u"},
{Opt_rfdno, "rfdno=%u"},
{Opt_wfdno, "wfdno=%u"},
+ {Opt_privport, "privport"},
{Opt_err, NULL},
};
@@ -161,6 +165,9 @@
static LIST_HEAD(p9_poll_pending_list);
static DECLARE_WORK(p9_poll_work, p9_poll_workfn);
+static unsigned int p9_ipport_resv_min = P9_DEF_MIN_RESVPORT;
+static unsigned int p9_ipport_resv_max = P9_DEF_MAX_RESVPORT;
+
static void p9_mux_poll_stop(struct p9_conn *m)
{
unsigned long flags;
@@ -741,7 +748,7 @@
if (!*p)
continue;
token = match_token(p, tokens, args);
- if (token != Opt_err) {
+ if ((token != Opt_err) && (token != Opt_privport)) {
r = match_int(&args[0], &option);
if (r < 0) {
p9_debug(P9_DEBUG_ERROR,
@@ -759,6 +766,9 @@
case Opt_wfdno:
opts->wfd = option;
break;
+ case Opt_privport:
+ opts->privport = 1;
+ break;
default:
continue;
}
@@ -898,6 +908,24 @@
return 0;
}
+static int p9_bind_privport(struct socket *sock)
+{
+ struct sockaddr_in cl;
+ int port, err = -EINVAL;
+
+ memset(&cl, 0, sizeof(cl));
+ cl.sin_family = AF_INET;
+ cl.sin_addr.s_addr = INADDR_ANY;
+ for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) {
+ cl.sin_port = htons((ushort)port);
+ err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl));
+ if (err != -EADDRINUSE)
+ break;
+ }
+ return err;
+}
+
+
static int
p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
{
@@ -926,6 +954,16 @@
return err;
}
+ if (opts.privport) {
+ err = p9_bind_privport(csocket);
+ if (err < 0) {
+ pr_err("%s (%d): problem binding to privport\n",
+ __func__, task_pid_nr(current));
+ sock_release(csocket);
+ return err;
+ }
+ }
+
err = csocket->ops->connect(csocket,
(struct sockaddr *)&sin_server,
sizeof(struct sockaddr_in), 0);
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index 2c69ddd..928f2bb 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -57,9 +57,7 @@
#define P9_RDMA_IRD 0
#define P9_RDMA_ORD 0
#define P9_RDMA_TIMEOUT 30000 /* 30 seconds */
-#define P9_RDMA_MAXSIZE (4*4096) /* Min SGE is 4, so we can
- * safely advertise a maxsize
- * of 64k */
+#define P9_RDMA_MAXSIZE (1024*1024) /* 1MB */
/**
* struct p9_trans_rdma - RDMA transport instance
@@ -75,7 +73,9 @@
* @sq_depth: The depth of the Send Queue
* @sq_sem: Semaphore for the SQ
* @rq_depth: The depth of the Receive Queue.
- * @rq_count: Count of requests in the Receive Queue.
+ * @rq_sem: Semaphore for the RQ
+ * @excess_rc : Amount of posted Receive Contexts without a pending request.
+ * See rdma_request()
* @addr: The remote peer's address
* @req_lock: Protects the active request list
* @cm_done: Completion event for connection management tracking
@@ -100,7 +100,8 @@
int sq_depth;
struct semaphore sq_sem;
int rq_depth;
- atomic_t rq_count;
+ struct semaphore rq_sem;
+ atomic_t excess_rc;
struct sockaddr_in addr;
spinlock_t req_lock;
@@ -296,6 +297,13 @@
if (!req)
goto err_out;
+ /* Check that we have not yet received a reply for this request.
+ */
+ if (unlikely(req->rc)) {
+ pr_err("Duplicate reply for request %d", tag);
+ goto err_out;
+ }
+
req->rc = c->rc;
req->status = REQ_STATUS_RCVD;
p9_client_cb(client, req);
@@ -336,8 +344,8 @@
switch (c->wc_op) {
case IB_WC_RECV:
- atomic_dec(&rdma->rq_count);
handle_recv(client, rdma, c, wc.status, wc.byte_len);
+ up(&rdma->rq_sem);
break;
case IB_WC_SEND:
@@ -421,32 +429,33 @@
struct p9_rdma_context *c = NULL;
struct p9_rdma_context *rpl_context = NULL;
+ /* When an error occurs between posting the recv and the send,
+ * there will be a receive context posted without a pending request.
+ * Since there is no way to "un-post" it, we remember it and skip
+ * post_recv() for the next request.
+ * So here,
+ * see if we are this `next request' and need to absorb an excess rc.
+ * If yes, then drop and free our own, and do not recv_post().
+ **/
+ if (unlikely(atomic_read(&rdma->excess_rc) > 0)) {
+ if ((atomic_sub_return(1, &rdma->excess_rc) >= 0)) {
+ /* Got one ! */
+ kfree(req->rc);
+ req->rc = NULL;
+ goto dont_need_post_recv;
+ } else {
+ /* We raced and lost. */
+ atomic_inc(&rdma->excess_rc);
+ }
+ }
+
/* Allocate an fcall for the reply */
rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS);
if (!rpl_context) {
err = -ENOMEM;
- goto err_close;
- }
-
- /*
- * If the request has a buffer, steal it, otherwise
- * allocate a new one. Typically, requests should already
- * have receive buffers allocated and just swap them around
- */
- if (!req->rc) {
- req->rc = kmalloc(sizeof(struct p9_fcall)+client->msize,
- GFP_NOFS);
- if (req->rc) {
- req->rc->sdata = (char *) req->rc +
- sizeof(struct p9_fcall);
- req->rc->capacity = client->msize;
- }
+ goto recv_error;
}
rpl_context->rc = req->rc;
- if (!rpl_context->rc) {
- err = -ENOMEM;
- goto err_free2;
- }
/*
* Post a receive buffer for this request. We need to ensure
@@ -455,29 +464,35 @@
* outstanding request, so we must keep a count to avoid
* overflowing the RQ.
*/
- if (atomic_inc_return(&rdma->rq_count) <= rdma->rq_depth) {
- err = post_recv(client, rpl_context);
- if (err)
- goto err_free1;
- } else
- atomic_dec(&rdma->rq_count);
+ if (down_interruptible(&rdma->rq_sem)) {
+ err = -EINTR;
+ goto recv_error;
+ }
+ err = post_recv(client, rpl_context);
+ if (err) {
+ p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n");
+ goto recv_error;
+ }
/* remove posted receive buffer from request structure */
req->rc = NULL;
+dont_need_post_recv:
/* Post the request */
c = kmalloc(sizeof *c, GFP_NOFS);
if (!c) {
err = -ENOMEM;
- goto err_free1;
+ goto send_error;
}
c->req = req;
c->busa = ib_dma_map_single(rdma->cm_id->device,
c->req->tc->sdata, c->req->tc->size,
DMA_TO_DEVICE);
- if (ib_dma_mapping_error(rdma->cm_id->device, c->busa))
- goto error;
+ if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) {
+ err = -EIO;
+ goto send_error;
+ }
sge.addr = c->busa;
sge.length = c->req->tc->size;
@@ -491,22 +506,32 @@
wr.sg_list = &sge;
wr.num_sge = 1;
- if (down_interruptible(&rdma->sq_sem))
- goto error;
+ if (down_interruptible(&rdma->sq_sem)) {
+ err = -EINTR;
+ goto send_error;
+ }
- return ib_post_send(rdma->qp, &wr, &bad_wr);
+ err = ib_post_send(rdma->qp, &wr, &bad_wr);
+ if (err)
+ goto send_error;
- error:
+ /* Success */
+ return 0;
+
+ /* Handle errors that happened during or while preparing the send: */
+ send_error:
kfree(c);
- kfree(rpl_context->rc);
+ p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err);
+
+ /* Ach.
+ * We did recv_post(), but not send. We have one recv_post in excess.
+ */
+ atomic_inc(&rdma->excess_rc);
+ return err;
+
+ /* Handle errors that happened during or while preparing post_recv(): */
+ recv_error:
kfree(rpl_context);
- p9_debug(P9_DEBUG_ERROR, "EIO\n");
- return -EIO;
- err_free1:
- kfree(rpl_context->rc);
- err_free2:
- kfree(rpl_context);
- err_close:
spin_lock_irqsave(&rdma->req_lock, flags);
if (rdma->state < P9_RDMA_CLOSING) {
rdma->state = P9_RDMA_CLOSING;
@@ -551,7 +576,8 @@
spin_lock_init(&rdma->req_lock);
init_completion(&rdma->cm_done);
sema_init(&rdma->sq_sem, rdma->sq_depth);
- atomic_set(&rdma->rq_count, 0);
+ sema_init(&rdma->rq_sem, rdma->rq_depth);
+ atomic_set(&rdma->excess_rc, 0);
return rdma;
}
@@ -562,6 +588,17 @@
return 1;
}
+/* A request has been fully flushed without a reply.
+ * That means we have posted one buffer in excess.
+ */
+static int rdma_cancelled(struct p9_client *client, struct p9_req_t *req)
+{
+ struct p9_trans_rdma *rdma = client->trans;
+
+ atomic_inc(&rdma->excess_rc);
+ return 0;
+}
+
/**
* trans_create_rdma - Transport method for creating atransport instance
* @client: client instance
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index defa9d3..27ce262 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -139,11 +139,12 @@
}
EXPORT_SYMBOL_GPL(gss_mech_unregister);
-static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
+struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
{
__module_get(gm->gm_owner);
return gm;
}
+EXPORT_SYMBOL(gss_mech_get);
static struct gss_api_mech *
_gss_mech_get_by_name(const char *name)
@@ -360,6 +361,7 @@
}
return 0;
}
+EXPORT_SYMBOL(gss_pseudoflavor_to_service);
char *
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
@@ -379,6 +381,7 @@
if (gm)
module_put(gm->gm_owner);
}
+EXPORT_SYMBOL(gss_mech_put);
/* The mech could probably be determined from the token instead, but it's just
* as easy for now to pass it in. */
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index b05ace4..d0347d1 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -377,8 +377,7 @@
new->handle.data = tmp->handle.data;
tmp->handle.data = NULL;
new->mechctx = NULL;
- new->cred.cr_group_info = NULL;
- new->cred.cr_principal = NULL;
+ init_svc_cred(&new->cred);
}
static void
@@ -392,9 +391,7 @@
memset(&new->seqdata, 0, sizeof(new->seqdata));
spin_lock_init(&new->seqdata.sd_lock);
new->cred = tmp->cred;
- tmp->cred.cr_group_info = NULL;
- new->cred.cr_principal = tmp->cred.cr_principal;
- tmp->cred.cr_principal = NULL;
+ init_svc_cred(&tmp->cred);
}
static struct cache_head *
@@ -487,7 +484,7 @@
len = qword_get(&mesg, buf, mlen);
if (len < 0)
goto out;
- gm = gss_mech_get_by_name(buf);
+ gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf);
status = -EOPNOTSUPP;
if (!gm)
goto out;
@@ -517,7 +514,6 @@
rscp = rsc_update(cd, &rsci, rscp);
status = 0;
out:
- gss_mech_put(gm);
rsc_free(&rsci);
if (rscp)
cache_put(&rscp->h, cd);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 80fe5c8..49eb370 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -50,12 +50,6 @@
h->last_refresh = now;
}
-static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
-{
- return (h->expiry_time < seconds_since_boot()) ||
- (detail->flush_time > h->last_refresh);
-}
-
struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
struct cache_head *key, int hash)
{
@@ -201,7 +195,7 @@
return sunrpc_cache_pipe_upcall(cd, h);
}
-static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h)
+static inline int cache_is_valid(struct cache_head *h)
{
if (!test_bit(CACHE_VALID, &h->flags))
return -EAGAIN;
@@ -227,16 +221,15 @@
int rv;
write_lock(&detail->hash_lock);
- rv = cache_is_valid(detail, h);
- if (rv != -EAGAIN) {
- write_unlock(&detail->hash_lock);
- return rv;
+ rv = cache_is_valid(h);
+ if (rv == -EAGAIN) {
+ set_bit(CACHE_NEGATIVE, &h->flags);
+ cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
+ rv = -ENOENT;
}
- set_bit(CACHE_NEGATIVE, &h->flags);
- cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
write_unlock(&detail->hash_lock);
cache_fresh_unlocked(h, detail);
- return -ENOENT;
+ return rv;
}
/*
@@ -260,7 +253,7 @@
long refresh_age, age;
/* First decide return status as best we can */
- rv = cache_is_valid(detail, h);
+ rv = cache_is_valid(h);
/* now see if we want to start an upcall */
refresh_age = (h->expiry_time - h->last_refresh);
@@ -269,19 +262,17 @@
if (rqstp == NULL) {
if (rv == -EAGAIN)
rv = -ENOENT;
- } else if (rv == -EAGAIN || age > refresh_age/2) {
+ } else if (rv == -EAGAIN ||
+ (h->expiry_time != 0 && age > refresh_age/2)) {
dprintk("RPC: Want update, refage=%ld, age=%ld\n",
refresh_age, age);
if (!test_and_set_bit(CACHE_PENDING, &h->flags)) {
switch (cache_make_upcall(detail, h)) {
case -EINVAL:
- clear_bit(CACHE_PENDING, &h->flags);
- cache_revisit_request(h);
rv = try_to_negate_entry(detail, h);
break;
case -EAGAIN:
- clear_bit(CACHE_PENDING, &h->flags);
- cache_revisit_request(h);
+ cache_fresh_unlocked(h, detail);
break;
}
}
@@ -293,7 +284,7 @@
* Request was not deferred; handle it as best
* we can ourselves:
*/
- rv = cache_is_valid(detail, h);
+ rv = cache_is_valid(h);
if (rv == -EAGAIN)
rv = -ETIMEDOUT;
}
@@ -310,7 +301,7 @@
* a current pointer into that list and into the table
* for that entry.
*
- * Each time clean_cache is called it finds the next non-empty entry
+ * Each time cache_clean is called it finds the next non-empty entry
* in the current table and walks the list in that entry
* looking for entries that can be removed.
*
@@ -457,9 +448,8 @@
current_index ++;
spin_unlock(&cache_list_lock);
if (ch) {
- if (test_and_clear_bit(CACHE_PENDING, &ch->flags))
- cache_dequeue(current_detail, ch);
- cache_revisit_request(ch);
+ set_bit(CACHE_CLEANED, &ch->flags);
+ cache_fresh_unlocked(ch, d);
cache_put(ch, d);
}
} else
@@ -1036,23 +1026,32 @@
static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
{
- struct cache_queue *cq;
+ struct cache_queue *cq, *tmp;
+ struct cache_request *cr;
+ struct list_head dequeued;
+
+ INIT_LIST_HEAD(&dequeued);
spin_lock(&queue_lock);
- list_for_each_entry(cq, &detail->queue, list)
+ list_for_each_entry_safe(cq, tmp, &detail->queue, list)
if (!cq->reader) {
- struct cache_request *cr = container_of(cq, struct cache_request, q);
+ cr = container_of(cq, struct cache_request, q);
if (cr->item != ch)
continue;
+ if (test_bit(CACHE_PENDING, &ch->flags))
+ /* Lost a race and it is pending again */
+ break;
if (cr->readers != 0)
continue;
- list_del(&cr->q.list);
- spin_unlock(&queue_lock);
- cache_put(cr->item, detail);
- kfree(cr->buf);
- kfree(cr);
- return;
+ list_move(&cr->q.list, &dequeued);
}
spin_unlock(&queue_lock);
+ while (!list_empty(&dequeued)) {
+ cr = list_entry(dequeued.next, struct cache_request, q.list);
+ list_del(&cr->q.list);
+ cache_put(cr->item, detail);
+ kfree(cr->buf);
+ kfree(cr);
+ }
}
/*
@@ -1166,6 +1165,7 @@
char *buf;
struct cache_request *crq;
+ int ret = 0;
if (!detail->cache_request)
return -EINVAL;
@@ -1174,6 +1174,9 @@
warn_no_listener(detail);
return -EINVAL;
}
+ if (test_bit(CACHE_CLEANED, &h->flags))
+ /* Too late to make an upcall */
+ return -EAGAIN;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
@@ -1191,10 +1194,18 @@
crq->len = 0;
crq->readers = 0;
spin_lock(&queue_lock);
- list_add_tail(&crq->q.list, &detail->queue);
+ if (test_bit(CACHE_PENDING, &h->flags))
+ list_add_tail(&crq->q.list, &detail->queue);
+ else
+ /* Lost a race, no longer PENDING, so don't enqueue */
+ ret = -EAGAIN;
spin_unlock(&queue_lock);
wake_up(&queue_wait);
- return 0;
+ if (ret == -EAGAIN) {
+ kfree(buf);
+ kfree(crq);
+ }
+ return ret;
}
EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index f0339ae9..aa40156 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -290,7 +290,7 @@
struct rpc_auth *auth;
struct net *net = rpc_net_ns(clnt);
struct super_block *pipefs_sb;
- int err = 0;
+ int err;
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
@@ -299,6 +299,10 @@
goto out;
}
+ rpc_register_client(clnt);
+ if (pipefs_sb)
+ rpc_put_sb_net(net);
+
auth = rpcauth_create(args->authflavor, clnt);
if (IS_ERR(auth)) {
dprintk("RPC: Couldn't create auth handle (flavor %u)\n",
@@ -306,16 +310,14 @@
err = PTR_ERR(auth);
goto err_auth;
}
-
- rpc_register_client(clnt);
+ return 0;
+err_auth:
+ pipefs_sb = rpc_get_sb_net(net);
+ __rpc_clnt_remove_pipedir(clnt);
out:
if (pipefs_sb)
rpc_put_sb_net(net);
return err;
-
-err_auth:
- __rpc_clnt_remove_pipedir(clnt);
- goto out;
}
static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt)
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 4679df5..61239a2 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -480,6 +480,23 @@
.d_delete = rpc_delete_dentry,
};
+/*
+ * Lookup the data. This is trivial - if the dentry didn't already
+ * exist, we know it is negative.
+ */
+static struct dentry *
+rpc_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+{
+ if (dentry->d_name.len > NAME_MAX)
+ return ERR_PTR(-ENAMETOOLONG);
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+static const struct inode_operations rpc_dir_inode_operations = {
+ .lookup = rpc_lookup,
+};
+
static struct inode *
rpc_get_inode(struct super_block *sb, umode_t mode)
{
@@ -492,7 +509,7 @@
switch (mode & S_IFMT) {
case S_IFDIR:
inode->i_fop = &simple_dir_operations;
- inode->i_op = &simple_dir_inode_operations;
+ inode->i_op = &rpc_dir_inode_operations;
inc_nlink(inode);
default:
break;
@@ -666,11 +683,8 @@
if (!dentry)
return ERR_PTR(-ENOMEM);
}
- if (dentry->d_inode == NULL) {
- if (!dentry->d_op)
- d_set_d_op(dentry, &rpc_dentry_operations);
+ if (dentry->d_inode == NULL)
return dentry;
- }
dput(dentry);
return ERR_PTR(-EEXIST);
}
@@ -1117,6 +1131,7 @@
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = RPCAUTH_GSSMAGIC;
sb->s_op = &s_ops;
+ sb->s_d_op = &rpc_dentry_operations;
sb->s_time_gran = 1;
inode = rpc_get_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 06bdf5a..621ca7b 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -347,13 +347,13 @@
spin_lock(&xprt->xpt_lock);
ipm = xprt->xpt_auth_cache;
if (ipm != NULL) {
- if (!cache_valid(&ipm->h)) {
+ sn = net_generic(xprt->xpt_net, sunrpc_net_id);
+ if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
/*
* The entry has been invalidated since it was
* remembered, e.g. by a second mount from the
* same IP address.
*/
- sn = net_generic(xprt->xpt_net, sunrpc_net_id);
xprt->xpt_auth_cache = NULL;
spin_unlock(&xprt->xpt_lock);
cache_put(&ipm->h, sn->ip_map_cache);
@@ -493,8 +493,6 @@
if (rv)
return -EINVAL;
uid = make_kuid(&init_user_ns, id);
- if (!uid_valid(uid))
- return -EINVAL;
ug.uid = uid;
expiry = get_expiry(&mesg);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 0f679df..305374d 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -917,7 +917,10 @@
len = svsk->sk_datalen;
npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
- BUG_ON(svsk->sk_pages[i] == NULL);
+ if (svsk->sk_pages[i] == NULL) {
+ WARN_ON_ONCE(1);
+ continue;
+ }
put_page(svsk->sk_pages[i]);
svsk->sk_pages[i] = NULL;
}
@@ -1092,8 +1095,10 @@
goto err_noclose;
}
- if (svc_sock_reclen(svsk) < 8)
+ if (svsk->sk_datalen < 8) {
+ svsk->sk_datalen = 0;
goto err_delete; /* client is nuts. */
+ }
rqstp->rq_arg.len = svsk->sk_datalen;
rqstp->rq_arg.page_base = 0;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 412de7c..ddf0602 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -2534,7 +2534,6 @@
.reserve_xprt = xprt_reserve_xprt,
.release_xprt = xprt_release_xprt,
.alloc_slot = xprt_alloc_slot,
- .rpcbind = xs_local_rpcbind,
.buf_alloc = bc_malloc,
.buf_free = bc_free,
.send_request = bc_send_request,
diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst
index 182084d..8ccf830 100644
--- a/scripts/Makefile.headersinst
+++ b/scripts/Makefile.headersinst
@@ -47,18 +47,24 @@
all-files := $(header-y) $(genhdr-y) $(wrapper-files)
output-files := $(addprefix $(installdir)/, $(all-files))
-input-files := $(foreach hdr, $(header-y), \
+input-files1 := $(foreach hdr, $(header-y), \
$(if $(wildcard $(srcdir)/$(hdr)), \
- $(wildcard $(srcdir)/$(hdr)), \
+ $(wildcard $(srcdir)/$(hdr))) \
+ )
+input-files1-name := $(notdir $(input-files1))
+input-files2 := $(foreach hdr, $(header-y), \
+ $(if $(wildcard $(srcdir)/$(hdr)),, \
$(if $(wildcard $(oldsrcdir)/$(hdr)), \
$(wildcard $(oldsrcdir)/$(hdr)), \
$(error Missing UAPI file $(srcdir)/$(hdr))) \
- )) \
- $(foreach hdr, $(genhdr-y), \
+ ))
+input-files2-name := $(notdir $(input-files2))
+input-files3 := $(foreach hdr, $(genhdr-y), \
$(if $(wildcard $(gendir)/$(hdr)), \
$(wildcard $(gendir)/$(hdr)), \
$(error Missing generated UAPI file $(gendir)/$(hdr)) \
))
+input-files3-name := $(notdir $(input-files3))
# Work out what needs to be removed
oldheaders := $(patsubst $(installdir)/%,%,$(wildcard $(installdir)/*.h))
@@ -72,7 +78,9 @@
quiet_cmd_install = INSTALL $(printdir) ($(words $(all-files))\
file$(if $(word 2, $(all-files)),s))
cmd_install = \
- $(CONFIG_SHELL) $< $(installdir) $(input-files); \
+ $(CONFIG_SHELL) $< $(installdir) $(srcdir) $(input-files1-name); \
+ $(CONFIG_SHELL) $< $(installdir) $(oldsrcdir) $(input-files2-name); \
+ $(CONFIG_SHELL) $< $(installdir) $(gendir) $(input-files3-name); \
for F in $(wrapper-files); do \
echo "\#include <asm-generic/$$F>" > $(installdir)/$$F; \
done; \
@@ -98,7 +106,7 @@
@:
targets += $(install-file)
-$(install-file): scripts/headers_install.sh $(input-files) FORCE
+$(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE
$(if $(unwanted),$(call cmd,remove),)
$(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@)))
$(call if_changed,install)
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 6031e23..49392ec 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -63,7 +63,7 @@
subdir-obj-y := $(filter %/built-in.o, $(obj-y))
# $(obj-dirs) is a list of directories that contain object files
-obj-dirs := $(dir $(multi-objs) $(subdir-obj-y))
+obj-dirs := $(dir $(multi-objs) $(obj-y))
# Replace multi-part objects by their individual parts, look at local dir only
real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y)
@@ -244,7 +244,7 @@
# ---------------------------------------------------------------------------
# Generate an assembly file to wrap the output of the device tree compiler
-quiet_cmd_dt_S_dtb= DTB $@
+quiet_cmd_dt_S_dtb= DTB $@
cmd_dt_S_dtb= \
( \
echo '\#include <asm-generic/vmlinux.lds.h>'; \
diff --git a/scripts/coccicheck b/scripts/coccicheck
index 06fcb33..bbf901a 100755
--- a/scripts/coccicheck
+++ b/scripts/coccicheck
@@ -1,17 +1,31 @@
#!/bin/bash
+#
+# This script requires at least spatch
+# version 1.0.0-rc11.
+#
+
SPATCH="`which ${SPATCH:=spatch}`"
+trap kill_running SIGTERM SIGINT
+declare -a SPATCH_PID
+
# The verbosity may be set by the environmental parameter V=
# as for example with 'make V=1 coccicheck'
if [ -n "$V" -a "$V" != "0" ]; then
- VERBOSE=1
+ VERBOSE="$V"
else
VERBOSE=0
fi
-FLAGS="$SPFLAGS -very_quiet"
+if [ -z "$J" ]; then
+ NPROC=$(getconf _NPROCESSORS_ONLN)
+else
+ NPROC="$J"
+fi
+
+FLAGS="$SPFLAGS --very-quiet"
# spatch only allows include directories with the syntax "-I include"
# while gcc also allows "-Iinclude" and "-include include"
@@ -27,14 +41,14 @@
else
ONLINE=0
if [ "$KBUILD_EXTMOD" = "" ] ; then
- OPTIONS="-dir $srctree $COCCIINCLUDE"
+ OPTIONS="--dir $srctree $COCCIINCLUDE"
else
- OPTIONS="-dir $KBUILD_EXTMOD $COCCIINCLUDE"
+ OPTIONS="--dir $KBUILD_EXTMOD $COCCIINCLUDE"
fi
fi
if [ "$KBUILD_EXTMOD" != "" ] ; then
- OPTIONS="-patch $srctree $OPTIONS"
+ OPTIONS="--patch $srctree $OPTIONS"
fi
if [ ! -x "$SPATCH" ]; then
@@ -44,13 +58,21 @@
if [ "$MODE" = "" ] ; then
if [ "$ONLINE" = "0" ] ; then
- echo 'You have not explicitly specified the mode to use. Using default "chain" mode.'
- echo 'All available modes will be tried (in that order): patch, report, context, org'
+ echo 'You have not explicitly specified the mode to use. Using default "report" mode.'
+ echo 'Available modes are the following: patch, report, context, org'
echo 'You can specify the mode with "make coccicheck MODE=<mode>"'
+ echo 'Note however that some modes are not implemented by some semantic patches.'
fi
- MODE="chain"
+ MODE="report"
+fi
+
+if [ "$MODE" = "chain" ] ; then
+ if [ "$ONLINE" = "0" ] ; then
+ echo 'You have selected the "chain" mode.'
+ echo 'All available modes will be tried (in that order): patch, report, context, org'
+ fi
elif [ "$MODE" = "report" -o "$MODE" = "org" ] ; then
- FLAGS="$FLAGS -no_show_diff"
+ FLAGS="$FLAGS --no-show-diff"
fi
if [ "$ONLINE" = "0" ] ; then
@@ -61,19 +83,35 @@
fi
run_cmd() {
+ local i
if [ $VERBOSE -ne 0 ] ; then
- echo "Running: $@"
+ echo "Running ($NPROC in parallel): $@"
fi
- eval $@
+ for i in $(seq 0 $(( NPROC - 1)) ); do
+ eval "$@ --max $NPROC --index $i &"
+ SPATCH_PID[$i]=$!
+ if [ $VERBOSE -eq 2 ] ; then
+ echo "${SPATCH_PID[$i]} running"
+ fi
+ done
+ wait
}
+kill_running() {
+ for i in $(seq $(( NPROC - 1 )) ); do
+ if [ $VERBOSE -eq 2 ] ; then
+ echo "Killing ${SPATCH_PID[$i]}"
+ fi
+ kill ${SPATCH_PID[$i]} 2>/dev/null
+ done
+}
coccinelle () {
COCCI="$1"
OPT=`grep "Option" $COCCI | cut -d':' -f2`
-# The option '-parse_cocci' can be used to syntactically check the SmPL files.
+# The option '--parse-cocci' can be used to syntactically check the SmPL files.
#
# $SPATCH -D $MODE $FLAGS -parse_cocci $COCCI $OPT > /dev/null
@@ -114,20 +152,20 @@
if [ "$MODE" = "chain" ] ; then
run_cmd $SPATCH -D patch \
- $FLAGS -sp_file $COCCI $OPT $OPTIONS || \
+ $FLAGS --cocci-file $COCCI $OPT $OPTIONS || \
run_cmd $SPATCH -D report \
- $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff || \
+ $FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff || \
run_cmd $SPATCH -D context \
- $FLAGS -sp_file $COCCI $OPT $OPTIONS || \
+ $FLAGS --cocci-file $COCCI $OPT $OPTIONS || \
run_cmd $SPATCH -D org \
- $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff || exit 1
+ $FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff || exit 1
elif [ "$MODE" = "rep+ctxt" ] ; then
run_cmd $SPATCH -D report \
- $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff && \
+ $FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff && \
run_cmd $SPATCH -D context \
- $FLAGS -sp_file $COCCI $OPT $OPTIONS || exit 1
+ $FLAGS --cocci-file $COCCI $OPT $OPTIONS || exit 1
else
- run_cmd $SPATCH -D $MODE $FLAGS -sp_file $COCCI $OPT $OPTIONS || exit 1
+ run_cmd $SPATCH -D $MODE $FLAGS --cocci-file $COCCI $OPT $OPTIONS || exit 1
fi
}
diff --git a/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci b/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci
index 7d4771d..bd5d08b 100644
--- a/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci
+++ b/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci
@@ -5,7 +5,7 @@
// Confidence: High
// Copyright: 2009,2010 Nicolas Palix, DIKU. GPLv2.
// URL: http://coccinelle.lip6.fr/
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
//
// Keywords: kmalloc, kzalloc, kcalloc
// Version min: < 2.6.12 kmalloc
diff --git a/scripts/coccinelle/api/alloc/kzalloc-simple.cocci b/scripts/coccinelle/api/alloc/kzalloc-simple.cocci
index 046b9b1..52c55e4 100644
--- a/scripts/coccinelle/api/alloc/kzalloc-simple.cocci
+++ b/scripts/coccinelle/api/alloc/kzalloc-simple.cocci
@@ -9,7 +9,7 @@
// Copyright: (C) 2009-2010 Julia Lawall, Nicolas Palix, DIKU. GPLv2.
// Copyright: (C) 2009-2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/rules/kzalloc.html
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
//
// Keywords: kmalloc, kzalloc
// Version min: < 2.6.12 kmalloc
diff --git a/scripts/coccinelle/api/d_find_alias.cocci b/scripts/coccinelle/api/d_find_alias.cocci
index a9694a8..9594c9f 100644
--- a/scripts/coccinelle/api/d_find_alias.cocci
+++ b/scripts/coccinelle/api/d_find_alias.cocci
@@ -4,7 +4,7 @@
//
// Confidence: Moderate
// URL: http://coccinelle.lip6.fr/
-// Options: -include_headers
+// Options: --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/api/devm_request_and_ioremap.cocci b/scripts/coccinelle/api/devm_request_and_ioremap.cocci
index 46beb81..562ec88 100644
--- a/scripts/coccinelle/api/devm_request_and_ioremap.cocci
+++ b/scripts/coccinelle/api/devm_request_and_ioremap.cocci
@@ -10,7 +10,7 @@
// Copyright: (C) 2011 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual org
diff --git a/scripts/coccinelle/api/kstrdup.cocci b/scripts/coccinelle/api/kstrdup.cocci
index 07a74b2..09cba54 100644
--- a/scripts/coccinelle/api/kstrdup.cocci
+++ b/scripts/coccinelle/api/kstrdup.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/api/memdup.cocci b/scripts/coccinelle/api/memdup.cocci
index 4dceab6..3d1aa71 100644
--- a/scripts/coccinelle/api/memdup.cocci
+++ b/scripts/coccinelle/api/memdup.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci
index 2b131a8..c606231 100644
--- a/scripts/coccinelle/api/memdup_user.cocci
+++ b/scripts/coccinelle/api/memdup_user.cocci
@@ -7,7 +7,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/api/ptr_ret.cocci b/scripts/coccinelle/api/ptr_ret.cocci
index 15f076f..2274638 100644
--- a/scripts/coccinelle/api/ptr_ret.cocci
+++ b/scripts/coccinelle/api/ptr_ret.cocci
@@ -5,7 +5,7 @@
// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2.
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
//
// Keywords: ERR_PTR, PTR_ERR, PTR_RET
// Version min: 2.6.39
diff --git a/scripts/coccinelle/api/simple_open.cocci b/scripts/coccinelle/api/simple_open.cocci
index 05962f7..b67e174 100644
--- a/scripts/coccinelle/api/simple_open.cocci
+++ b/scripts/coccinelle/api/simple_open.cocci
@@ -4,7 +4,7 @@
///
// Confidence: High
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual report
diff --git a/scripts/coccinelle/free/devm_free.cocci b/scripts/coccinelle/free/devm_free.cocci
index 0a1e361..3d93490 100644
--- a/scripts/coccinelle/free/devm_free.cocci
+++ b/scripts/coccinelle/free/devm_free.cocci
@@ -18,7 +18,7 @@
// Copyright: (C) 2011 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/free/kfree.cocci b/scripts/coccinelle/free/kfree.cocci
index d9ae6d8..577b7805 100644
--- a/scripts/coccinelle/free/kfree.cocci
+++ b/scripts/coccinelle/free/kfree.cocci
@@ -10,7 +10,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/free/kfreeaddr.cocci b/scripts/coccinelle/free/kfreeaddr.cocci
new file mode 100644
index 0000000..ce8aacc
--- /dev/null
+++ b/scripts/coccinelle/free/kfreeaddr.cocci
@@ -0,0 +1,32 @@
+/// Free of a structure field
+///
+// Confidence: High
+// Copyright: (C) 2013 Julia Lawall, INRIA/LIP6. GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options: --no-includes --include-headers
+
+virtual org
+virtual report
+virtual context
+
+@r depends on context || report || org @
+expression e;
+identifier f;
+position p;
+@@
+
+* kfree@p(&e->f)
+
+@script:python depends on org@
+p << r.p;
+@@
+
+cocci.print_main("kfree",p)
+
+@script:python depends on report@
+p << r.p;
+@@
+
+msg = "ERROR: kfree of structure field"
+coccilib.report.print_report(p[0],msg)
diff --git a/scripts/coccinelle/free/pci_free_consistent.cocci b/scripts/coccinelle/free/pci_free_consistent.cocci
new file mode 100644
index 0000000..43600cc
--- /dev/null
+++ b/scripts/coccinelle/free/pci_free_consistent.cocci
@@ -0,0 +1,52 @@
+/// Find missing pci_free_consistent for every pci_alloc_consistent.
+///
+// Confidence: Moderate
+// Copyright: (C) 2013 Petr Strnad. GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Keywords: pci_free_consistent, pci_alloc_consistent
+// Options: --no-includes --include-headers
+
+virtual report
+virtual org
+
+@search@
+local idexpression id;
+expression x,y,z,e;
+position p1,p2;
+type T;
+@@
+
+id = pci_alloc_consistent@p1(x,y,&z)
+... when != e = id
+if (id == NULL || ...) { ... return ...; }
+... when != pci_free_consistent(x,y,id,z)
+ when != if (id) { ... pci_free_consistent(x,y,id,z) ... }
+ when != if (y) { ... pci_free_consistent(x,y,id,z) ... }
+ when != e = (T)id
+ when exists
+(
+return 0;
+|
+return 1;
+|
+return id;
+|
+return@p2 ...;
+)
+
+@script:python depends on report@
+p1 << search.p1;
+p2 << search.p2;
+@@
+
+msg = "ERROR: missing pci_free_consistent; pci_alloc_consistent on line %s and return without freeing on line %s" % (p1[0].line,p2[0].line)
+coccilib.report.print_report(p2[0],msg)
+
+@script:python depends on org@
+p1 << search.p1;
+p2 << search.p2;
+@@
+
+msg = "ERROR: missing pci_free_consistent; pci_alloc_consistent on line %s and return without freeing on line %s" % (p1[0].line,p2[0].line)
+cocci.print_main(msg,p1)
+cocci.print_secs("",p2)
diff --git a/scripts/coccinelle/iterators/fen.cocci b/scripts/coccinelle/iterators/fen.cocci
index 0a40af8..48c152f 100644
--- a/scripts/coccinelle/iterators/fen.cocci
+++ b/scripts/coccinelle/iterators/fen.cocci
@@ -7,7 +7,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/iterators/itnull.cocci b/scripts/coccinelle/iterators/itnull.cocci
index 259899f..f58732b 100644
--- a/scripts/coccinelle/iterators/itnull.cocci
+++ b/scripts/coccinelle/iterators/itnull.cocci
@@ -11,7 +11,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/iterators/list_entry_update.cocci b/scripts/coccinelle/iterators/list_entry_update.cocci
index b296747..873f444 100644
--- a/scripts/coccinelle/iterators/list_entry_update.cocci
+++ b/scripts/coccinelle/iterators/list_entry_update.cocci
@@ -9,7 +9,7 @@
// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/iterators/use_after_iter.cocci b/scripts/coccinelle/iterators/use_after_iter.cocci
index 06284c5..f085f59 100644
--- a/scripts/coccinelle/iterators/use_after_iter.cocci
+++ b/scripts/coccinelle/iterators/use_after_iter.cocci
@@ -11,7 +11,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA/LIP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/locks/call_kern.cocci b/scripts/coccinelle/locks/call_kern.cocci
index 8f10b49..669b244 100644
--- a/scripts/coccinelle/locks/call_kern.cocci
+++ b/scripts/coccinelle/locks/call_kern.cocci
@@ -9,7 +9,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/locks/double_lock.cocci b/scripts/coccinelle/locks/double_lock.cocci
index 63b24e6..002752f 100644
--- a/scripts/coccinelle/locks/double_lock.cocci
+++ b/scripts/coccinelle/locks/double_lock.cocci
@@ -8,7 +8,7 @@
// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/locks/flags.cocci b/scripts/coccinelle/locks/flags.cocci
index 1c4ffe6..debd70e 100644
--- a/scripts/coccinelle/locks/flags.cocci
+++ b/scripts/coccinelle/locks/flags.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/locks/mini_lock.cocci b/scripts/coccinelle/locks/mini_lock.cocci
index 3267d74..47f649b 100644
--- a/scripts/coccinelle/locks/mini_lock.cocci
+++ b/scripts/coccinelle/locks/mini_lock.cocci
@@ -11,7 +11,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/misc/boolinit.cocci b/scripts/coccinelle/misc/boolinit.cocci
index 97ce41c..b9abed4 100644
--- a/scripts/coccinelle/misc/boolinit.cocci
+++ b/scripts/coccinelle/misc/boolinit.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2.
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
-// Options: -include_headers
+// Options: --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/misc/cstptr.cocci b/scripts/coccinelle/misc/cstptr.cocci
index d425644..f0368b3 100644
--- a/scripts/coccinelle/misc/cstptr.cocci
+++ b/scripts/coccinelle/misc/cstptr.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/misc/doubleinit.cocci b/scripts/coccinelle/misc/doubleinit.cocci
index cf74a00..c0c3371 100644
--- a/scripts/coccinelle/misc/doubleinit.cocci
+++ b/scripts/coccinelle/misc/doubleinit.cocci
@@ -8,7 +8,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments: requires at least Coccinelle 0.2.4, lex or parse error otherwise
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/misc/ifaddr.cocci b/scripts/coccinelle/misc/ifaddr.cocci
index 3e4089a..8aebd18 100644
--- a/scripts/coccinelle/misc/ifaddr.cocci
+++ b/scripts/coccinelle/misc/ifaddr.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/misc/ifcol.cocci b/scripts/coccinelle/misc/ifcol.cocci
index b7ed91d..d0d00ef 100644
--- a/scripts/coccinelle/misc/ifcol.cocci
+++ b/scripts/coccinelle/misc/ifcol.cocci
@@ -13,7 +13,7 @@
// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/misc/noderef.cocci b/scripts/coccinelle/misc/noderef.cocci
index c170721..80a831c 100644
--- a/scripts/coccinelle/misc/noderef.cocci
+++ b/scripts/coccinelle/misc/noderef.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/misc/orplus.cocci b/scripts/coccinelle/misc/orplus.cocci
index 4a28cef..81fabf3 100644
--- a/scripts/coccinelle/misc/orplus.cocci
+++ b/scripts/coccinelle/misc/orplus.cocci
@@ -7,7 +7,7 @@
// Copyright: (C) 2013 Gilles Muller, INRIA/LIP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual org
virtual report
diff --git a/scripts/coccinelle/misc/warn.cocci b/scripts/coccinelle/misc/warn.cocci
index fda8c35..d2e5b6c 100644
--- a/scripts/coccinelle/misc/warn.cocci
+++ b/scripts/coccinelle/misc/warn.cocci
@@ -5,7 +5,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/null/eno.cocci b/scripts/coccinelle/null/eno.cocci
index ed961a1..9bd29aa 100644
--- a/scripts/coccinelle/null/eno.cocci
+++ b/scripts/coccinelle/null/eno.cocci
@@ -6,7 +6,7 @@
// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/coccinelle/null/kmerr.cocci b/scripts/coccinelle/null/kmerr.cocci
index 949bf65..5354a79 100644
--- a/scripts/coccinelle/null/kmerr.cocci
+++ b/scripts/coccinelle/null/kmerr.cocci
@@ -10,7 +10,7 @@
// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/tests/doublebitand.cocci b/scripts/coccinelle/tests/doublebitand.cocci
index 9ba73d0..72f1572a 100644
--- a/scripts/coccinelle/tests/doublebitand.cocci
+++ b/scripts/coccinelle/tests/doublebitand.cocci
@@ -10,7 +10,7 @@
// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/tests/doubletest.cocci b/scripts/coccinelle/tests/doubletest.cocci
index 13a2c0e..78d74c2 100644
--- a/scripts/coccinelle/tests/doubletest.cocci
+++ b/scripts/coccinelle/tests/doubletest.cocci
@@ -8,7 +8,7 @@
// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual context
virtual org
diff --git a/scripts/coccinelle/tests/odd_ptr_err.cocci b/scripts/coccinelle/tests/odd_ptr_err.cocci
index e8dd8a6..cfe0a35 100644
--- a/scripts/coccinelle/tests/odd_ptr_err.cocci
+++ b/scripts/coccinelle/tests/odd_ptr_err.cocci
@@ -7,7 +7,7 @@
// Copyright: (C) 2012 Gilles Muller, INRIA. GPLv2.
// URL: http://coccinelle.lip6.fr/
// Comments:
-// Options: -no_includes -include_headers
+// Options: --no-includes --include-headers
virtual patch
virtual context
diff --git a/scripts/config b/scripts/config
index a65ecbb..567120a 100755
--- a/scripts/config
+++ b/scripts/config
@@ -1,6 +1,8 @@
#!/bin/bash
# Manipulate options in a .config file from the command line
+myname=${0##*/}
+
# If no prefix forced, use the default CONFIG_
CONFIG_="${CONFIG_-CONFIG_}"
@@ -8,7 +10,7 @@
cat >&2 <<EOL
Manipulate options in a .config file from the command line.
Usage:
-config options command ...
+$myname options command ...
commands:
--enable|-e option Enable option
--disable|-d option Disable option
@@ -33,14 +35,14 @@
--file config-file .config file to change (default .config)
--keep-case|-k Keep next symbols' case (dont' upper-case it)
-config doesn't check the validity of the .config file. This is done at next
+$myname doesn't check the validity of the .config file. This is done at next
make time.
-By default, config will upper-case the given symbol. Use --keep-case to keep
+By default, $myname will upper-case the given symbol. Use --keep-case to keep
the case of all following symbols unchanged.
-config uses 'CONFIG_' as the default symbol prefix. Set the environment
-variable CONFIG_ to the prefix to use. Eg.: CONFIG_="FOO_" config ...
+$myname uses 'CONFIG_' as the default symbol prefix. Set the environment
+variable CONFIG_ to the prefix to use. Eg.: CONFIG_="FOO_" $myname ...
EOL
exit 1
}
diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh
index 643764f..5de5660 100644
--- a/scripts/headers_install.sh
+++ b/scripts/headers_install.sh
@@ -2,7 +2,7 @@
if [ $# -lt 1 ]
then
- echo "Usage: headers_install.sh OUTDIR [FILES...]
+ echo "Usage: headers_install.sh OUTDIR SRCDIR [FILES...]
echo
echo "Prepares kernel header files for use by user space, by removing"
echo "all compiler.h definitions and #includes, removing any"
@@ -10,6 +10,7 @@
echo "asm/inline/volatile keywords."
echo
echo "OUTDIR: directory to write each userspace header FILE to."
+ echo "SRCDIR: source directory where files are picked."
echo "FILES: list of header files to operate on."
exit 1
@@ -19,6 +20,8 @@
OUTDIR="$1"
shift
+SRCDIR="$1"
+shift
# Iterate through files listed on command line
@@ -34,7 +37,7 @@
-e 's/(^|[^a-zA-Z0-9])__packed([^a-zA-Z0-9_]|$)/\1__attribute__((packed))\2/g' \
-e 's/(^|[ \t(])(inline|asm|volatile)([ \t(]|$)/\1__\2__\3/g' \
-e 's@#(ifndef|define|endif[ \t]*/[*])[ \t]*_UAPI@#\1 @' \
- "$i" > "$OUTDIR/$FILE.sed" || exit 1
+ "$SRCDIR/$i" > "$OUTDIR/$FILE.sed" || exit 1
scripts/unifdef -U__KERNEL__ -D__EXPORTED_HEADERS__ "$OUTDIR/$FILE.sed" \
> "$OUTDIR/$FILE"
[ $? -gt 1 ] && exit 1
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index bde5b95..d19944f 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -527,11 +527,12 @@
seed_env = getenv("KCONFIG_SEED");
if( seed_env && *seed_env ) {
char *endp;
- int tmp = (int)strtol(seed_env, &endp, 10);
+ int tmp = (int)strtol(seed_env, &endp, 0);
if (*endp == '\0') {
seed = tmp;
}
}
+ fprintf( stderr, "KCONFIG_SEED=0x%X\n", seed );
srand(seed);
break;
}
@@ -653,7 +654,8 @@
conf_set_all_new_symbols(def_default);
break;
case randconfig:
- conf_set_all_new_symbols(def_random);
+ /* Really nothing to do in this loop */
+ while (conf_set_all_new_symbols(def_random)) ;
break;
case defconfig:
conf_set_all_new_symbols(def_default);
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 43eda40..c55c227 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -1040,7 +1040,7 @@
conf_changed_callback = fn;
}
-static void randomize_choice_values(struct symbol *csym)
+static bool randomize_choice_values(struct symbol *csym)
{
struct property *prop;
struct symbol *sym;
@@ -1053,7 +1053,7 @@
* In both cases stop.
*/
if (csym->curr.tri != yes)
- return;
+ return false;
prop = sym_get_choice_prop(csym);
@@ -1077,13 +1077,18 @@
else {
sym->def[S_DEF_USER].tri = no;
}
+ sym->flags |= SYMBOL_DEF_USER;
+ /* clear VALID to get value calculated */
+ sym->flags &= ~SYMBOL_VALID;
}
csym->flags |= SYMBOL_DEF_USER;
/* clear VALID to get value calculated */
csym->flags &= ~(SYMBOL_VALID);
+
+ return true;
}
-static void set_all_choice_values(struct symbol *csym)
+void set_all_choice_values(struct symbol *csym)
{
struct property *prop;
struct symbol *sym;
@@ -1100,10 +1105,10 @@
}
csym->flags |= SYMBOL_DEF_USER;
/* clear VALID to get value calculated */
- csym->flags &= ~(SYMBOL_VALID);
+ csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES);
}
-void conf_set_all_new_symbols(enum conf_def_mode mode)
+bool conf_set_all_new_symbols(enum conf_def_mode mode)
{
struct symbol *sym, *csym;
int i, cnt, pby, pty, ptm; /* pby: probability of boolean = y
@@ -1151,6 +1156,7 @@
exit( 1 );
}
}
+ bool has_changed = false;
for_all_symbols(i, sym) {
if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID))
@@ -1158,6 +1164,7 @@
switch (sym_get_type(sym)) {
case S_BOOLEAN:
case S_TRISTATE:
+ has_changed = true;
switch (mode) {
case def_yes:
sym->def[S_DEF_USER].tri = yes;
@@ -1202,14 +1209,26 @@
* selected in a choice block and we set it to yes,
* and the rest to no.
*/
+ if (mode != def_random) {
+ for_all_symbols(i, csym) {
+ if ((sym_is_choice(csym) && !sym_has_value(csym)) ||
+ sym_is_choice_value(csym))
+ csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES;
+ }
+ }
+
for_all_symbols(i, csym) {
if (sym_has_value(csym) || !sym_is_choice(csym))
continue;
sym_calc_value(csym);
if (mode == def_random)
- randomize_choice_values(csym);
- else
+ has_changed = randomize_choice_values(csym);
+ else {
set_all_choice_values(csym);
+ has_changed = true;
+ }
}
+
+ return has_changed;
}
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index cdd4860..df198a5 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -106,6 +106,9 @@
#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */
#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */
+/* choice values need to be set before calculating this symbol value */
+#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000
+
#define SYMBOL_MAXLENGTH 256
#define SYMBOL_HASHSIZE 9973
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index f8aee5f..09f4edf 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -86,7 +86,8 @@
char *conf_get_default_confname(void);
void sym_set_change_count(int count);
void sym_add_change_count(int count);
-void conf_set_all_new_symbols(enum conf_def_mode mode);
+bool conf_set_all_new_symbols(enum conf_def_mode mode);
+void set_all_choice_values(struct symbol *csym);
struct conf_printer {
void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index ef1a738..ecdb965 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -14,6 +14,7 @@
/* menu.c */
P(rootmenu,struct menu,);
+P(menu_is_empty, bool, (struct menu *menu));
P(menu_is_visible, bool, (struct menu *menu));
P(menu_has_prompt, bool, (struct menu *menu));
P(menu_get_prompt,const char *,(struct menu *menu));
diff --git a/scripts/kconfig/lxdialog/checklist.c b/scripts/kconfig/lxdialog/checklist.c
index a2eb80f..3b15c08 100644
--- a/scripts/kconfig/lxdialog/checklist.c
+++ b/scripts/kconfig/lxdialog/checklist.c
@@ -132,16 +132,16 @@
}
do_resize:
- if (getmaxy(stdscr) < (height + 6))
+ if (getmaxy(stdscr) < (height + CHECKLIST_HEIGTH_MIN))
return -ERRDISPLAYTOOSMALL;
- if (getmaxx(stdscr) < (width + 6))
+ if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN))
return -ERRDISPLAYTOOSMALL;
max_choice = MIN(list_height, item_count());
/* center dialog box on screen */
- x = (COLS - width) / 2;
- y = (LINES - height) / 2;
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index 10993370..b4343d3 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -200,6 +200,20 @@
int on_key_esc(WINDOW *win);
int on_key_resize(void);
+/* minimum (re)size values */
+#define CHECKLIST_HEIGTH_MIN 6 /* For dialog_checklist() */
+#define CHECKLIST_WIDTH_MIN 6
+#define INPUTBOX_HEIGTH_MIN 2 /* For dialog_inputbox() */
+#define INPUTBOX_WIDTH_MIN 2
+#define MENUBOX_HEIGTH_MIN 15 /* For dialog_menu() */
+#define MENUBOX_WIDTH_MIN 65
+#define TEXTBOX_HEIGTH_MIN 8 /* For dialog_textbox() */
+#define TEXTBOX_WIDTH_MIN 8
+#define YESNO_HEIGTH_MIN 4 /* For dialog_yesno() */
+#define YESNO_WIDTH_MIN 4
+#define WINDOW_HEIGTH_MIN 19 /* For init_dialog() */
+#define WINDOW_WIDTH_MIN 80
+
int init_dialog(const char *backtitle);
void set_dialog_backtitle(const char *backtitle);
void set_dialog_subtitles(struct subtitle_list *subtitles);
diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c
index 21404a0..447a582 100644
--- a/scripts/kconfig/lxdialog/inputbox.c
+++ b/scripts/kconfig/lxdialog/inputbox.c
@@ -56,14 +56,14 @@
strcpy(instr, init);
do_resize:
- if (getmaxy(stdscr) <= (height - 2))
+ if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGTH_MIN))
return -ERRDISPLAYTOOSMALL;
- if (getmaxx(stdscr) <= (width - 2))
+ if (getmaxx(stdscr) <= (width - INPUTBOX_WIDTH_MIN))
return -ERRDISPLAYTOOSMALL;
/* center dialog box on screen */
- x = (COLS - width) / 2;
- y = (LINES - height) / 2;
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c
index 38cd69c..c93de0b 100644
--- a/scripts/kconfig/lxdialog/menubox.c
+++ b/scripts/kconfig/lxdialog/menubox.c
@@ -193,7 +193,7 @@
do_resize:
height = getmaxy(stdscr);
width = getmaxx(stdscr);
- if (height < 15 || width < 65)
+ if (height < MENUBOX_HEIGTH_MIN || width < MENUBOX_WIDTH_MIN)
return -ERRDISPLAYTOOSMALL;
height -= 4;
@@ -203,8 +203,8 @@
max_choice = MIN(menu_height, item_count());
/* center dialog box on screen */
- x = (COLS - width) / 2;
- y = (LINES - height) / 2;
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index a48bb93..1773319 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -80,7 +80,7 @@
do_resize:
getmaxyx(stdscr, height, width);
- if (height < 8 || width < 8)
+ if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN)
return -ERRDISPLAYTOOSMALL;
if (initial_height != 0)
height = initial_height;
@@ -98,8 +98,8 @@
width = 0;
/* center dialog box on screen */
- x = (COLS - width) / 2;
- y = (LINES - height) / 2;
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c
index a0e97c2..58a8289 100644
--- a/scripts/kconfig/lxdialog/util.c
+++ b/scripts/kconfig/lxdialog/util.c
@@ -254,7 +254,12 @@
void dialog_clear(void)
{
- attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
+ int lines, columns;
+
+ lines = getmaxy(stdscr);
+ columns = getmaxx(stdscr);
+
+ attr_clear(stdscr, lines, columns, dlg.screen.atr);
/* Display background title if it exists ... - SLH */
if (dlg.backtitle != NULL) {
int i, len = 0, skip = 0;
@@ -269,10 +274,10 @@
}
wmove(stdscr, 1, 1);
- if (len > COLS - 2) {
+ if (len > columns - 2) {
const char *ellipsis = "[...] ";
waddstr(stdscr, ellipsis);
- skip = len - (COLS - 2 - strlen(ellipsis));
+ skip = len - (columns - 2 - strlen(ellipsis));
}
for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
@@ -298,7 +303,7 @@
skip--;
}
- for (i = len + 1; i < COLS - 1; i++)
+ for (i = len + 1; i < columns - 1; i++)
waddch(stdscr, ACS_HLINE);
}
wnoutrefresh(stdscr);
@@ -317,7 +322,7 @@
getyx(stdscr, saved_y, saved_x);
getmaxyx(stdscr, height, width);
- if (height < 19 || width < 80) {
+ if (height < WINDOW_HEIGTH_MIN || width < WINDOW_WIDTH_MIN) {
endwin();
return -ERRDISPLAYTOOSMALL;
}
@@ -371,27 +376,19 @@
/*
* Print a string of text in a window, automatically wrap around to the
* next line if the string is too long to fit on one line. Newline
- * characters '\n' are replaced by spaces. We start on a new line
+ * characters '\n' are propperly processed. We start on a new line
* if there is no room for at least 4 nonblanks following a double-space.
*/
void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
{
int newl, cur_x, cur_y;
- int i, prompt_len, room, wlen;
- char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
+ int prompt_len, room, wlen;
+ char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0;
strcpy(tempstr, prompt);
prompt_len = strlen(tempstr);
- /*
- * Remove newlines
- */
- for (i = 0; i < prompt_len; i++) {
- if (tempstr[i] == '\n')
- tempstr[i] = ' ';
- }
-
if (prompt_len <= width - x * 2) { /* If prompt is short */
wmove(win, y, (width - prompt_len) / 2);
waddstr(win, tempstr);
@@ -401,7 +398,10 @@
newl = 1;
word = tempstr;
while (word && *word) {
- sp = strchr(word, ' ');
+ sp = strpbrk(word, "\n ");
+ if (sp && *sp == '\n')
+ newline_separator = sp;
+
if (sp)
*sp++ = 0;
@@ -413,7 +413,7 @@
if (wlen > room ||
(newl && wlen < 4 && sp
&& wlen + 1 + strlen(sp) > room
- && (!(sp2 = strchr(sp, ' '))
+ && (!(sp2 = strpbrk(sp, "\n "))
|| wlen + 1 + (sp2 - sp) > room))) {
cur_y++;
cur_x = x;
@@ -421,7 +421,15 @@
wmove(win, cur_y, cur_x);
waddstr(win, word);
getyx(win, cur_y, cur_x);
- cur_x++;
+
+ /* Move to the next line if the word separator was a newline */
+ if (newline_separator) {
+ cur_y++;
+ cur_x = x;
+ newline_separator = 0;
+ } else
+ cur_x++;
+
if (sp && *sp == ' ') {
cur_x++; /* double space */
while (*++sp == ' ') ;
diff --git a/scripts/kconfig/lxdialog/yesno.c b/scripts/kconfig/lxdialog/yesno.c
index 4e6e809..676fb2f8 100644
--- a/scripts/kconfig/lxdialog/yesno.c
+++ b/scripts/kconfig/lxdialog/yesno.c
@@ -45,14 +45,14 @@
WINDOW *dialog;
do_resize:
- if (getmaxy(stdscr) < (height + 4))
+ if (getmaxy(stdscr) < (height + YESNO_HEIGTH_MIN))
return -ERRDISPLAYTOOSMALL;
- if (getmaxx(stdscr) < (width + 4))
+ if (getmaxx(stdscr) < (width + YESNO_WIDTH_MIN))
return -ERRDISPLAYTOOSMALL;
/* center dialog box on screen */
- x = (COLS - width) / 2;
- y = (LINES - height) / 2;
+ x = (getmaxx(stdscr) - width) / 2;
+ y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index a69cbd7..6c9c45f 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -48,7 +48,7 @@
"----------\n"
"o Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
" you wish to change or submenu wish to select and press <Enter>.\n"
-" Submenus are designated by \"--->\".\n"
+" Submenus are designated by \"--->\", empty ones by \"----\".\n"
"\n"
" Shortcut: Press the option's highlighted letter (hotkey).\n"
" Pressing a hotkey more than once will sequence\n"
@@ -176,7 +176,7 @@
"\n"),
menu_instructions[] = N_(
"Arrow keys navigate the menu. "
- "<Enter> selects submenus --->. "
+ "<Enter> selects submenus ---> (or empty submenus ----). "
"Highlighted letters are hotkeys. "
"Pressing <Y> includes, <N> excludes, <M> modularizes features. "
"Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
@@ -401,7 +401,7 @@
struct subtitle_part stpart;
title = str_new();
- str_printf( &title, _("Enter %s (sub)string to search for "
+ str_printf( &title, _("Enter %s (sub)string or regexp to search for "
"(with or without \"%s\")"), CONFIG_, CONFIG_);
again:
@@ -498,8 +498,9 @@
menu->data ? "-->" : "++>",
indent + 1, ' ', prompt);
} else
- item_make(" %*c%s --->", indent + 1, ' ', prompt);
-
+ item_make(" %*c%s %s",
+ indent + 1, ' ', prompt,
+ menu_is_empty(menu) ? "----" : "--->");
item_set_tag('m');
item_set_data(menu);
if (single_menu_mode && menu->data)
@@ -630,7 +631,7 @@
(sym_has_value(sym) || !sym_is_changable(sym)) ?
"" : _(" (NEW)"));
if (menu->prompt->type == P_MENU) {
- item_add_str(" --->");
+ item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->");
return;
}
}
@@ -826,7 +827,9 @@
dialog_clear();
res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
_(radiolist_instructions),
- 15, 70, 6);
+ MENUBOX_HEIGTH_MIN,
+ MENUBOX_WIDTH_MIN,
+ CHECKLIST_HEIGTH_MIN);
selected = item_activate_selected();
switch (res) {
case 0:
@@ -957,8 +960,8 @@
dialog_clear();
if (conf_get_changed())
res = dialog_yesno(NULL,
- _("Do you wish to save your new configuration ?\n"
- "<ESC><ESC> to continue."),
+ _("Do you wish to save your new configuration?\n"
+ "(Press <ESC><ESC> to continue kernel configuration.)"),
6, 60);
else
res = -1;
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index fd3f018..7e233a6 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -443,6 +443,22 @@
return true;
}
+/*
+ * Determine if a menu is empty.
+ * A menu is considered empty if it contains no or only
+ * invisible entries.
+ */
+bool menu_is_empty(struct menu *menu)
+{
+ struct menu *child;
+
+ for (child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child))
+ return(false);
+ }
+ return(true);
+}
+
bool menu_is_visible(struct menu *menu)
{
struct menu *child;
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index dbf31ed..7975d8d 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -45,8 +45,8 @@
"<n> to remove it. You may press the <Space> key to cycle through the\n"
"available options.\n"
"\n"
-"A trailing \"--->\" designates a submenu.\n"
-"\n"
+"A trailing \"--->\" designates a submenu, a trailing \"----\" an\n"
+"empty submenu.\n"
"\n"
"Menu navigation keys\n"
"----------------------------------------------------------------------\n"
@@ -131,7 +131,7 @@
"\n"),
menu_no_f_instructions[] = N_(
"Legend: [*] built-in [ ] excluded <M> module < > module capable.\n"
-"Submenus are designated by a trailing \"--->\".\n"
+"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
"\n"
"Use the following keys to navigate the menus:\n"
"Move up or down with <Up> and <Down>.\n"
@@ -148,7 +148,7 @@
"For help related to the current menu entry press <?> or <h>.\n"),
menu_instructions[] = N_(
"Legend: [*] built-in [ ] excluded <M> module < > module capable.\n"
-"Submenus are designated by a trailing \"--->\".\n"
+"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
"\n"
"Use the following keys to navigate the menus:\n"
"Move up or down with <Up> or <Down>.\n"
@@ -365,15 +365,16 @@
int i;
int offset = 1;
const int skip = 1;
+ int lines = getmaxy(stdscr);
for (i = 0; i < function_keys_num; i++) {
(void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
- mvwprintw(main_window, LINES-3, offset,
+ mvwprintw(main_window, lines-3, offset,
"%s",
function_keys[i].key_str);
(void) wattrset(main_window, attributes[FUNCTION_TEXT]);
offset += strlen(function_keys[i].key_str);
- mvwprintw(main_window, LINES-3,
+ mvwprintw(main_window, lines-3,
offset, "%s",
function_keys[i].func);
offset += strlen(function_keys[i].func) + skip;
@@ -694,7 +695,7 @@
int dres;
title = str_new();
- str_printf( &title, _("Enter %s (sub)string to search for "
+ str_printf( &title, _("Enter %s (sub)string or regexp to search for "
"(with or without \"%s\")"), CONFIG_, CONFIG_);
again:
@@ -759,9 +760,9 @@
indent + 1, ' ', prompt);
} else
item_make(menu, 'm',
- " %*c%s --->",
- indent + 1,
- ' ', prompt);
+ " %*c%s %s",
+ indent + 1, ' ', prompt,
+ menu_is_empty(menu) ? "----" : "--->");
if (single_menu_mode && menu->data)
goto conf_childs;
@@ -903,7 +904,7 @@
(sym_has_value(sym) || !sym_is_changable(sym)) ?
"" : _(" (NEW)"));
if (menu->prompt && menu->prompt->type == P_MENU) {
- item_add_str(" --->");
+ item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->");
return;
}
}
@@ -954,7 +955,7 @@
clear();
(void) wattrset(main_window, attributes[NORMAL]);
- print_in_middle(stdscr, 1, 0, COLS,
+ print_in_middle(stdscr, 1, 0, getmaxx(stdscr),
menu_backtitle,
attributes[MAIN_HEADING]);
@@ -1455,14 +1456,18 @@
void setup_windows(void)
{
+ int lines, columns;
+
+ getmaxyx(stdscr, lines, columns);
+
if (main_window != NULL)
delwin(main_window);
/* set up the menu and menu window */
- main_window = newwin(LINES-2, COLS-2, 2, 1);
+ main_window = newwin(lines-2, columns-2, 2, 1);
keypad(main_window, TRUE);
- mwin_max_lines = LINES-7;
- mwin_max_cols = COLS-6;
+ mwin_max_lines = lines-7;
+ mwin_max_cols = columns-6;
/* panels order is from bottom to top */
new_panel(main_window);
@@ -1470,6 +1475,7 @@
int main(int ac, char **av)
{
+ int lines, columns;
char *mode;
setlocale(LC_ALL, "");
@@ -1495,7 +1501,8 @@
keypad(stdscr, TRUE);
curs_set(0);
- if (COLS < 75 || LINES < 20) {
+ getmaxyx(stdscr, lines, columns);
+ if (columns < 75 || lines < 20) {
endwin();
printf("Your terminal should have at "
"least 20 lines and 75 columns\n");
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 9f8c44e..8275f0e5 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -276,8 +276,8 @@
total_width = max(msg_width, btns_width);
/* place dialog in middle of screen */
- y = (LINES-(msg_lines+4))/2;
- x = (COLS-(total_width+4))/2;
+ y = (getmaxy(stdscr)-(msg_lines+4))/2;
+ x = (getmaxx(stdscr)-(total_width+4))/2;
/* create the windows */
@@ -387,8 +387,8 @@
prompt_width = max(prompt_width, strlen(title));
/* place dialog in middle of screen */
- y = (LINES-(prompt_lines+4))/2;
- x = (COLS-(prompt_width+4))/2;
+ y = (getmaxy(stdscr)-(prompt_lines+4))/2;
+ x = (getmaxx(stdscr)-(prompt_width+4))/2;
strncpy(result, init, *result_len);
@@ -545,7 +545,7 @@
{
int res;
int total_lines = get_line_no(text);
- int x, y;
+ int x, y, lines, columns;
int start_x = 0, start_y = 0;
int text_lines = 0, text_cols = 0;
int total_cols = 0;
@@ -556,6 +556,8 @@
WINDOW *pad;
PANEL *panel;
+ getmaxyx(stdscr, lines, columns);
+
/* find the widest line of msg: */
total_lines = get_line_no(text);
for (i = 0; i < total_lines; i++) {
@@ -569,14 +571,14 @@
(void) wattrset(pad, attributes[SCROLLWIN_TEXT]);
fill_window(pad, text);
- win_lines = min(total_lines+4, LINES-2);
- win_cols = min(total_cols+2, COLS-2);
+ win_lines = min(total_lines+4, lines-2);
+ win_cols = min(total_cols+2, columns-2);
text_lines = max(win_lines-4, 0);
text_cols = max(win_cols-2, 0);
/* place window in middle of screen */
- y = (LINES-win_lines)/2;
- x = (COLS-win_cols)/2;
+ y = (lines-win_lines)/2;
+ x = (columns-win_cols)/2;
win = newwin(win_lines, win_cols, y, x);
keypad(win, TRUE);
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index ecc5aa5..d550300 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -136,7 +136,7 @@
return NULL;
}
-static int sym_get_range_val(struct symbol *sym, int base)
+static long sym_get_range_val(struct symbol *sym, int base)
{
sym_calc_value(sym);
switch (sym->type) {
@@ -155,7 +155,7 @@
static void sym_validate_range(struct symbol *sym)
{
struct property *prop;
- int base, val, val2;
+ long base, val, val2;
char str[64];
switch (sym->type) {
@@ -179,9 +179,9 @@
return;
}
if (sym->type == S_INT)
- sprintf(str, "%d", val2);
+ sprintf(str, "%ld", val2);
else
- sprintf(str, "0x%x", val2);
+ sprintf(str, "0x%lx", val2);
sym->curr.val = strdup(str);
}
@@ -300,6 +300,14 @@
if (sym->flags & SYMBOL_VALID)
return;
+
+ if (sym_is_choice_value(sym) &&
+ sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) {
+ sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES;
+ prop = sym_get_choice_prop(sym);
+ sym_calc_value(prop_get_symbol(prop));
+ }
+
sym->flags |= SYMBOL_VALID;
oldval = sym->curr;
@@ -425,6 +433,9 @@
if (sym->flags & SYMBOL_AUTO)
sym->flags &= ~SYMBOL_WRITE;
+
+ if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
+ set_all_choice_values(sym);
}
void sym_clear_all_valid(void)
@@ -583,7 +594,7 @@
bool sym_string_within_range(struct symbol *sym, const char *str)
{
struct property *prop;
- int val;
+ long val;
switch (sym->type) {
case S_STRING:
@@ -943,38 +954,98 @@
return res;
}
+struct sym_match {
+ struct symbol *sym;
+ off_t so, eo;
+};
+
+/* Compare matched symbols as thus:
+ * - first, symbols that match exactly
+ * - then, alphabetical sort
+ */
+static int sym_rel_comp( const void *sym1, const void *sym2 )
+{
+ struct sym_match *s1 = *(struct sym_match **)sym1;
+ struct sym_match *s2 = *(struct sym_match **)sym2;
+ int l1, l2;
+
+ /* Exact match:
+ * - if matched length on symbol s1 is the length of that symbol,
+ * then this symbol should come first;
+ * - if matched length on symbol s2 is the length of that symbol,
+ * then this symbol should come first.
+ * Note: since the search can be a regexp, both symbols may match
+ * exactly; if this is the case, we can't decide which comes first,
+ * and we fallback to sorting alphabetically.
+ */
+ l1 = s1->eo - s1->so;
+ l2 = s2->eo - s2->so;
+ if (l1 == strlen(s1->sym->name) && l2 != strlen(s2->sym->name))
+ return -1;
+ if (l1 != strlen(s1->sym->name) && l2 == strlen(s2->sym->name))
+ return 1;
+
+ /* As a fallback, sort symbols alphabetically */
+ return strcmp(s1->sym->name, s2->sym->name);
+}
+
struct symbol **sym_re_search(const char *pattern)
{
struct symbol *sym, **sym_arr = NULL;
+ struct sym_match **sym_match_arr = NULL;
int i, cnt, size;
regex_t re;
+ regmatch_t match[1];
cnt = size = 0;
/* Skip if empty */
if (strlen(pattern) == 0)
return NULL;
- if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE))
+ if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE))
return NULL;
for_all_symbols(i, sym) {
+ struct sym_match *tmp_sym_match;
if (sym->flags & SYMBOL_CONST || !sym->name)
continue;
- if (regexec(&re, sym->name, 0, NULL, 0))
+ if (regexec(&re, sym->name, 1, match, 0))
continue;
if (cnt + 1 >= size) {
- void *tmp = sym_arr;
+ void *tmp;
size += 16;
- sym_arr = realloc(sym_arr, size * sizeof(struct symbol *));
- if (!sym_arr) {
- free(tmp);
- return NULL;
+ tmp = realloc(sym_match_arr, size * sizeof(struct sym_match *));
+ if (!tmp) {
+ goto sym_re_search_free;
}
+ sym_match_arr = tmp;
}
sym_calc_value(sym);
- sym_arr[cnt++] = sym;
+ tmp_sym_match = (struct sym_match*)malloc(sizeof(struct sym_match));
+ if (!tmp_sym_match)
+ goto sym_re_search_free;
+ tmp_sym_match->sym = sym;
+ /* As regexec return 0, we know we have a match, so
+ * we can use match[0].rm_[se]o without further checks
+ */
+ tmp_sym_match->so = match[0].rm_so;
+ tmp_sym_match->eo = match[0].rm_eo;
+ sym_match_arr[cnt++] = tmp_sym_match;
}
- if (sym_arr)
+ if (sym_match_arr) {
+ qsort(sym_match_arr, cnt, sizeof(struct sym_match*), sym_rel_comp);
+ sym_arr = malloc((cnt+1) * sizeof(struct symbol));
+ if (!sym_arr)
+ goto sym_re_search_free;
+ for (i = 0; i < cnt; i++)
+ sym_arr[i] = sym_match_arr[i]->sym;
sym_arr[cnt] = NULL;
+ }
+sym_re_search_free:
+ if (sym_match_arr) {
+ for (i = 0; i < cnt; i++)
+ free(sym_match_arr[i]);
+ free(sym_match_arr);
+ }
regfree(&re);
return sym_arr;
diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index 75d59fc..c11212f 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -15,8 +15,8 @@
quiet_cmd_offsets = GEN $@
define cmd_offsets
(set -e; \
- echo "#ifndef __DEVICEVTABLE_OFFSETS_H__"; \
- echo "#define __DEVICEVTABLE_OFFSETS_H__"; \
+ echo "#ifndef __DEVICETABLE_OFFSETS_H__"; \
+ echo "#define __DEVICETABLE_OFFSETS_H__"; \
echo "/*"; \
echo " * DO NOT MODIFY."; \
echo " *"; \
@@ -29,15 +29,10 @@
echo "#endif" ) > $@
endef
-# We use internal kbuild rules to avoid the "is up to date" message from make
-scripts/mod/devicetable-offsets.s: scripts/mod/devicetable-offsets.c FORCE
- $(Q)mkdir -p $(dir $@)
- $(call if_changed_dep,cc_s_c)
+$(obj)/$(devicetable-offsets-file): $(obj)/devicetable-offsets.s
+ $(call if_changed,offsets)
-$(obj)/$(devicetable-offsets-file): scripts/mod/devicetable-offsets.s
- $(call cmd,offsets)
-
-targets += $(devicetable-offsets-file)
+targets += $(devicetable-offsets-file) devicetable-offsets.s
# dependencies on generated files need to be listed explicitly
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index d9e67b7..2370863 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -79,10 +79,12 @@
extern struct devtable *__start___devtable[], *__stop___devtable[];
#endif /* __MACH__ */
-#if __GNUC__ == 3 && __GNUC_MINOR__ < 3
-# define __used __attribute__((__unused__))
-#else
-# define __used __attribute__((__used__))
+#if !defined(__used)
+# if __GNUC__ == 3 && __GNUC_MINOR__ < 3
+# define __used __attribute__((__unused__))
+# else
+# define __used __attribute__((__used__))
+# endif
#endif
/* Define a variable f that holds the value of field f of struct devid
diff --git a/scripts/package/mkspec b/scripts/package/mkspec
index fbbfd08..fdd3fbf 100755
--- a/scripts/package/mkspec
+++ b/scripts/package/mkspec
@@ -74,6 +74,7 @@
fi
echo "%install"
+echo 'KBUILD_IMAGE=$(make image_name)'
echo "%ifarch ia64"
echo 'mkdir -p $RPM_BUILD_ROOT/boot/efi $RPM_BUILD_ROOT/lib/modules'
echo 'mkdir -p $RPM_BUILD_ROOT/lib/firmware'
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 84b88f1..d105a44 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -71,9 +71,6 @@
printf -- '-svn%s' "`git svn find-rev $head`"
fi
- # Update index only on r/w media
- [ -w . ] && git update-index --refresh --unmerged > /dev/null
-
# Check for uncommitted changes
if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then
printf '%s' -dirty
diff --git a/security/capability.c b/security/capability.c
index d32e16e..32b5157 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -858,7 +858,7 @@
static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
{
- return 0;
+ return -EOPNOTSUPP;
}
#ifdef CONFIG_KEYS
static int cap_key_alloc(struct key *key, const struct cred *cred,
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 977b0d8..d97f0d6 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -2112,6 +2112,9 @@
{
struct hda_codec *codec = private_data;
struct ad198x_spec *spec = codec->spec;
+
+ if (!spec->eapd_nid)
+ return;
snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
AC_VERB_SET_EAPD_BTLENABLE,
enabled ? 0x02 : 0x00);
@@ -3601,13 +3604,16 @@
{
struct ad198x_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
else
spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
- if (spec->eapd_nid)
- spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ break;
}
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 14ac9b0..8bd2261 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -37,6 +37,9 @@
#include "hda_jack.h"
#include "hda_generic.h"
+/* keep halting ALC5505 DSP, for power saving */
+#define HALT_REALTEK_ALC5505
+
/* unsol event tags */
#define ALC_DCVOL_EVENT 0x08
@@ -2659,15 +2662,27 @@
alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
alc5505_coef_set(codec, 0x880c, 0x00000003);
alc5505_coef_set(codec, 0x880c, 0x00000010);
+
+#ifdef HALT_REALTEK_ALC5505
+ alc5505_dsp_halt(codec);
+#endif
}
+#ifdef HALT_REALTEK_ALC5505
+#define alc5505_dsp_suspend(codec) /* NOP */
+#define alc5505_dsp_resume(codec) /* NOP */
+#else
+#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
+#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
+#endif
+
#ifdef CONFIG_PM
static int alc269_suspend(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
if (spec->has_alc5505_dsp)
- alc5505_dsp_halt(codec);
+ alc5505_dsp_suspend(codec);
return alc_suspend(codec);
}
@@ -2696,7 +2711,7 @@
alc_inv_dmic_sync(codec, true);
hda_call_check_power_status(codec, 0x01);
if (spec->has_alc5505_dsp)
- alc5505_dsp_back_from_halt(codec);
+ alc5505_dsp_resume(codec);
return 0;
}
#endif /* CONFIG_PM */
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index b1dc7d4..e2de9ec 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3377,7 +3377,7 @@
{
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
+ struct wm8962_pdata *pdata = &wm8962->pdata;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 7a8bc12..3f726e4 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -113,13 +113,13 @@
ssi_pdev = of_find_device_by_node(ssi_np);
if (!ssi_pdev) {
dev_err(&pdev->dev, "failed to find SSI platform device\n");
- ret = -EINVAL;
+ ret = -EPROBE_DEFER;
goto fail;
}
codec_dev = of_find_i2c_device_by_node(codec_np);
if (!codec_dev) {
dev_err(&pdev->dev, "failed to find codec platform device\n");
- return -EINVAL;
+ return -EPROBE_DEFER;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index 49d8700..54511c5 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <sound/core.h>
@@ -658,6 +659,33 @@
return IRQ_HANDLED;
}
+static int mxs_saif_mclk_init(struct platform_device *pdev)
+{
+ struct mxs_saif *saif = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct clk *clk;
+ int ret;
+
+ clk = clk_register_divider(&pdev->dev, "mxs_saif_mclk",
+ __clk_get_name(saif->clk), 0,
+ saif->base + SAIF_CTRL,
+ BP_SAIF_CTRL_BITCLK_MULT_RATE, 3,
+ 0, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ if (ret == -EEXIST)
+ return 0;
+ dev_err(&pdev->dev, "failed to register mclk: %d\n", ret);
+ return PTR_ERR(clk);
+ }
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int mxs_saif_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -734,6 +762,13 @@
platform_set_drvdata(pdev, saif);
+ /* We only support saif0 being tx and clock master */
+ if (saif->id == 0) {
+ ret = mxs_saif_mclk_init(pdev);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to init clocks\n");
+ }
+
ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component,
&mxs_saif_dai, 1);
if (ret) {
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 249cd23..611179c 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -396,7 +396,7 @@
{
int err;
- if (!machine_is_nokia_rx51())
+ if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900"))
return -ENODEV;
err = gpio_request_one(RX51_TVOUT_SEL_GPIO,
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 82ebb1a..7a17346 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1016,52 +1016,6 @@
return i2s;
}
-#ifdef CONFIG_OF
-static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s)
-{
- struct device *dev = &i2s->pdev->dev;
- int index, gpio, ret;
-
- for (index = 0; index < 7; index++) {
- gpio = of_get_gpio(dev->of_node, index);
- if (!gpio_is_valid(gpio)) {
- dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio);
- goto free_gpio;
- }
-
- ret = gpio_request(gpio, dev_name(dev));
- if (ret) {
- dev_err(dev, "gpio [%d] request failed\n", gpio);
- goto free_gpio;
- }
- i2s->gpios[index] = gpio;
- }
- return 0;
-
-free_gpio:
- while (--index >= 0)
- gpio_free(i2s->gpios[index]);
- return -EINVAL;
-}
-
-static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s)
-{
- unsigned int index;
- for (index = 0; index < 7; index++)
- gpio_free(i2s->gpios[index]);
-}
-#else
-static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai)
-{
- return -EINVAL;
-}
-
-static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai)
-{
-}
-
-#endif
-
static const struct of_device_id exynos_i2s_match[];
static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
@@ -1235,18 +1189,10 @@
pri_dai->sec_dai = sec_dai;
}
- if (np) {
- if (samsung_i2s_parse_dt_gpio(pri_dai)) {
- dev_err(&pdev->dev, "Unable to configure gpio\n");
- ret = -EINVAL;
- goto err;
- }
- } else {
- if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
- dev_err(&pdev->dev, "Unable to configure gpio\n");
- ret = -EINVAL;
- goto err;
- }
+ if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
+ dev_err(&pdev->dev, "Unable to configure gpio\n");
+ ret = -EINVAL;
+ goto err;
}
snd_soc_register_component(&pri_dai->pdev->dev, &samsung_i2s_component,
@@ -1267,14 +1213,10 @@
{
struct i2s_dai *i2s, *other;
struct resource *res;
- struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
i2s = dev_get_drvdata(&pdev->dev);
other = i2s->pri_dai ? : i2s->sec_dai;
- if (!i2s_pdata->cfg_gpio && pdev->dev.of_node)
- samsung_i2s_dt_gpio_free(i2s->pri_dai);
-
if (other) {
other->pri_dai = NULL;
other->sec_dai = NULL;
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index 20e98d1..e5e81b1 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -1,6 +1,4 @@
-/* sound/soc/samsung/s3c-i2c-v2.c
- *
- * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
+/* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
*
* Copyright (c) 2006 Wolfson Microelectronics PLC.
* Graeme Gregory graeme.gregory@wolfsonmicro.com
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 5b01330..1bc45e7 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -129,6 +129,7 @@
{
struct audioformat *fp;
struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
int stream, err;
unsigned *rate_table = NULL;
@@ -166,6 +167,9 @@
return -EINVAL;
}
alts = &iface->altsetting[fp->altset_idx];
+ altsd = get_iface_desc(alts);
+ fp->protocol = altsd->bInterfaceProtocol;
+
if (fp->datainterval == 0)
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
if (fp->maxpacksize == 0)
diff --git a/tools/include/tools/be_byteshift.h b/tools/include/tools/be_byteshift.h
index f4912e2..84c17d8 100644
--- a/tools/include/tools/be_byteshift.h
+++ b/tools/include/tools/be_byteshift.h
@@ -1,68 +1,68 @@
#ifndef _TOOLS_BE_BYTESHIFT_H
#define _TOOLS_BE_BYTESHIFT_H
-#include <linux/types.h>
+#include <stdint.h>
-static inline __u16 __get_unaligned_be16(const __u8 *p)
+static inline uint16_t __get_unaligned_be16(const uint8_t *p)
{
return p[0] << 8 | p[1];
}
-static inline __u32 __get_unaligned_be32(const __u8 *p)
+static inline uint32_t __get_unaligned_be32(const uint8_t *p)
{
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
-static inline __u64 __get_unaligned_be64(const __u8 *p)
+static inline uint64_t __get_unaligned_be64(const uint8_t *p)
{
- return (__u64)__get_unaligned_be32(p) << 32 |
+ return (uint64_t)__get_unaligned_be32(p) << 32 |
__get_unaligned_be32(p + 4);
}
-static inline void __put_unaligned_be16(__u16 val, __u8 *p)
+static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
{
*p++ = val >> 8;
*p++ = val;
}
-static inline void __put_unaligned_be32(__u32 val, __u8 *p)
+static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
{
__put_unaligned_be16(val >> 16, p);
__put_unaligned_be16(val, p + 2);
}
-static inline void __put_unaligned_be64(__u64 val, __u8 *p)
+static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
{
__put_unaligned_be32(val >> 32, p);
__put_unaligned_be32(val, p + 4);
}
-static inline __u16 get_unaligned_be16(const void *p)
+static inline uint16_t get_unaligned_be16(const void *p)
{
- return __get_unaligned_be16((const __u8 *)p);
+ return __get_unaligned_be16((const uint8_t *)p);
}
-static inline __u32 get_unaligned_be32(const void *p)
+static inline uint32_t get_unaligned_be32(const void *p)
{
- return __get_unaligned_be32((const __u8 *)p);
+ return __get_unaligned_be32((const uint8_t *)p);
}
-static inline __u64 get_unaligned_be64(const void *p)
+static inline uint64_t get_unaligned_be64(const void *p)
{
- return __get_unaligned_be64((const __u8 *)p);
+ return __get_unaligned_be64((const uint8_t *)p);
}
-static inline void put_unaligned_be16(__u16 val, void *p)
+static inline void put_unaligned_be16(uint16_t val, void *p)
{
__put_unaligned_be16(val, p);
}
-static inline void put_unaligned_be32(__u32 val, void *p)
+static inline void put_unaligned_be32(uint32_t val, void *p)
{
__put_unaligned_be32(val, p);
}
-static inline void put_unaligned_be64(__u64 val, void *p)
+static inline void put_unaligned_be64(uint64_t val, void *p)
{
__put_unaligned_be64(val, p);
}
diff --git a/tools/include/tools/le_byteshift.h b/tools/include/tools/le_byteshift.h
index c99d45a..8fe9f24 100644
--- a/tools/include/tools/le_byteshift.h
+++ b/tools/include/tools/le_byteshift.h
@@ -1,68 +1,68 @@
#ifndef _TOOLS_LE_BYTESHIFT_H
#define _TOOLS_LE_BYTESHIFT_H
-#include <linux/types.h>
+#include <stdint.h>
-static inline __u16 __get_unaligned_le16(const __u8 *p)
+static inline uint16_t __get_unaligned_le16(const uint8_t *p)
{
return p[0] | p[1] << 8;
}
-static inline __u32 __get_unaligned_le32(const __u8 *p)
+static inline uint32_t __get_unaligned_le32(const uint8_t *p)
{
return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
}
-static inline __u64 __get_unaligned_le64(const __u8 *p)
+static inline uint64_t __get_unaligned_le64(const uint8_t *p)
{
- return (__u64)__get_unaligned_le32(p + 4) << 32 |
+ return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
__get_unaligned_le32(p);
}
-static inline void __put_unaligned_le16(__u16 val, __u8 *p)
+static inline void __put_unaligned_le16(uint16_t val, uint8_t *p)
{
*p++ = val;
*p++ = val >> 8;
}
-static inline void __put_unaligned_le32(__u32 val, __u8 *p)
+static inline void __put_unaligned_le32(uint32_t val, uint8_t *p)
{
__put_unaligned_le16(val >> 16, p + 2);
__put_unaligned_le16(val, p);
}
-static inline void __put_unaligned_le64(__u64 val, __u8 *p)
+static inline void __put_unaligned_le64(uint64_t val, uint8_t *p)
{
__put_unaligned_le32(val >> 32, p + 4);
__put_unaligned_le32(val, p);
}
-static inline __u16 get_unaligned_le16(const void *p)
+static inline uint16_t get_unaligned_le16(const void *p)
{
- return __get_unaligned_le16((const __u8 *)p);
+ return __get_unaligned_le16((const uint8_t *)p);
}
-static inline __u32 get_unaligned_le32(const void *p)
+static inline uint32_t get_unaligned_le32(const void *p)
{
- return __get_unaligned_le32((const __u8 *)p);
+ return __get_unaligned_le32((const uint8_t *)p);
}
-static inline __u64 get_unaligned_le64(const void *p)
+static inline uint64_t get_unaligned_le64(const void *p)
{
- return __get_unaligned_le64((const __u8 *)p);
+ return __get_unaligned_le64((const uint8_t *)p);
}
-static inline void put_unaligned_le16(__u16 val, void *p)
+static inline void put_unaligned_le16(uint16_t val, void *p)
{
__put_unaligned_le16(val, p);
}
-static inline void put_unaligned_le32(__u32 val, void *p)
+static inline void put_unaligned_le32(uint32_t val, void *p)
{
__put_unaligned_le32(val, p);
}
-static inline void put_unaligned_le64(__u64 val, void *p)
+static inline void put_unaligned_le64(uint64_t val, void *p)
{
__put_unaligned_le64(val, p);
}
diff --git a/tools/lguest/Makefile b/tools/lguest/Makefile
index 0ac3420..97bca48 100644
--- a/tools/lguest/Makefile
+++ b/tools/lguest/Makefile
@@ -1,5 +1,4 @@
# This creates the demonstration utility "lguest" which runs a Linux guest.
-# Missing headers? Add "-I../../../include -I../../../arch/x86/include"
CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE
all: lguest
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c
index 07a0345..68f67cf 100644
--- a/tools/lguest/lguest.c
+++ b/tools/lguest/lguest.c
@@ -42,14 +42,6 @@
#include <pwd.h>
#include <grp.h>
-#include <linux/virtio_config.h>
-#include <linux/virtio_net.h>
-#include <linux/virtio_blk.h>
-#include <linux/virtio_console.h>
-#include <linux/virtio_rng.h>
-#include <linux/virtio_ring.h>
-#include <asm/bootparam.h>
-#include "../../include/linux/lguest_launcher.h"
/*L:110
* We can ignore the 43 include files we need for this program, but I do want
* to draw attention to the use of kernel-style types.
@@ -65,6 +57,15 @@
typedef uint8_t u8;
/*:*/
+#include <linux/virtio_config.h>
+#include <linux/virtio_net.h>
+#include <linux/virtio_blk.h>
+#include <linux/virtio_console.h>
+#include <linux/virtio_rng.h>
+#include <linux/virtio_ring.h>
+#include <asm/bootparam.h>
+#include "../../include/linux/lguest_launcher.h"
+
#define BRIDGE_PFX "bridge:"
#ifndef SIOCBRADDIF
#define SIOCBRADDIF 0x89a2 /* add interface to bridge */
@@ -177,7 +178,8 @@
* in precise order.
*/
#define wmb() __asm__ __volatile__("" : : : "memory")
-#define mb() __asm__ __volatile__("" : : : "memory")
+#define rmb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory")
+#define mb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory")
/* Wrapper for the last available index. Makes it easier to change. */
#define lg_last_avail(vq) ((vq)->last_avail_idx)
@@ -676,6 +678,12 @@
errx(1, "Guest moved used index from %u to %u",
last_avail, vq->vring.avail->idx);
+ /*
+ * Make sure we read the descriptor number *after* we read the ring
+ * update; don't let the cpu or compiler change the order.
+ */
+ rmb();
+
/*
* Grab the next descriptor number they're advertising, and increment
* the index we've seen.
@@ -695,6 +703,12 @@
i = head;
/*
+ * We have to read the descriptor after we read the descriptor number,
+ * but there's a data dependency there so the CPU shouldn't reorder
+ * that: no rmb() required.
+ */
+
+ /*
* If this is an indirect entry, then this buffer contains a descriptor
* table which we handle as if it's any normal descriptor chain.
*/
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile
index 2c5a197..280dd82 100644
--- a/tools/lib/lk/Makefile
+++ b/tools/lib/lk/Makefile
@@ -3,6 +3,21 @@
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
+# Makefiles suck: This macro sets a default value of $(2) for the
+# variable named by $(1), unless the variable has been set by
+# environment or command line. This is necessary for CC and AR
+# because make sets default values, so the simpler ?= approach
+# won't work as expected.
+define allow-override
+ $(if $(or $(findstring environment,$(origin $(1))),\
+ $(findstring command line,$(origin $(1)))),,\
+ $(eval $(1) = $(2)))
+endef
+
+# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
+$(call allow-override,CC,$(CROSS_COMPILE)gcc)
+$(call allow-override,AR,$(CROSS_COMPILE)ar)
+
# guard against environment variables
LIB_H=
LIB_OBJS=
@@ -14,7 +29,7 @@
LIBFILE = liblk.a
CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC
-EXTLIBS = -lpthread -lrt -lelf -lm
+EXTLIBS = -lelf -lpthread -lrt -lm
ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
ALL_LDFLAGS = $(LDFLAGS)
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index eb30044..5a37a7c 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -1,12 +1,6 @@
+include ../../scripts/Makefile.include
include ../config/utilities.mak
-OUTPUT := ./
-ifeq ("$(origin O)", "command line")
- ifneq ($(O),)
- OUTPUT := $(O)/
- endif
-endif
-
MAN1_TXT= \
$(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
$(wildcard perf-*.txt)) \
@@ -150,7 +144,7 @@
endif
ifneq ($(findstring $(MAKEFLAGS),s),s)
-ifndef V
+ifneq ($(V),1)
QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@;
QUIET_XMLTO = @echo ' ' XMLTO $@;
QUIET_DB2TEXI = @echo ' ' DB2TEXI $@;
@@ -277,7 +271,7 @@
$(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml
$(QUIET_XMLTO)$(RM) $@ && \
- $(XMLTO) -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
+ $(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
$(OUTPUT)%.xml : %.txt
$(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
diff --git a/tools/perf/Documentation/examples.txt b/tools/perf/Documentation/examples.txt
index 77f9527..a4e3921 100644
--- a/tools/perf/Documentation/examples.txt
+++ b/tools/perf/Documentation/examples.txt
@@ -66,7 +66,7 @@
well. For example the page allocations done by a 'git gc' can be
captured the following way:
- titan:~/git> perf record -f -e kmem:mm_page_alloc -c 1 ./git gc
+ titan:~/git> perf record -e kmem:mm_page_alloc -c 1 ./git gc
Counting objects: 1148, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (450/450), done.
@@ -120,7 +120,7 @@
allocations - to see precisely what kind of page allocations there
are:
- titan:~/git> perf record -f -g -e kmem:mm_page_alloc -c 1 ./git gc
+ titan:~/git> perf record -g -e kmem:mm_page_alloc -c 1 ./git gc
Counting objects: 1148, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (450/450), done.
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index d4da111..e297b74 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -65,16 +65,10 @@
-r::
--realtime=::
Collect data with this RT SCHED_FIFO priority.
+
-D::
--no-delay::
Collect data without buffering.
--A::
---append::
- Append to the output file to do incremental profiling.
-
--f::
---force::
- Overwrite existing data file. (deprecated)
-c::
--count=::
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 203cb0e..641fccd 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -121,17 +121,16 @@
grep-libs = $(filter -l%,$(1))
strip-libs = $(filter-out -l%,$(1))
-LK_PATH=$(LK_DIR)
-
ifneq ($(OUTPUT),)
TE_PATH=$(OUTPUT)
ifneq ($(subdir),)
- LK_PATH=$(OUTPUT)$(LK_DIR)
+ LK_PATH=$(objtree)/lib/lk/
else
LK_PATH=$(OUTPUT)
endif
else
TE_PATH=$(TRACE_EVENT_DIR)
+ LK_PATH=$(LK_DIR)
endif
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
index 93c83e3..25fd3f1 100644
--- a/tools/perf/bench/mem-memcpy.c
+++ b/tools/perf/bench/mem-memcpy.c
@@ -111,11 +111,11 @@
static void alloc_mem(void **dst, void **src, size_t length)
{
*dst = zalloc(length);
- if (!dst)
+ if (!*dst)
die("memory allocation failed - maybe length is too large?\n");
*src = zalloc(length);
- if (!src)
+ if (!*src)
die("memory allocation failed - maybe length is too large?\n");
}
diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c
index c6e4bc5..4a2f120 100644
--- a/tools/perf/bench/mem-memset.c
+++ b/tools/perf/bench/mem-memset.c
@@ -111,7 +111,7 @@
static void alloc_mem(void **dst, size_t length)
{
*dst = zalloc(length);
- if (!dst)
+ if (!*dst)
die("memory allocation failed - maybe length is too large?\n");
}
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index da8f8eb..0aac5f3 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -607,7 +607,6 @@
input_new = "perf.data.guest";
}
- symbol_conf.exclude_other = false;
if (symbol__init() < 0)
return -1;
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 46878da..0259502 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -708,7 +708,7 @@
static int __cmd_record(int argc, const char **argv)
{
const char * const record_args[] = {
- "record", "-a", "-R", "-f", "-c", "1",
+ "record", "-a", "-R", "-c", "1",
"-e", "kmem:kmalloc",
"-e", "kmem:kmalloc_node",
"-e", "kmem:kfree",
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 4258300..76543a4 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -878,7 +878,7 @@
static int __cmd_record(int argc, const char **argv)
{
const char *record_args[] = {
- "record", "-R", "-f", "-m", "1024", "-c", "1",
+ "record", "-R", "-m", "1024", "-c", "1",
};
unsigned int rec_argc, i, j;
const char **rec_argv;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index fff985c..ecca62e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -61,11 +61,6 @@
}
#endif
-enum write_mode_t {
- WRITE_FORCE,
- WRITE_APPEND
-};
-
struct perf_record {
struct perf_tool tool;
struct perf_record_opts opts;
@@ -77,12 +72,8 @@
int output;
unsigned int page_size;
int realtime_prio;
- enum write_mode_t write_mode;
bool no_buildid;
bool no_buildid_cache;
- bool force;
- bool file_new;
- bool append_file;
long samples;
off_t post_processing_offset;
};
@@ -200,25 +191,6 @@
signal(signr, SIG_DFL);
}
-static bool perf_evlist__equal(struct perf_evlist *evlist,
- struct perf_evlist *other)
-{
- struct perf_evsel *pos, *pair;
-
- if (evlist->nr_entries != other->nr_entries)
- return false;
-
- pair = perf_evlist__first(other);
-
- list_for_each_entry(pos, &evlist->entries, node) {
- if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0))
- return false;
- pair = perf_evsel__next(pair);
- }
-
- return true;
-}
-
static int perf_record__open(struct perf_record *rec)
{
char msg[512];
@@ -273,16 +245,7 @@
goto out;
}
- if (rec->file_new)
- session->evlist = evlist;
- else {
- if (!perf_evlist__equal(session->evlist, evlist)) {
- fprintf(stderr, "incompatible append\n");
- rc = -1;
- goto out;
- }
- }
-
+ session->evlist = evlist;
perf_session__set_id_hdr_size(session);
out:
return rc;
@@ -415,23 +378,15 @@
if (!strcmp(output_name, "-"))
opts->pipe_output = true;
else if (!stat(output_name, &st) && st.st_size) {
- if (rec->write_mode == WRITE_FORCE) {
- char oldname[PATH_MAX];
- snprintf(oldname, sizeof(oldname), "%s.old",
- output_name);
- unlink(oldname);
- rename(output_name, oldname);
- }
- } else if (rec->write_mode == WRITE_APPEND) {
- rec->write_mode = WRITE_FORCE;
+ char oldname[PATH_MAX];
+ snprintf(oldname, sizeof(oldname), "%s.old",
+ output_name);
+ unlink(oldname);
+ rename(output_name, oldname);
}
}
- flags = O_CREAT|O_RDWR;
- if (rec->write_mode == WRITE_APPEND)
- rec->file_new = 0;
- else
- flags |= O_TRUNC;
+ flags = O_CREAT|O_RDWR|O_TRUNC;
if (opts->pipe_output)
output = STDOUT_FILENO;
@@ -445,7 +400,7 @@
rec->output = output;
session = perf_session__new(output_name, O_WRONLY,
- rec->write_mode == WRITE_FORCE, false, NULL);
+ true, false, NULL);
if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n");
return -1;
@@ -465,12 +420,6 @@
if (!rec->opts.branch_stack)
perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
- if (!rec->file_new) {
- err = perf_session__read_header(session, output);
- if (err < 0)
- goto out_delete_session;
- }
-
if (forks) {
err = perf_evlist__prepare_workload(evsel_list, &opts->target,
argv, opts->pipe_output,
@@ -498,7 +447,7 @@
err = perf_header__write_pipe(output);
if (err < 0)
goto out_delete_session;
- } else if (rec->file_new) {
+ } else {
err = perf_session__write_header(session, evsel_list,
output, false);
if (err < 0)
@@ -869,8 +818,6 @@
.uses_mmap = true,
},
},
- .write_mode = WRITE_FORCE,
- .file_new = true,
};
#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
@@ -906,12 +853,8 @@
"collect raw sample records from all opened counters"),
OPT_BOOLEAN('a', "all-cpus", &record.opts.target.system_wide,
"system-wide collection from all CPUs"),
- OPT_BOOLEAN('A', "append", &record.append_file,
- "append to the output file to do incremental profiling"),
OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu",
"list of cpus to monitor"),
- OPT_BOOLEAN('f', "force", &record.force,
- "overwrite existing data file (deprecated)"),
OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
OPT_STRING('o', "output", &record.output_name, "file",
"output file name"),
@@ -977,16 +920,6 @@
if (!argc && perf_target__none(&rec->opts.target))
usage_with_options(record_usage, record_options);
- if (rec->force && rec->append_file) {
- ui__error("Can't overwrite and append at the same time."
- " You need to choose between -f and -A");
- usage_with_options(record_usage, record_options);
- } else if (rec->append_file) {
- rec->write_mode = WRITE_APPEND;
- } else {
- rec->write_mode = WRITE_FORCE;
- }
-
if (nr_cgroups && !rec->opts.target.system_wide) {
ui__error("cgroup monitoring only available in"
" system-wide mode\n");
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index ca98d34..3662047 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -939,8 +939,7 @@
*/
if (!strstr(sort_order, "parent"))
sort_parent.elide = 1;
- } else
- symbol_conf.exclude_other = false;
+ }
if (argc) {
/*
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 2da2a6c..fed9ae4 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1632,7 +1632,6 @@
"record",
"-a",
"-R",
- "-f",
"-m", "1024",
"-c", "1",
"-e", "sched:sched_switch",
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 7e910ba..352fbd7 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -87,7 +87,7 @@
static bool no_inherit = false;
static bool scale = true;
static enum aggr_mode aggr_mode = AGGR_GLOBAL;
-static pid_t child_pid = -1;
+static volatile pid_t child_pid = -1;
static bool null_run = false;
static int detailed_run = 0;
static bool big_num = true;
@@ -924,7 +924,7 @@
static void print_aggr(char *prefix)
{
struct perf_evsel *counter;
- int cpu, s, s2, id, nr;
+ int cpu, cpu2, s, s2, id, nr;
u64 ena, run, val;
if (!(aggr_map || aggr_get_id))
@@ -936,7 +936,8 @@
val = ena = run = 0;
nr = 0;
for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
- s2 = aggr_get_id(evsel_list->cpus, cpu);
+ cpu2 = perf_evsel__cpus(counter)->map[cpu];
+ s2 = aggr_get_id(evsel_list->cpus, cpu2);
if (s2 != id)
continue;
val += counter->counts->cpu[cpu].val;
@@ -948,7 +949,7 @@
fprintf(output, "%s", prefix);
if (run == 0 || ena == 0) {
- aggr_printout(counter, cpu, nr);
+ aggr_printout(counter, id, nr);
fprintf(output, "%*s%s%*s",
csv_output ? 0 : 18,
@@ -1148,13 +1149,34 @@
done = 1;
signr = signo;
+ /*
+ * render child_pid harmless
+ * won't send SIGTERM to a random
+ * process in case of race condition
+ * and fast PID recycling
+ */
+ child_pid = -1;
}
static void sig_atexit(void)
{
+ sigset_t set, oset;
+
+ /*
+ * avoid race condition with SIGCHLD handler
+ * in skip_signal() which is modifying child_pid
+ * goal is to avoid send SIGTERM to a random
+ * process
+ */
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &set, &oset);
+
if (child_pid != -1)
kill(child_pid, SIGTERM);
+ sigprocmask(SIG_SETMASK, &oset, NULL);
+
if (signr == -1)
return;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index ab4cf232..4536a92 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -1005,7 +1005,7 @@
{
#ifdef SUPPORT_OLD_POWER_EVENTS
const char * const record_old_args[] = {
- "record", "-a", "-R", "-f", "-c", "1",
+ "record", "-a", "-R", "-c", "1",
"-e", "power:power_start",
"-e", "power:power_end",
"-e", "power:power_frequency",
@@ -1014,7 +1014,7 @@
};
#endif
const char * const record_new_args[] = {
- "record", "-a", "-R", "-f", "-c", "1",
+ "record", "-a", "-R", "-c", "1",
"-e", "power:cpu_frequency",
"-e", "power:cpu_idle",
"-e", "sched:sched_wakeup",
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index f036af9b..e06c4f8 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1130,8 +1130,6 @@
if (top.evlist == NULL)
return -ENOMEM;
- symbol_conf.exclude_other = false;
-
argc = parse_options(argc, argv, options, top_usage, 0);
if (argc)
usage_with_options(top_usage, options);
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index f139dcd..b5d9238 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -39,7 +39,7 @@
endif
ifeq ($(obj-perf),)
-obj-perf := $(objtree)
+obj-perf := $(OUTPUT)
endif
ifneq ($(obj-perf),)
@@ -85,7 +85,7 @@
CFLAGS += -Wextra
CFLAGS += -std=gnu99
-EXTLIBS = -lpthread -lrt -lelf -lm
+EXTLIBS = -lelf -lpthread -lrt -lm
ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y)
CFLAGS += -fstack-protector-all
@@ -165,7 +165,7 @@
LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib
endif
- FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS)
+ FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lz -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS)
ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y)
msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
NO_DWARF := 1
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak
index 8ef3bd3..94d2d4f 100644
--- a/tools/perf/config/utilities.mak
+++ b/tools/perf/config/utilities.mak
@@ -173,7 +173,7 @@
# Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default)
#
define get-executable-or-default
-$(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2),$(1)))
+$(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2)))
endef
_ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2)))
_gea_warn = $(warning The path '$(1)' is not executable.)
@@ -181,7 +181,7 @@
# try-cc
# Usage: option = $(call try-cc, source-to-build, cc-options, msg)
-ifndef V
+ifneq ($(V),1)
TRY_CC_OUTPUT= > /dev/null 2>&1
endif
TRY_CC_MSG=echo " CHK $(3)" 1>&2;
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
index c1e2ed1..8c7ea42 100644
--- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
+++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs
@@ -23,7 +23,7 @@
#include "perl.h"
#include "XSUB.h"
#include "../../../perf.h"
-#include "../../../util/script-event.h"
+#include "../../../util/trace-event.h"
MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context
PROTOTYPES: ENABLE
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index 055fef3..15a77b7 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -13,13 +13,22 @@
# First check if there is a .git to get the version from git describe
# otherwise try to get the version from the kernel Makefile
#
-if test -d ../../.git -o -f ../../.git &&
- VN=$(git tag 2>/dev/null | tail -1 | grep -E "v[0-9].[0-9]*")
+CID=
+TAG=
+if test -d ../../.git -o -f ../../.git
then
- VN=$(echo $VN"-g"$(git log -1 --abbrev=4 --pretty=format:"%h" HEAD))
- VN=$(echo "$VN" | sed -e 's/-/./g');
-else
- VN=$(MAKEFLAGS= make -sC ../.. kernelversion)
+ TAG=$(git describe --abbrev=0 --match "v[0-9].[0-9]*" 2>/dev/null )
+ CID=$(git log -1 --abbrev=4 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID"
+fi
+if test -z "$TAG"
+then
+ TAG=$(MAKEFLAGS= make -sC ../.. kernelversion)
+fi
+VN="$TAG$CID"
+if test -n "$CID"
+then
+ # format version string, strip trailing zero of sublevel:
+ VN=$(echo "$VN" | sed -e 's/-/./g;s/\([0-9]*[.][0-9]*\)[.]0/\1/')
fi
VN=$(expr "$VN" : v*'\(.*\)')
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 6f7d5a9..c4374f0 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -513,10 +513,16 @@
list_add_tail(&dso->node, head);
}
-struct dso *dsos__find(struct list_head *head, const char *name)
+struct dso *dsos__find(struct list_head *head, const char *name, bool cmp_short)
{
struct dso *pos;
+ if (cmp_short) {
+ list_for_each_entry(pos, head, node)
+ if (strcmp(pos->short_name, name) == 0)
+ return pos;
+ return NULL;
+ }
list_for_each_entry(pos, head, node)
if (strcmp(pos->long_name, name) == 0)
return pos;
@@ -525,7 +531,7 @@
struct dso *__dsos__findnew(struct list_head *head, const char *name)
{
- struct dso *dso = dsos__find(head, name);
+ struct dso *dso = dsos__find(head, name, false);
if (!dso) {
dso = dso__new(name);
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 450199a..d51aaf2 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -133,7 +133,8 @@
const char *short_name, int dso_type);
void dsos__add(struct list_head *head, struct dso *dso);
-struct dso *dsos__find(struct list_head *head, const char *name);
+struct dso *dsos__find(struct list_head *head, const char *name,
+ bool cmp_short);
struct dso *__dsos__findnew(struct list_head *head, const char *name);
bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 99b43dd..8065ce8 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -821,6 +821,7 @@
goto out_close_pipes;
}
+ fcntl(go_pipe[1], F_SETFD, FD_CLOEXEC);
evlist->workload.cork_fd = go_pipe[1];
close(child_ready_pipe[0]);
return 0;
@@ -837,10 +838,17 @@
int perf_evlist__start_workload(struct perf_evlist *evlist)
{
if (evlist->workload.cork_fd > 0) {
+ char bf;
+ int ret;
/*
* Remove the cork, let it rip!
*/
- return close(evlist->workload.cork_fd);
+ ret = write(evlist->workload.cork_fd, &bf, 1);
+ if (ret < 0)
+ perror("enable to write to pipe");
+
+ close(evlist->workload.cork_fd);
+ return ret;
}
return 0;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 63b6f8c..c9c7494 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -124,7 +124,7 @@
bf = nbf;
}
- n = read(fd, bf + size, BUFSIZ);
+ n = read(fd, bf + size, alloc_size - size);
if (n < 0)
goto out_free_bf;
size += n;
@@ -1170,7 +1170,7 @@
} else {
data->user_stack.data = (char *)array;
array += size / sizeof(*array);
- data->user_stack.size = *array;
+ data->user_stack.size = *array++;
}
}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 738d3b8..a4dafbe 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -2303,29 +2303,18 @@
struct perf_file_header f_header;
struct perf_file_attr f_attr;
struct perf_header *header = &session->header;
- struct perf_evsel *evsel, *pair = NULL;
+ struct perf_evsel *evsel;
int err;
lseek(fd, sizeof(f_header), SEEK_SET);
- if (session->evlist != evlist)
- pair = perf_evlist__first(session->evlist);
-
list_for_each_entry(evsel, &evlist->entries, node) {
evsel->id_offset = lseek(fd, 0, SEEK_CUR);
err = do_write(fd, evsel->id, evsel->ids * sizeof(u64));
if (err < 0) {
-out_err_write:
pr_debug("failed to write perf header\n");
return err;
}
- if (session->evlist != evlist) {
- err = do_write(fd, pair->id, pair->ids * sizeof(u64));
- if (err < 0)
- goto out_err_write;
- evsel->ids += pair->ids;
- pair = perf_evsel__next(pair);
- }
}
header->attr_offset = lseek(fd, 0, SEEK_CUR);
@@ -2967,6 +2956,8 @@
perf_evlist__id_add(evlist, evsel, 0, i, event->attr.id[i]);
}
+ symbol_conf.nr_events = evlist->nr_entries;
+
return 0;
}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 6c8bb0f..995fc25 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -860,7 +860,8 @@
return 0;
}
- parse_events__free_terms(data.terms);
+ if (data.terms)
+ parse_events__free_terms(data.terms);
return ret;
}
@@ -1183,6 +1184,7 @@
term->val.str = str;
break;
default:
+ free(term);
return -EINVAL;
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8cf3b54..d5528e1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -32,7 +32,6 @@
char **vmlinux_path;
struct symbol_conf symbol_conf = {
- .exclude_other = true,
.use_modules = true,
.try_vmlinux_path = true,
.annotate_src = true,
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 7a484c9..2732fad 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -72,6 +72,7 @@
#include "types.h"
#include <sys/ttydefaults.h>
#include <lk/debugfs.h>
+#include <termios.h>
extern const char *graph_line;
extern const char *graph_dotted_line;
@@ -274,6 +275,5 @@
extern unsigned int page_size;
-struct winsize;
void get_term_dimensions(struct winsize *ws);
#endif /* GIT_COMPAT_UTIL_H */
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index e60951f..3915982 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -91,7 +91,7 @@
struct dso *vdso__dso_findnew(struct list_head *head)
{
- struct dso *dso = dsos__find(head, VDSO__MAP_NAME);
+ struct dso *dso = dsos__find(head, VDSO__MAP_NAME, true);
if (!dso) {
char *file;
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile
index d875a74..cbfec92 100644
--- a/tools/power/cpupower/Makefile
+++ b/tools/power/cpupower/Makefile
@@ -128,10 +128,12 @@
utils/helpers/sysfs.o utils/helpers/misc.o utils/helpers/cpuid.o \
utils/helpers/pci.o utils/helpers/bitmask.o \
utils/idle_monitor/nhm_idle.o utils/idle_monitor/snb_idle.o \
+ utils/idle_monitor/hsw_ext_idle.o \
utils/idle_monitor/amd_fam14h_idle.o utils/idle_monitor/cpuidle_sysfs.o \
utils/idle_monitor/mperf_monitor.o utils/idle_monitor/cpupower-monitor.o \
utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \
- utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o
+ utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o \
+ utils/cpuidle-set.o
UTIL_SRC := $(UTIL_OBJS:.o=.c)
diff --git a/tools/power/cpupower/man/cpupower-monitor.1 b/tools/power/cpupower/man/cpupower-monitor.1
index e01c35d..914cbb9 100644
--- a/tools/power/cpupower/man/cpupower-monitor.1
+++ b/tools/power/cpupower/man/cpupower-monitor.1
@@ -110,13 +110,21 @@
kernel frequency driver periodically cleared aperf/mperf registers in those
kernels.
-.SS "Nehalem" "SandyBridge"
+.SS "Nehalem" "SandyBridge" "HaswellExtended"
Intel Core and Package sleep state counters.
Threads (hyperthreaded cores) may not be able to enter deeper core states if
its sibling is utilized.
Deepest package sleep states may in reality show up as machine/platform wide
sleep states and can only be entered if all cores are idle. Look up Intel
manuals (some are provided in the References section) for further details.
+The monitors are named after the CPU family where the sleep state capabilities
+got introduced and may not match exactly the CPU name of the platform.
+For example an IvyBridge processor has sleep state capabilities which got
+introduced in Nehalem and SandyBridge processor families.
+Thus on an IvyBridge processor one will get Nehalem and SandyBridge sleep
+state monitors.
+HaswellExtended extra package sleep state capabilities are available only in a
+specific Haswell (family 0x45) and probably also other future processors.
.SS "Fam_12h" "Fam_14h"
AMD laptop and desktop processor (family 12h and 14h) sleep state counters.
diff --git a/tools/power/cpupower/utils/builtin.h b/tools/power/cpupower/utils/builtin.h
index c10496f..2284c8e 100644
--- a/tools/power/cpupower/utils/builtin.h
+++ b/tools/power/cpupower/utils/builtin.h
@@ -5,6 +5,7 @@
extern int cmd_info(int argc, const char **argv);
extern int cmd_freq_set(int argc, const char **argv);
extern int cmd_freq_info(int argc, const char **argv);
+extern int cmd_idle_set(int argc, const char **argv);
extern int cmd_idle_info(int argc, const char **argv);
extern int cmd_monitor(int argc, const char **argv);
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c
index 8145af5..75e66de 100644
--- a/tools/power/cpupower/utils/cpuidle-info.c
+++ b/tools/power/cpupower/utils/cpuidle-info.c
@@ -22,7 +22,7 @@
static void cpuidle_cpu_output(unsigned int cpu, int verbose)
{
- int idlestates, idlestate;
+ unsigned int idlestates, idlestate;
char *tmp;
printf(_ ("Analyzing CPU %d:\n"), cpu);
@@ -31,10 +31,8 @@
if (idlestates == 0) {
printf(_("CPU %u: No idle states\n"), cpu);
return;
- } else if (idlestates <= 0) {
- printf(_("CPU %u: Can't read idle state info\n"), cpu);
- return;
}
+
printf(_("Number of idle states: %d\n"), idlestates);
printf(_("Available idle states:"));
for (idlestate = 0; idlestate < idlestates; idlestate++) {
@@ -50,10 +48,14 @@
return;
for (idlestate = 0; idlestate < idlestates; idlestate++) {
+ int disabled = sysfs_is_idlestate_disabled(cpu, idlestate);
+ /* Disabled interface not supported on older kernels */
+ if (disabled < 0)
+ disabled = 0;
tmp = sysfs_get_idlestate_name(cpu, idlestate);
if (!tmp)
continue;
- printf("%s:\n", tmp);
+ printf("%s%s:\n", tmp, (disabled) ? " (DISABLED) " : "");
free(tmp);
tmp = sysfs_get_idlestate_desc(cpu, idlestate);
@@ -98,21 +100,13 @@
static void proc_cpuidle_cpu_output(unsigned int cpu)
{
long max_allowed_cstate = 2000000000;
- int cstates, cstate;
+ unsigned int cstate, cstates;
cstates = sysfs_get_idlestate_count(cpu);
if (cstates == 0) {
- /*
- * Go on and print same useless info as you'd see with
- * cat /proc/acpi/processor/../power
- * printf(_("CPU %u: No C-states available\n"), cpu);
- * return;
- */
- } else if (cstates <= 0) {
- printf(_("CPU %u: Can't read C-state info\n"), cpu);
+ printf(_("CPU %u: No C-states info\n"), cpu);
return;
}
- /* printf("Cstates: %d\n", cstates); */
printf(_("active state: C0\n"));
printf(_("max_cstate: C%u\n"), cstates-1);
diff --git a/tools/power/cpupower/utils/cpuidle-set.c b/tools/power/cpupower/utils/cpuidle-set.c
new file mode 100644
index 0000000..c78141c
--- /dev/null
+++ b/tools/power/cpupower/utils/cpuidle-set.c
@@ -0,0 +1,118 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <getopt.h>
+
+#include "cpufreq.h"
+#include "helpers/helpers.h"
+#include "helpers/sysfs.h"
+
+static struct option info_opts[] = {
+ { .name = "disable", .has_arg = required_argument, .flag = NULL, .val = 'd'},
+ { .name = "enable", .has_arg = required_argument, .flag = NULL, .val = 'e'},
+ { },
+};
+
+
+int cmd_idle_set(int argc, char **argv)
+{
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+ int ret = 0, cont = 1, param = 0, idlestate = 0;
+ unsigned int cpu = 0;
+
+ do {
+ ret = getopt_long(argc, argv, "d:e:", info_opts, NULL);
+ if (ret == -1)
+ break;
+ switch (ret) {
+ case '?':
+ param = '?';
+ cont = 0;
+ break;
+ case 'd':
+ if (param) {
+ param = -1;
+ cont = 0;
+ break;
+ }
+ param = ret;
+ idlestate = atoi(optarg);
+ break;
+ case 'e':
+ if (param) {
+ param = -1;
+ cont = 0;
+ break;
+ }
+ param = ret;
+ idlestate = atoi(optarg);
+ break;
+ case -1:
+ cont = 0;
+ break;
+ }
+ } while (cont);
+
+ switch (param) {
+ case -1:
+ printf(_("You can't specify more than one "
+ "output-specific argument\n"));
+ exit(EXIT_FAILURE);
+ case '?':
+ printf(_("invalid or unknown argument\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ /* Default is: set all CPUs */
+ if (bitmask_isallclear(cpus_chosen))
+ bitmask_setall(cpus_chosen);
+
+ for (cpu = bitmask_first(cpus_chosen);
+ cpu <= bitmask_last(cpus_chosen); cpu++) {
+
+ if (!bitmask_isbitset(cpus_chosen, cpu))
+ continue;
+
+ switch (param) {
+
+ case 'd':
+ ret = sysfs_idlestate_disable(cpu, idlestate, 1);
+ if (ret == 0)
+ printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu);
+ else if (ret == -1)
+ printf(_("Idlestate %u not available on CPU %u\n"),
+ idlestate, cpu);
+ else if (ret == -2)
+ printf(_("Idlestate disabling not supported by kernel\n"));
+ else
+ printf(_("Idlestate %u not disabled on CPU %u\n"),
+ idlestate, cpu);
+ break;
+ case 'e':
+ ret = sysfs_idlestate_disable(cpu, idlestate, 0);
+ if (ret == 0)
+ printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu);
+ else if (ret == -1)
+ printf(_("Idlestate %u not available on CPU %u\n"),
+ idlestate, cpu);
+ else if (ret == -2)
+ printf(_("Idlestate enabling not supported by kernel\n"));
+ else
+ printf(_("Idlestate %u not enabled on CPU %u\n"),
+ idlestate, cpu);
+ break;
+ default:
+ /* Not reachable with proper args checking */
+ printf(_("Invalid or unknown argument\n"));
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c
index 52bee59..7efc570 100644
--- a/tools/power/cpupower/utils/cpupower.c
+++ b/tools/power/cpupower/utils/cpupower.c
@@ -17,12 +17,6 @@
#include "helpers/helpers.h"
#include "helpers/bitmask.h"
-struct cmd_struct {
- const char *cmd;
- int (*main)(int, const char **);
- int needs_root;
-};
-
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static int cmd_help(int argc, const char **argv);
@@ -43,10 +37,17 @@
static void print_help(void);
+struct cmd_struct {
+ const char *cmd;
+ int (*main)(int, const char **);
+ int needs_root;
+};
+
static struct cmd_struct commands[] = {
{ "frequency-info", cmd_freq_info, 0 },
{ "frequency-set", cmd_freq_set, 1 },
{ "idle-info", cmd_idle_info, 0 },
+ { "idle-set", cmd_idle_set, 1 },
{ "set", cmd_set, 1 },
{ "info", cmd_info, 0 },
{ "monitor", cmd_monitor, 0 },
diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c
index 38ab916..5cdc600 100644
--- a/tools/power/cpupower/utils/helpers/sysfs.c
+++ b/tools/power/cpupower/utils/helpers/sysfs.c
@@ -89,6 +89,33 @@
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
+
+/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
+
+/*
+ * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
+ * exists.
+ * For example the functionality to disable c-states was introduced in later
+ * kernel versions, this function can be used to explicitly check for this
+ * feature.
+ *
+ * returns 1 if the file exists, 0 otherwise.
+ */
+unsigned int sysfs_idlestate_file_exists(unsigned int cpu,
+ unsigned int idlestate,
+ const char *fname)
+{
+ char path[SYSFS_PATH_MAX];
+ struct stat statbuf;
+
+
+ snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
+ cpu, idlestate, fname);
+ if (stat(path, &statbuf) != 0)
+ return 0;
+ return 1;
+}
+
/*
* helper function to read file from /sys into given buffer
* fname is a relative path under "cpuX/cpuidle/stateX/" dir
@@ -121,6 +148,40 @@
return (unsigned int) numread;
}
+/*
+ * helper function to write a new value to a /sys file
+ * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
+ *
+ * Returns the number of bytes written or 0 on error
+ */
+static
+unsigned int sysfs_idlestate_write_file(unsigned int cpu,
+ unsigned int idlestate,
+ const char *fname,
+ const char *value, size_t len)
+{
+ char path[SYSFS_PATH_MAX];
+ int fd;
+ ssize_t numwrite;
+
+ snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
+ cpu, idlestate, fname);
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return 0;
+
+ numwrite = write(fd, value, len);
+ if (numwrite < 1) {
+ close(fd);
+ return 0;
+ }
+
+ close(fd);
+
+ return (unsigned int) numwrite;
+}
+
/* read access to files which contain one numeric value */
enum idlestate_value {
@@ -128,6 +189,7 @@
IDLESTATE_POWER,
IDLESTATE_LATENCY,
IDLESTATE_TIME,
+ IDLESTATE_DISABLE,
MAX_IDLESTATE_VALUE_FILES
};
@@ -136,6 +198,7 @@
[IDLESTATE_POWER] = "power",
[IDLESTATE_LATENCY] = "latency",
[IDLESTATE_TIME] = "time",
+ [IDLESTATE_DISABLE] = "disable",
};
static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
@@ -205,8 +268,59 @@
return result;
}
+/*
+ * Returns:
+ * 1 if disabled
+ * 0 if enabled
+ * -1 if idlestate is not available
+ * -2 if disabling is not supported by the kernel
+ */
+int sysfs_is_idlestate_disabled(unsigned int cpu,
+ unsigned int idlestate)
+{
+ if (sysfs_get_idlestate_count(cpu) < idlestate)
+ return -1;
+
+ if (!sysfs_idlestate_file_exists(cpu, idlestate,
+ idlestate_value_files[IDLESTATE_DISABLE]))
+ return -2;
+ return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
+}
+
+/*
+ * Pass 1 as last argument to disable or 0 to enable the state
+ * Returns:
+ * 0 on success
+ * negative values on error, for example:
+ * -1 if idlestate is not available
+ * -2 if disabling is not supported by the kernel
+ * -3 No write access to disable/enable C-states
+ */
+int sysfs_idlestate_disable(unsigned int cpu,
+ unsigned int idlestate,
+ unsigned int disable)
+{
+ char value[SYSFS_PATH_MAX];
+ int bytes_written;
+
+ if (sysfs_get_idlestate_count(cpu) < idlestate)
+ return -1;
+
+ if (!sysfs_idlestate_file_exists(cpu, idlestate,
+ idlestate_value_files[IDLESTATE_DISABLE]))
+ return -2;
+
+ snprintf(value, SYSFS_PATH_MAX, "%u", disable);
+
+ bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",
+ value, sizeof(disable));
+ if (bytes_written)
+ return 0;
+ return -3;
+}
+
unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
- unsigned int idlestate)
+ unsigned int idlestate)
{
return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
}
@@ -238,7 +352,7 @@
* Negativ in error case
* Zero if cpuidle does not export any C-states
*/
-int sysfs_get_idlestate_count(unsigned int cpu)
+unsigned int sysfs_get_idlestate_count(unsigned int cpu)
{
char file[SYSFS_PATH_MAX];
struct stat statbuf;
diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h
index 8cb797b..d28f11f 100644
--- a/tools/power/cpupower/utils/helpers/sysfs.h
+++ b/tools/power/cpupower/utils/helpers/sysfs.h
@@ -7,8 +7,16 @@
extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen);
+extern unsigned int sysfs_idlestate_file_exists(unsigned int cpu,
+ unsigned int idlestate,
+ const char *fname);
+
extern int sysfs_is_cpu_online(unsigned int cpu);
+extern int sysfs_is_idlestate_disabled(unsigned int cpu,
+ unsigned int idlestate);
+extern int sysfs_idlestate_disable(unsigned int cpu, unsigned int idlestate,
+ unsigned int disable);
extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
unsigned int idlestate);
extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
@@ -19,7 +27,7 @@
unsigned int idlestate);
extern char *sysfs_get_idlestate_desc(unsigned int cpu,
unsigned int idlestate);
-extern int sysfs_get_idlestate_count(unsigned int cpu);
+extern unsigned int sysfs_get_idlestate_count(unsigned int cpu);
extern char *sysfs_get_cpuidle_governor(void);
extern char *sysfs_get_cpuidle_driver(void);
diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
new file mode 100644
index 0000000..ebeaba6
--- /dev/null
+++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
@@ -0,0 +1,196 @@
+/*
+ * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * Based on SandyBridge monitor. Implements the new package C-states
+ * (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "helpers/helpers.h"
+#include "idle_monitor/cpupower-monitor.h"
+
+#define MSR_PKG_C8_RESIDENCY 0x00000630
+#define MSR_PKG_C9_RESIDENCY 0x00000631
+#define MSR_PKG_C10_RESIDENCY 0x00000632
+
+#define MSR_TSC 0x10
+
+enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT,
+ TSC = 0xFFFF };
+
+static int hsw_ext_get_count_percent(unsigned int self_id, double *percent,
+ unsigned int cpu);
+
+static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = {
+ {
+ .name = "PC8",
+ .desc = N_("Processor Package C8"),
+ .id = PC8,
+ .range = RANGE_PACKAGE,
+ .get_count_percent = hsw_ext_get_count_percent,
+ },
+ {
+ .name = "PC9",
+ .desc = N_("Processor Package C9"),
+ .desc = N_("Processor Package C2"),
+ .id = PC9,
+ .range = RANGE_PACKAGE,
+ .get_count_percent = hsw_ext_get_count_percent,
+ },
+ {
+ .name = "PC10",
+ .desc = N_("Processor Package C10"),
+ .id = PC10,
+ .range = RANGE_PACKAGE,
+ .get_count_percent = hsw_ext_get_count_percent,
+ },
+};
+
+static unsigned long long tsc_at_measure_start;
+static unsigned long long tsc_at_measure_end;
+static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT];
+static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT];
+/* valid flag for all CPUs. If a MSR read failed it will be zero */
+static int *is_valid;
+
+static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val,
+ unsigned int cpu)
+{
+ int msr;
+
+ switch (id) {
+ case PC8:
+ msr = MSR_PKG_C8_RESIDENCY;
+ break;
+ case PC9:
+ msr = MSR_PKG_C9_RESIDENCY;
+ break;
+ case PC10:
+ msr = MSR_PKG_C10_RESIDENCY;
+ break;
+ case TSC:
+ msr = MSR_TSC;
+ break;
+ default:
+ return -1;
+ };
+ if (read_msr(cpu, msr, val))
+ return -1;
+ return 0;
+}
+
+static int hsw_ext_get_count_percent(unsigned int id, double *percent,
+ unsigned int cpu)
+{
+ *percent = 0.0;
+
+ if (!is_valid[cpu])
+ return -1;
+
+ *percent = (100.0 *
+ (current_count[id][cpu] - previous_count[id][cpu])) /
+ (tsc_at_measure_end - tsc_at_measure_start);
+
+ dprint("%s: previous: %llu - current: %llu - (%u)\n",
+ hsw_ext_cstates[id].name, previous_count[id][cpu],
+ current_count[id][cpu], cpu);
+
+ dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
+ hsw_ext_cstates[id].name,
+ (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
+ current_count[id][cpu] - previous_count[id][cpu],
+ *percent, cpu);
+
+ return 0;
+}
+
+static int hsw_ext_start(void)
+{
+ int num, cpu;
+ unsigned long long val;
+
+ for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
+ for (cpu = 0; cpu < cpu_count; cpu++) {
+ hsw_ext_get_count(num, &val, cpu);
+ previous_count[num][cpu] = val;
+ }
+ }
+ hsw_ext_get_count(TSC, &tsc_at_measure_start, 0);
+ return 0;
+}
+
+static int hsw_ext_stop(void)
+{
+ unsigned long long val;
+ int num, cpu;
+
+ hsw_ext_get_count(TSC, &tsc_at_measure_end, 0);
+
+ for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
+ for (cpu = 0; cpu < cpu_count; cpu++) {
+ is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu);
+ current_count[num][cpu] = val;
+ }
+ }
+ return 0;
+}
+
+struct cpuidle_monitor intel_hsw_ext_monitor;
+
+static struct cpuidle_monitor *hsw_ext_register(void)
+{
+ int num;
+
+ if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
+ || cpupower_cpu_info.family != 6)
+ return NULL;
+
+ switch (cpupower_cpu_info.model) {
+ case 0x45: /* HSW */
+ break;
+ default:
+ return NULL;
+ }
+
+ is_valid = calloc(cpu_count, sizeof(int));
+ for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
+ previous_count[num] = calloc(cpu_count,
+ sizeof(unsigned long long));
+ current_count[num] = calloc(cpu_count,
+ sizeof(unsigned long long));
+ }
+ intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name);
+ return &intel_hsw_ext_monitor;
+}
+
+void hsw_ext_unregister(void)
+{
+ int num;
+ free(is_valid);
+ for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
+ free(previous_count[num]);
+ free(current_count[num]);
+ }
+}
+
+struct cpuidle_monitor intel_hsw_ext_monitor = {
+ .name = "HaswellExtended",
+ .hw_states = hsw_ext_cstates,
+ .hw_states_num = HSW_EXT_CSTATE_COUNT,
+ .start = hsw_ext_start,
+ .stop = hsw_ext_stop,
+ .do_register = hsw_ext_register,
+ .unregister = hsw_ext_unregister,
+ .needs_root = 1,
+ .overflow_s = 922000000 /* 922337203 seconds TSC overflow
+ at 20GHz */
+};
+#endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def
index e3f8d9b..0d6ba4d 100644
--- a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def
+++ b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def
@@ -2,6 +2,7 @@
DEF(amd_fam14h)
DEF(intel_nhm)
DEF(intel_snb)
+DEF(intel_hsw_ext)
DEF(mperf)
#endif
DEF(cpuidle_sysfs)
diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c
index a99b43b..efc8a69 100644
--- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c
+++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c
@@ -155,6 +155,10 @@
case 0x2D: /* SNB Xeon */
case 0x3A: /* IVB */
case 0x3E: /* IVB Xeon */
+ case 0x3C: /* HSW */
+ case 0x3F: /* HSW */
+ case 0x45: /* HSW */
+ case 0x46: /* HSW */
break;
default:
return NULL;
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include
index f03e681..0d0506d 100644
--- a/tools/scripts/Makefile.include
+++ b/tools/scripts/Makefile.include
@@ -59,7 +59,7 @@
QUIET_SUBDIR1 =
ifneq ($(findstring $(MAKEFLAGS),s),s)
-ifndef V
+ifneq ($(V),1)
QUIET_CC = @echo ' ' CC $@;
QUIET_AR = @echo ' ' AR $@;
QUIET_LINK = @echo ' ' LINK $@;
diff --git a/tools/virtio/linux/module.h b/tools/virtio/linux/module.h
index 3039a7e..28ce95a 100644
--- a/tools/virtio/linux/module.h
+++ b/tools/virtio/linux/module.h
@@ -1 +1,6 @@
#include <linux/export.h>
+
+#define MODULE_LICENSE(__MODULE_LICENSE_value) \
+ static __attribute__((unused)) const char *__MODULE_LICENSE_name = \
+ __MODULE_LICENSE_value
+
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index cd80183..8447830 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -45,9 +45,6 @@
void *priv;
};
-#define MODULE_LICENSE(__MODULE_LICENSE_value) \
- const char *__MODULE_LICENSE_name = __MODULE_LICENSE_value
-
/* Interfaces exported by virtio_ring. */
int virtqueue_add_sgs(struct virtqueue *vq,
struct scatterlist *sgs[],