Staging driver pull request for 3.15-rc1
Here's the huge drivers/staging/ update for 3.15-rc1. Loads of cleanup fixes, a few drivers removed, and some new ones added. All have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlM7BqAACgkQMUfUDdst+ykHUwCguJDlvM7/FGb3QQslAuKN5Np4 n2YAoJ3C355mo8Wxr/bJah3Jms4f+a7Q =4XGY -----END PGP SIGNATURE----- Merge tag 'staging-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver updates from Greg KH: "Here's the huge drivers/staging/ update for 3.15-rc1. Loads of cleanup fixes, a few drivers removed, and some new ones added. All have been in linux-next for a while" * tag 'staging-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1375 commits) staging: xillybus: XILLYBUS_PCIE depends on PCI_MSI staging: xillybus: Added "select CRC32" for XILLYBUS in Kconfig staging: comedi: poc: remove obsolete driver staging: unisys: replace kzalloc/kfree with UISMALLOC/UISFREE staging: octeon-usb: prevent memory corruption staging: usbip: fix line over 80 characters staging: usbip: fix quoted string split across lines Staging: unisys: Remove RETINT macro Staging: unisys: Remove FAIL macro Staging: unisys: Remove RETVOID macro Staging: unisys: Remove RETPTR macro Staging: unisys: Remove RETBOOL macro Staging: unisys: Remove FAIL_WPOSTCODE_1 macro Staging: unisys: Cleanup macros to get rid of goto statements Staging: unisys: include: Remove unused macros from timskmod.h staging: dgap: fix the rest of the checkpatch warnings in dgap.c Staging: bcm: Remove unnecessary parentheses staging: wlags49_h2: Delete unnecessary braces staging: wlags49_h2: Do not use assignment in if condition staging: wlags49_h2: Enclose macro in a do-while loop ...
This commit is contained in:
commit
c12e69c6aa
943 changed files with 61969 additions and 48113 deletions
|
@ -5,6 +5,9 @@ Required properties:
|
|||
<chip> can be "at91sam9260", "at91sam9g45" or "at91sam9x5"
|
||||
- reg: Should contain ADC registers location and length
|
||||
- interrupts: Should contain the IRQ line for the ADC
|
||||
- clock-names: tuple listing input clock names.
|
||||
Required elements: "adc_clk", "adc_op_clk".
|
||||
- clocks: phandles to input clocks.
|
||||
- atmel,adc-channels-used: Bitmask of the channels muxed and enable for this
|
||||
device
|
||||
- atmel,adc-startup-time: Startup Time of the ADC in microseconds as
|
||||
|
@ -44,6 +47,8 @@ adc0: adc@fffb0000 {
|
|||
compatible = "atmel,at91sam9260-adc";
|
||||
reg = <0xfffb0000 0x100>;
|
||||
interrupts = <20 4>;
|
||||
clocks = <&adc_clk>, <&adc_op_clk>;
|
||||
clock-names = "adc_clk", "adc_op_clk";
|
||||
atmel,adc-channel-base = <0x30>;
|
||||
atmel,adc-channels-used = <0xff>;
|
||||
atmel,adc-drdy-mask = <0x10000>;
|
||||
|
|
129
Documentation/devicetree/bindings/graph.txt
Normal file
129
Documentation/devicetree/bindings/graph.txt
Normal file
|
@ -0,0 +1,129 @@
|
|||
Common bindings for device graphs
|
||||
|
||||
General concept
|
||||
---------------
|
||||
|
||||
The hierarchical organisation of the device tree is well suited to describe
|
||||
control flow to devices, but there can be more complex connections between
|
||||
devices that work together to form a logical compound device, following an
|
||||
arbitrarily complex graph.
|
||||
There already is a simple directed graph between devices tree nodes using
|
||||
phandle properties pointing to other nodes to describe connections that
|
||||
can not be inferred from device tree parent-child relationships. The device
|
||||
tree graph bindings described herein abstract more complex devices that can
|
||||
have multiple specifiable ports, each of which can be linked to one or more
|
||||
ports of other devices.
|
||||
|
||||
These common bindings do not contain any information about the direction or
|
||||
type of the connections, they just map their existence. Specific properties
|
||||
may be described by specialized bindings depending on the type of connection.
|
||||
|
||||
To see how this binding applies to video pipelines, for example, see
|
||||
Documentation/device-tree/bindings/media/video-interfaces.txt.
|
||||
Here the ports describe data interfaces, and the links between them are
|
||||
the connecting data buses. A single port with multiple connections can
|
||||
correspond to multiple devices being connected to the same physical bus.
|
||||
|
||||
Organisation of ports and endpoints
|
||||
-----------------------------------
|
||||
|
||||
Ports are described by child 'port' nodes contained in the device node.
|
||||
Each port node contains an 'endpoint' subnode for each remote device port
|
||||
connected to this port. If a single port is connected to more than one
|
||||
remote device, an 'endpoint' child node must be provided for each link.
|
||||
If more than one port is present in a device node or there is more than one
|
||||
endpoint at a port, or a port node needs to be associated with a selected
|
||||
hardware interface, a common scheme using '#address-cells', '#size-cells'
|
||||
and 'reg' properties is used number the nodes.
|
||||
|
||||
device {
|
||||
...
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
|
||||
endpoint@0 {
|
||||
reg = <0>;
|
||||
...
|
||||
};
|
||||
endpoint@1 {
|
||||
reg = <1>;
|
||||
...
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
endpoint { ... };
|
||||
};
|
||||
};
|
||||
|
||||
All 'port' nodes can be grouped under an optional 'ports' node, which
|
||||
allows to specify #address-cells, #size-cells properties for the 'port'
|
||||
nodes independently from any other child device nodes a device might
|
||||
have.
|
||||
|
||||
device {
|
||||
...
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
...
|
||||
endpoint@0 { ... };
|
||||
endpoint@1 { ... };
|
||||
};
|
||||
|
||||
port@1 { ... };
|
||||
};
|
||||
};
|
||||
|
||||
Links between endpoints
|
||||
-----------------------
|
||||
|
||||
Each endpoint should contain a 'remote-endpoint' phandle property that points
|
||||
to the corresponding endpoint in the port of the remote device. In turn, the
|
||||
remote endpoint should contain a 'remote-endpoint' property. If it has one,
|
||||
it must not point to another than the local endpoint. Two endpoints with their
|
||||
'remote-endpoint' phandles pointing at each other form a link between the
|
||||
containing ports.
|
||||
|
||||
device-1 {
|
||||
port {
|
||||
device_1_output: endpoint {
|
||||
remote-endpoint = <&device_2_input>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
device-2 {
|
||||
port {
|
||||
device_2_input: endpoint {
|
||||
remote-endpoint = <&device_1_output>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Required properties
|
||||
-------------------
|
||||
|
||||
If there is more than one 'port' or more than one 'endpoint' node or 'reg'
|
||||
property is present in port and/or endpoint nodes the following properties
|
||||
are required in a relevant parent node:
|
||||
|
||||
- #address-cells : number of cells required to define port/endpoint
|
||||
identifier, should be 1.
|
||||
- #size-cells : should be zero.
|
||||
|
||||
Optional endpoint properties
|
||||
----------------------------
|
||||
|
||||
- remote-endpoint: phandle to an 'endpoint' subnode of a remote device node.
|
||||
|
22
Documentation/devicetree/bindings/iio/adc/vf610-adc.txt
Normal file
22
Documentation/devicetree/bindings/iio/adc/vf610-adc.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
Freescale vf610 Analog to Digital Converter bindings
|
||||
|
||||
The devicetree bindings are for the new ADC driver written for
|
||||
vf610/i.MX6slx and upward SoCs from Freescale.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "fsl,vf610-adc"
|
||||
- reg: Offset and length of the register set for the device
|
||||
- interrupts: Should contain the interrupt for the device
|
||||
- clocks: The clock is needed by the ADC controller, ADC clock source is ipg clock.
|
||||
- clock-names: Must contain "adc", matching entry in the clocks property.
|
||||
- vref-supply: The regulator supply ADC refrence voltage.
|
||||
|
||||
Example:
|
||||
adc0: adc@4003b000 {
|
||||
compatible = "fsl,vf610-adc";
|
||||
reg = <0x4003b000 0x1000>;
|
||||
interrupts = <0 53 0x04>;
|
||||
clocks = <&clks VF610_CLK_ADC0>;
|
||||
clock-names = "adc";
|
||||
vref-supply = <®_vcc_3v3_mcu>;
|
||||
};
|
113
Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
Normal file
113
Documentation/devicetree/bindings/iio/adc/xilinx-xadc.txt
Normal file
|
@ -0,0 +1,113 @@
|
|||
Xilinx XADC device driver
|
||||
|
||||
This binding document describes the bindings for both of them since the
|
||||
bindings are very similar. The Xilinx XADC is a ADC that can be found in the
|
||||
series 7 FPGAs from Xilinx. The XADC has a DRP interface for communication.
|
||||
Currently two different frontends for the DRP interface exist. One that is only
|
||||
available on the ZYNQ family as a hardmacro in the SoC portion of the ZYNQ. The
|
||||
other one is available on all series 7 platforms and is a softmacro with a AXI
|
||||
interface. This binding document describes the bindings for both of them since
|
||||
the bindings are very similar.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
* "xlnx,zynq-xadc-1.00.a": When using the ZYNQ device
|
||||
configuration interface to interface to the XADC hardmacro.
|
||||
* "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
|
||||
interface to the XADC hardmacro.
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Interrupt for the XADC control interface.
|
||||
- clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
|
||||
when using the AXI-XADC pcore this must be the clock that provides the
|
||||
clock to the AXI bus interface of the core.
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: phandle to the parent interrupt controller
|
||||
- xlnx,external-mux:
|
||||
* "none": No external multiplexer is used, this is the default
|
||||
if the property is omitted.
|
||||
* "single": External multiplexer mode is used with one
|
||||
multiplexer.
|
||||
* "dual": External multiplexer mode is used with two
|
||||
multiplexers for simultaneous sampling.
|
||||
- xlnx,external-mux-channel: Configures which pair of pins is used to
|
||||
sample data in external mux mode.
|
||||
Valid values for single external multiplexer mode are:
|
||||
0: VP/VN
|
||||
1: VAUXP[0]/VAUXN[0]
|
||||
2: VAUXP[1]/VAUXN[1]
|
||||
...
|
||||
16: VAUXP[15]/VAUXN[15]
|
||||
Valid values for dual external multiplexer mode are:
|
||||
1: VAUXP[0]/VAUXN[0] - VAUXP[8]/VAUXN[8]
|
||||
2: VAUXP[1]/VAUXN[1] - VAUXP[9]/VAUXN[9]
|
||||
...
|
||||
8: VAUXP[7]/VAUXN[7] - VAUXP[15]/VAUXN[15]
|
||||
|
||||
This property needs to be present if the device is configured for
|
||||
external multiplexer mode (either single or dual). If the device is
|
||||
not using external multiplexer mode the property is ignored.
|
||||
- xnlx,channels: List of external channels that are connected to the ADC
|
||||
Required properties:
|
||||
* #address-cells: Should be 1.
|
||||
* #size-cells: Should be 0.
|
||||
|
||||
The child nodes of this node represent the external channels which are
|
||||
connected to the ADC. If the property is no present no external
|
||||
channels will be assumed to be connected.
|
||||
|
||||
Each child node represents one channel and has the following
|
||||
properties:
|
||||
Required properties:
|
||||
* reg: Pair of pins the the channel is connected to.
|
||||
0: VP/VN
|
||||
1: VAUXP[0]/VAUXN[0]
|
||||
2: VAUXP[1]/VAUXN[1]
|
||||
...
|
||||
16: VAUXP[15]/VAUXN[15]
|
||||
Note each channel number should only be used at most
|
||||
once.
|
||||
Optional properties:
|
||||
* xlnx,bipolar: If set the channel is used in bipolar
|
||||
mode.
|
||||
|
||||
|
||||
Examples:
|
||||
xadc@f8007100 {
|
||||
compatible = "xlnx,zynq-xadc-1.00.a";
|
||||
reg = <0xf8007100 0x20>;
|
||||
interrupts = <0 7 4>;
|
||||
interrupt-parent = <&gic>;
|
||||
clocks = <&pcap_clk>;
|
||||
|
||||
xlnx,channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
channel@8 {
|
||||
reg = <8>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
xadc@43200000 {
|
||||
compatible = "xlnx,axi-xadc-1.00.a";
|
||||
reg = <0x43200000 0x1000>;
|
||||
interrupts = <0 53 4>;
|
||||
interrupt-parent = <&gic>;
|
||||
clocks = <&fpga1_clk>;
|
||||
|
||||
xlnx,channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
xlnx,bipolar;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -1,3 +1,22 @@
|
|||
Freescale i.MX DRM master device
|
||||
================================
|
||||
|
||||
The freescale i.MX DRM master device is a virtual device needed to list all
|
||||
IPU or other display interface nodes that comprise the graphics subsystem.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx-display-subsystem"
|
||||
- ports: Should contain a list of phandles pointing to display interface ports
|
||||
of IPU devices
|
||||
|
||||
example:
|
||||
|
||||
display-subsystem {
|
||||
compatible = "fsl,display-subsystem";
|
||||
ports = <&ipu_di0>;
|
||||
};
|
||||
|
||||
|
||||
Freescale i.MX IPUv3
|
||||
====================
|
||||
|
||||
|
@ -7,18 +26,31 @@ Required properties:
|
|||
datasheet
|
||||
- interrupts: Should contain sync interrupt and error interrupt,
|
||||
in this order.
|
||||
- #crtc-cells: 1, See below
|
||||
- resets: phandle pointing to the system reset controller and
|
||||
reset line index, see reset/fsl,imx-src.txt for details
|
||||
Optional properties:
|
||||
- port@[0-3]: Port nodes with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt.
|
||||
Ports 0 and 1 should correspond to CSI0 and CSI1,
|
||||
ports 2 and 3 should correspond to DI0 and DI1, respectively.
|
||||
|
||||
example:
|
||||
|
||||
ipu: ipu@18000000 {
|
||||
#crtc-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx53-ipu";
|
||||
reg = <0x18000000 0x080000000>;
|
||||
interrupts = <11 10>;
|
||||
resets = <&src 2>;
|
||||
|
||||
ipu_di0: port@2 {
|
||||
reg = <2>;
|
||||
|
||||
ipu_di0_disp0: endpoint {
|
||||
remote-endpoint = <&display_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Parallel display support
|
||||
|
@ -26,19 +58,25 @@ Parallel display support
|
|||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx-parallel-display"
|
||||
- crtc: the crtc this display is connected to, see below
|
||||
Optional properties:
|
||||
- interface_pix_fmt: How this display is connected to the
|
||||
crtc. Currently supported types: "rgb24", "rgb565", "bgr666"
|
||||
display interface. Currently supported types: "rgb24", "rgb565", "bgr666"
|
||||
- edid: verbatim EDID data block describing attached display.
|
||||
- ddc: phandle describing the i2c bus handling the display data
|
||||
channel
|
||||
- port: A port node with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt.
|
||||
|
||||
example:
|
||||
|
||||
display@di0 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
edid = [edid-data];
|
||||
crtc = <&ipu 0>;
|
||||
interface-pix-fmt = "rgb24";
|
||||
|
||||
port {
|
||||
display_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_disp0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
58
Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt
Normal file
58
Documentation/devicetree/bindings/staging/imx-drm/hdmi.txt
Normal file
|
@ -0,0 +1,58 @@
|
|||
Device-Tree bindings for HDMI Transmitter
|
||||
|
||||
HDMI Transmitter
|
||||
================
|
||||
|
||||
The HDMI Transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
|
||||
with accompanying PHY IP.
|
||||
|
||||
Required properties:
|
||||
- #address-cells : should be <1>
|
||||
- #size-cells : should be <0>
|
||||
- compatible : should be "fsl,imx6q-hdmi" or "fsl,imx6dl-hdmi".
|
||||
- gpr : should be <&gpr>.
|
||||
The phandle points to the iomuxc-gpr region containing the HDMI
|
||||
multiplexer control register.
|
||||
- clocks, clock-names : phandles to the HDMI iahb and isrf clocks, as described
|
||||
in Documentation/devicetree/bindings/clock/clock-bindings.txt and
|
||||
Documentation/devicetree/bindings/clock/imx6q-clock.txt.
|
||||
- port@[0-4]: Up to four port nodes with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt,
|
||||
corresponding to the four inputs to the HDMI multiplexer.
|
||||
|
||||
Optional properties:
|
||||
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
|
||||
|
||||
example:
|
||||
|
||||
gpr: iomuxc-gpr@020e0000 {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
hdmi: hdmi@0120000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx6q-hdmi";
|
||||
reg = <0x00120000 0x9000>;
|
||||
interrupts = <0 115 0x04>;
|
||||
gpr = <&gpr>;
|
||||
clocks = <&clks 123>, <&clks 124>;
|
||||
clock-names = "iahb", "isfr";
|
||||
ddc-i2c-bus = <&i2c2>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
hdmi_mux_0: endpoint {
|
||||
remote-endpoint = <&ipu1_di0_hdmi>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
hdmi_mux_1: endpoint {
|
||||
remote-endpoint = <&ipu1_di1_hdmi>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -50,12 +50,14 @@ have a look at Documentation/devicetree/bindings/video/display-timing.txt.
|
|||
|
||||
Required properties:
|
||||
- reg : should be <0> or <1>
|
||||
- crtcs : a list of phandles with index pointing to the IPU display interfaces
|
||||
that can be used as video source for this channel.
|
||||
- fsl,data-mapping : should be "spwg" or "jeida"
|
||||
This describes how the color bits are laid out in the
|
||||
serialized LVDS signal.
|
||||
- fsl,data-width : should be <18> or <24>
|
||||
- port: A port node with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt.
|
||||
On i.MX6, there should be four ports (port@[0-3]) that correspond
|
||||
to the four LVDS multiplexer inputs.
|
||||
|
||||
example:
|
||||
|
||||
|
@ -77,23 +79,33 @@ ldb: ldb@53fa8008 {
|
|||
|
||||
lvds-channel@0 {
|
||||
reg = <0>;
|
||||
crtcs = <&ipu 0>;
|
||||
fsl,data-mapping = "spwg";
|
||||
fsl,data-width = <24>;
|
||||
|
||||
display-timings {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
port {
|
||||
lvds0_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_lvds0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lvds-channel@1 {
|
||||
reg = <1>;
|
||||
crtcs = <&ipu 1>;
|
||||
fsl,data-mapping = "spwg";
|
||||
fsl,data-width = <24>;
|
||||
|
||||
display-timings {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
port {
|
||||
lvds1_in: endpoint {
|
||||
remote-endpoint = <&ipu_di1_lvds1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
13
MAINTAINERS
13
MAINTAINERS
|
@ -8394,6 +8394,12 @@ M: Teddy Wang <teddy.wang@siliconmotion.com.cn>
|
|||
S: Odd Fixes
|
||||
F: drivers/staging/sm7xxfb/
|
||||
|
||||
STAGING - SLICOSS
|
||||
M: Lior Dotan <liodot@gmail.com>
|
||||
M: Christopher Harrer <charrer@alacritech.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/slicoss/
|
||||
|
||||
STAGING - SOFTLOGIC 6x10 MPEG CODEC
|
||||
M: Ismael Luceno <ismael.luceno@corp.bluecherry.net>
|
||||
S: Supported
|
||||
|
@ -9079,6 +9085,13 @@ F: drivers/cdrom/cdrom.c
|
|||
F: include/linux/cdrom.h
|
||||
F: include/uapi/linux/cdrom.h
|
||||
|
||||
UNISYS S-PAR DRIVERS
|
||||
M: Benjamin Romer <benjamin.romer@unisys.com>
|
||||
M: David Kershner <david.kershner@unisys.com>
|
||||
L: sparmaintainer@unisys.com (Unisys internal)
|
||||
S: Supported
|
||||
F: drivers/staging/unisys/
|
||||
|
||||
UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER
|
||||
M: Vinayak Holikatti <vinholikatti@gmail.com>
|
||||
M: Santosh Y <santoshsy@gmail.com>
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
display@di1 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
crtcs = <&ipu 0>;
|
||||
interface-pix-fmt = "bgr666";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ipu_disp1_1>;
|
||||
|
@ -41,6 +40,12 @@
|
|||
pixelclk-active = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
port {
|
||||
display_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_disp0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
|
@ -122,3 +127,7 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
&ipu_di0_disp0 {
|
||||
remote-endpoint = <&display_in>;
|
||||
};
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
reg = <0x90000000 0x20000000>;
|
||||
};
|
||||
|
||||
display@di0 {
|
||||
display0: display@di0 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
crtcs = <&ipu 0>;
|
||||
interface-pix-fmt = "rgb24";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ipu_disp1_1>;
|
||||
|
@ -41,11 +40,16 @@
|
|||
vsync-len = <10>;
|
||||
};
|
||||
};
|
||||
|
||||
port {
|
||||
display0_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_disp0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
display@di1 {
|
||||
display1: display@di1 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
crtcs = <&ipu 1>;
|
||||
interface-pix-fmt = "rgb565";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ipu_disp2_1>;
|
||||
|
@ -68,6 +72,12 @@
|
|||
pixelclk-active = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
port {
|
||||
display1_in: endpoint {
|
||||
remote-endpoint = <&ipu_di1_disp1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
|
@ -258,6 +268,14 @@
|
|||
};
|
||||
};
|
||||
|
||||
&ipu_di0_disp0 {
|
||||
remote-endpoint = <&display0_in>;
|
||||
};
|
||||
|
||||
&ipu_di1_disp1 {
|
||||
remote-endpoint = <&display1_in>;
|
||||
};
|
||||
|
||||
&ssi2 {
|
||||
fsl,mode = "i2s-slave";
|
||||
status = "okay";
|
||||
|
|
|
@ -79,6 +79,11 @@
|
|||
};
|
||||
};
|
||||
|
||||
display-subsystem {
|
||||
compatible = "fsl,imx-display-subsystem";
|
||||
ports = <&ipu_di0>, <&ipu_di1>;
|
||||
};
|
||||
|
||||
soc {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
@ -92,13 +97,28 @@
|
|||
};
|
||||
|
||||
ipu: ipu@40000000 {
|
||||
#crtc-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx51-ipu";
|
||||
reg = <0x40000000 0x20000000>;
|
||||
interrupts = <11 10>;
|
||||
clocks = <&clks 59>, <&clks 110>, <&clks 61>;
|
||||
clock-names = "bus", "di0", "di1";
|
||||
resets = <&src 2>;
|
||||
|
||||
ipu_di0: port@2 {
|
||||
reg = <2>;
|
||||
|
||||
ipu_di0_disp0: endpoint {
|
||||
};
|
||||
};
|
||||
|
||||
ipu_di1: port@3 {
|
||||
reg = <3>;
|
||||
|
||||
ipu_di1_disp1: endpoint {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
aips@70000000 { /* AIPS1 */
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
};
|
||||
|
||||
soc {
|
||||
display@di1 {
|
||||
display1: display@di1 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
crtcs = <&ipu 1>;
|
||||
interface-pix-fmt = "bgr666";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ipu_disp2_1>;
|
||||
|
@ -44,6 +43,12 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
port {
|
||||
display1_in: endpoint {
|
||||
remote-endpoint = <&ipu_di1_disp1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
backlight {
|
||||
|
@ -221,6 +226,10 @@
|
|||
};
|
||||
};
|
||||
|
||||
&ipu_di1_disp1 {
|
||||
remote-endpoint = <&display1_in>;
|
||||
};
|
||||
|
||||
&nfc {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_nand_1>;
|
||||
|
|
|
@ -38,9 +38,14 @@
|
|||
compatible = "fsl,imx-parallel-display";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_disp1_1>;
|
||||
crtcs = <&ipu 1>;
|
||||
interface-pix-fmt = "rgb24";
|
||||
status = "disabled";
|
||||
|
||||
port {
|
||||
display1_in: endpoint {
|
||||
remote-endpoint = <&ipu_di1_disp1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
reg_3p2v: 3p2v {
|
||||
|
@ -141,6 +146,10 @@
|
|||
};
|
||||
};
|
||||
|
||||
&ipu_di1_disp1 {
|
||||
remote-endpoint = <&display1_in>;
|
||||
};
|
||||
|
||||
&cspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
@ -228,7 +237,7 @@
|
|||
&tve {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_vga_sync_1>;
|
||||
ddc = <&i2c3>;
|
||||
i2c-ddc-bus = <&i2c3>;
|
||||
fsl,tve-mode = "vga";
|
||||
fsl,hsync-pin = <4>;
|
||||
fsl,vsync-pin = <6>;
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
reg = <0x70000000 0x40000000>;
|
||||
};
|
||||
|
||||
display@di0 {
|
||||
display0: display@di0 {
|
||||
compatible = "fsl,imx-parallel-display";
|
||||
crtcs = <&ipu 0>;
|
||||
interface-pix-fmt = "rgb565";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ipu_disp0_1>;
|
||||
|
@ -46,6 +45,12 @@
|
|||
pixelclk-active = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
port {
|
||||
display0_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_disp0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
gpio-keys {
|
||||
|
@ -126,6 +131,10 @@
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
&ipu_di0_disp0 {
|
||||
remote-endpoint = <&display0_in>;
|
||||
};
|
||||
|
||||
&ssi2 {
|
||||
fsl,mode = "i2s-slave";
|
||||
status = "okay";
|
||||
|
|
|
@ -45,6 +45,11 @@
|
|||
};
|
||||
};
|
||||
|
||||
display-subsystem {
|
||||
compatible = "fsl,imx-display-subsystem";
|
||||
ports = <&ipu_di0>, <&ipu_di1>;
|
||||
};
|
||||
|
||||
tzic: tz-interrupt-controller@0fffc000 {
|
||||
compatible = "fsl,imx53-tzic", "fsl,tzic";
|
||||
interrupt-controller;
|
||||
|
@ -85,13 +90,49 @@
|
|||
ranges;
|
||||
|
||||
ipu: ipu@18000000 {
|
||||
#crtc-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx53-ipu";
|
||||
reg = <0x18000000 0x080000000>;
|
||||
interrupts = <11 10>;
|
||||
clocks = <&clks 59>, <&clks 110>, <&clks 61>;
|
||||
clock-names = "bus", "di0", "di1";
|
||||
resets = <&src 2>;
|
||||
|
||||
ipu_di0: port@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <2>;
|
||||
|
||||
ipu_di0_disp0: endpoint@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
ipu_di0_lvds0: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&lvds0_in>;
|
||||
};
|
||||
};
|
||||
|
||||
ipu_di1: port@3 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <3>;
|
||||
|
||||
ipu_di1_disp1: endpoint@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
ipu_di1_lvds1: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&lvds1_in>;
|
||||
};
|
||||
|
||||
ipu_di1_tve: endpoint@2 {
|
||||
reg = <2>;
|
||||
remote-endpoint = <&tve_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
aips@50000000 { /* AIPS1 */
|
||||
|
@ -838,14 +879,24 @@
|
|||
|
||||
lvds-channel@0 {
|
||||
reg = <0>;
|
||||
crtcs = <&ipu 0>;
|
||||
status = "disabled";
|
||||
|
||||
port {
|
||||
lvds0_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_lvds0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lvds-channel@1 {
|
||||
reg = <1>;
|
||||
crtcs = <&ipu 1>;
|
||||
status = "disabled";
|
||||
|
||||
port {
|
||||
lvds1_in: endpoint {
|
||||
remote-endpoint = <&ipu_di0_lvds0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1103,8 +1154,13 @@
|
|||
interrupts = <92>;
|
||||
clocks = <&clks 69>, <&clks 116>;
|
||||
clock-names = "tve", "di_sel";
|
||||
crtcs = <&ipu 1>;
|
||||
status = "disabled";
|
||||
|
||||
port {
|
||||
tve_in: endpoint {
|
||||
remote-endpoint = <&ipu_di1_tve>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vpu: vpu@63ff4000 {
|
||||
|
|
|
@ -70,6 +70,15 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
display-subsystem {
|
||||
compatible = "fsl,imx-display-subsystem";
|
||||
ports = <&ipu1_di0>, <&ipu1_di1>;
|
||||
};
|
||||
};
|
||||
|
||||
&hdmi {
|
||||
compatible = "fsl,imx6dl-hdmi";
|
||||
};
|
||||
|
||||
&ldb {
|
||||
|
@ -79,12 +88,4 @@
|
|||
clock-names = "di0_pll", "di1_pll",
|
||||
"di0_sel", "di1_sel",
|
||||
"di0", "di1";
|
||||
|
||||
lvds-channel@0 {
|
||||
crtcs = <&ipu1 0>, <&ipu1 1>;
|
||||
};
|
||||
|
||||
lvds-channel@1 {
|
||||
crtcs = <&ipu1 0>, <&ipu1 1>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -132,13 +132,84 @@
|
|||
};
|
||||
|
||||
ipu2: ipu@02800000 {
|
||||
#crtc-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx6q-ipu";
|
||||
reg = <0x02800000 0x400000>;
|
||||
interrupts = <0 8 0x4 0 7 0x4>;
|
||||
clocks = <&clks 133>, <&clks 134>, <&clks 137>;
|
||||
clock-names = "bus", "di0", "di1";
|
||||
resets = <&src 4>;
|
||||
|
||||
ipu2_di0: port@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <2>;
|
||||
|
||||
ipu2_di0_disp0: endpoint@0 {
|
||||
};
|
||||
|
||||
ipu2_di0_hdmi: endpoint@1 {
|
||||
remote-endpoint = <&hdmi_mux_2>;
|
||||
};
|
||||
|
||||
ipu2_di0_mipi: endpoint@2 {
|
||||
};
|
||||
|
||||
ipu2_di0_lvds0: endpoint@3 {
|
||||
remote-endpoint = <&lvds0_mux_2>;
|
||||
};
|
||||
|
||||
ipu2_di0_lvds1: endpoint@4 {
|
||||
remote-endpoint = <&lvds1_mux_2>;
|
||||
};
|
||||
};
|
||||
|
||||
ipu2_di1: port@3 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <3>;
|
||||
|
||||
ipu2_di1_hdmi: endpoint@1 {
|
||||
remote-endpoint = <&hdmi_mux_3>;
|
||||
};
|
||||
|
||||
ipu2_di1_mipi: endpoint@2 {
|
||||
};
|
||||
|
||||
ipu2_di1_lvds0: endpoint@3 {
|
||||
remote-endpoint = <&lvds0_mux_3>;
|
||||
};
|
||||
|
||||
ipu2_di1_lvds1: endpoint@4 {
|
||||
remote-endpoint = <&lvds1_mux_3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
display-subsystem {
|
||||
compatible = "fsl,imx-display-subsystem";
|
||||
ports = <&ipu1_di0>, <&ipu1_di1>, <&ipu2_di0>, <&ipu2_di1>;
|
||||
};
|
||||
};
|
||||
|
||||
&hdmi {
|
||||
compatible = "fsl,imx6q-hdmi";
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
hdmi_mux_2: endpoint {
|
||||
remote-endpoint = <&ipu2_di0_hdmi>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
|
||||
hdmi_mux_3: endpoint {
|
||||
remote-endpoint = <&ipu2_di1_hdmi>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -152,10 +223,56 @@
|
|||
"di0", "di1";
|
||||
|
||||
lvds-channel@0 {
|
||||
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
lvds0_mux_2: endpoint {
|
||||
remote-endpoint = <&ipu2_di0_lvds0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
|
||||
lvds0_mux_3: endpoint {
|
||||
remote-endpoint = <&ipu2_di1_lvds0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lvds-channel@1 {
|
||||
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
lvds1_mux_2: endpoint {
|
||||
remote-endpoint = <&ipu2_di0_lvds1>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
|
||||
lvds1_mux_3: endpoint {
|
||||
remote-endpoint = <&ipu2_di1_lvds1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&mipi_dsi {
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
mipi_mux_2: endpoint {
|
||||
remote-endpoint = <&ipu2_di0_mipi>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
|
||||
mipi_mux_3: endpoint {
|
||||
remote-endpoint = <&ipu2_di1_mipi>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1358,13 +1358,76 @@
|
|||
status = "disabled";
|
||||
|
||||
lvds-channel@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
status = "disabled";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
lvds0_mux_0: endpoint {
|
||||
remote-endpoint = <&ipu1_di0_lvds0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
lvds0_mux_1: endpoint {
|
||||
remote-endpoint = <&ipu1_di1_lvds0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
lvds-channel@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <1>;
|
||||
status = "disabled";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
lvds1_mux_0: endpoint {
|
||||
remote-endpoint = <&ipu1_di0_lvds1>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
lvds1_mux_1: endpoint {
|
||||
remote-endpoint = <&ipu1_di1_lvds1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hdmi: hdmi@0120000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x00120000 0x9000>;
|
||||
interrupts = <0 115 0x04>;
|
||||
gpr = <&gpr>;
|
||||
clocks = <&clks 123>, <&clks 124>;
|
||||
clock-names = "iahb", "isfr";
|
||||
status = "disabled";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
hdmi_mux_0: endpoint {
|
||||
remote-endpoint = <&ipu1_di0_hdmi>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
hdmi_mux_1: endpoint {
|
||||
remote-endpoint = <&ipu1_di1_hdmi>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1579,8 +1642,27 @@
|
|||
reg = <0x021dc000 0x4000>;
|
||||
};
|
||||
|
||||
mipi@021e0000 { /* MIPI-DSI */
|
||||
mipi_dsi: mipi@021e0000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x021e0000 0x4000>;
|
||||
status = "disabled";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
mipi_mux_0: endpoint {
|
||||
remote-endpoint = <&ipu1_di0_mipi>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
mipi_mux_1: endpoint {
|
||||
remote-endpoint = <&ipu1_di1_mipi>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vdoa@021e4000 {
|
||||
|
@ -1634,13 +1716,64 @@
|
|||
};
|
||||
|
||||
ipu1: ipu@02400000 {
|
||||
#crtc-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx6q-ipu";
|
||||
reg = <0x02400000 0x400000>;
|
||||
interrupts = <0 6 0x4 0 5 0x4>;
|
||||
clocks = <&clks 130>, <&clks 131>, <&clks 132>;
|
||||
clock-names = "bus", "di0", "di1";
|
||||
resets = <&src 2>;
|
||||
|
||||
ipu1_di0: port@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <2>;
|
||||
|
||||
ipu1_di0_disp0: endpoint@0 {
|
||||
};
|
||||
|
||||
ipu1_di0_hdmi: endpoint@1 {
|
||||
remote-endpoint = <&hdmi_mux_0>;
|
||||
};
|
||||
|
||||
ipu1_di0_mipi: endpoint@2 {
|
||||
remote-endpoint = <&mipi_mux_0>;
|
||||
};
|
||||
|
||||
ipu1_di0_lvds0: endpoint@3 {
|
||||
remote-endpoint = <&lvds0_mux_0>;
|
||||
};
|
||||
|
||||
ipu1_di0_lvds1: endpoint@4 {
|
||||
remote-endpoint = <&lvds1_mux_0>;
|
||||
};
|
||||
};
|
||||
|
||||
ipu1_di1: port@3 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <3>;
|
||||
|
||||
ipu1_di0_disp1: endpoint@0 {
|
||||
};
|
||||
|
||||
ipu1_di1_hdmi: endpoint@1 {
|
||||
remote-endpoint = <&hdmi_mux_1>;
|
||||
};
|
||||
|
||||
ipu1_di1_mipi: endpoint@2 {
|
||||
remote-endpoint = <&mipi_mux_1>;
|
||||
};
|
||||
|
||||
ipu1_di1_lvds0: endpoint@3 {
|
||||
remote-endpoint = <&lvds0_mux_1>;
|
||||
};
|
||||
|
||||
ipu1_di1_lvds1: endpoint@4 {
|
||||
remote-endpoint = <&lvds1_mux_1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -451,9 +451,9 @@ static const struct iio_chan_spec_ext_info bma180_ext_info[] = {
|
|||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = AXIS_##_axis, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
|
|
|
@ -207,6 +207,16 @@ config TWL6030_GPADC
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called twl6030-gpadc.
|
||||
|
||||
config VF610_ADC
|
||||
tristate "Freescale vf610 ADC driver"
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to support for Vybrid board analog-to-digital converter.
|
||||
Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called vf610_adc.
|
||||
|
||||
config VIPERBOARD_ADC
|
||||
tristate "Viperboard ADC support"
|
||||
depends on MFD_VIPERBOARD && USB
|
||||
|
@ -214,4 +224,17 @@ config VIPERBOARD_ADC
|
|||
Say yes here to access the ADC part of the Nano River
|
||||
Technologies Viperboard.
|
||||
|
||||
config XILINX_XADC
|
||||
tristate "Xilinx XADC driver"
|
||||
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to have support for the Xilinx XADC. The driver does support
|
||||
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
|
||||
|
||||
The driver can also be build as a module. If so, the module will be called
|
||||
xilinx-xadc.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -22,4 +22,7 @@ obj-$(CONFIG_NAU7802) += nau7802.o
|
|||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
|
||||
xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
|
||||
obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
|
||||
|
|
|
@ -8,17 +8,11 @@
|
|||
* based on linux/drivers/acron/char/pcf8583.c
|
||||
* Copyright (C) 2000 Russell King
|
||||
*
|
||||
* Driver for max1363 and similar chips.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* max1363.c
|
||||
*
|
||||
* Partial support for max1363 and similar chips.
|
||||
*
|
||||
* Not currently implemented.
|
||||
*
|
||||
* - Control of internal reference.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -1253,7 +1247,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {
|
|||
},
|
||||
[max11604] = {
|
||||
.bits = 8,
|
||||
.int_vref_mv = 4098,
|
||||
.int_vref_mv = 4096,
|
||||
.mode_list = max1238_mode_list,
|
||||
.num_modes = ARRAY_SIZE(max1238_mode_list),
|
||||
.default_mode = s0to11,
|
||||
|
@ -1313,7 +1307,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {
|
|||
},
|
||||
[max11610] = {
|
||||
.bits = 10,
|
||||
.int_vref_mv = 4098,
|
||||
.int_vref_mv = 4096,
|
||||
.mode_list = max1238_mode_list,
|
||||
.num_modes = ARRAY_SIZE(max1238_mode_list),
|
||||
.default_mode = s0to11,
|
||||
|
@ -1373,7 +1367,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {
|
|||
},
|
||||
[max11616] = {
|
||||
.bits = 12,
|
||||
.int_vref_mv = 4098,
|
||||
.int_vref_mv = 4096,
|
||||
.mode_list = max1238_mode_list,
|
||||
.num_modes = ARRAY_SIZE(max1238_mode_list),
|
||||
.default_mode = s0to11,
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
|
711
drivers/iio/adc/vf610_adc.c
Normal file
711
drivers/iio/adc/vf610_adc.c
Normal file
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
* Freescale Vybrid vf610 ADC driver
|
||||
*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "vf610-adc"
|
||||
|
||||
/* Vybrid/IMX ADC registers */
|
||||
#define VF610_REG_ADC_HC0 0x00
|
||||
#define VF610_REG_ADC_HC1 0x04
|
||||
#define VF610_REG_ADC_HS 0x08
|
||||
#define VF610_REG_ADC_R0 0x0c
|
||||
#define VF610_REG_ADC_R1 0x10
|
||||
#define VF610_REG_ADC_CFG 0x14
|
||||
#define VF610_REG_ADC_GC 0x18
|
||||
#define VF610_REG_ADC_GS 0x1c
|
||||
#define VF610_REG_ADC_CV 0x20
|
||||
#define VF610_REG_ADC_OFS 0x24
|
||||
#define VF610_REG_ADC_CAL 0x28
|
||||
#define VF610_REG_ADC_PCTL 0x30
|
||||
|
||||
/* Configuration register field define */
|
||||
#define VF610_ADC_MODE_BIT8 0x00
|
||||
#define VF610_ADC_MODE_BIT10 0x04
|
||||
#define VF610_ADC_MODE_BIT12 0x08
|
||||
#define VF610_ADC_MODE_MASK 0x0c
|
||||
#define VF610_ADC_BUSCLK2_SEL 0x01
|
||||
#define VF610_ADC_ALTCLK_SEL 0x02
|
||||
#define VF610_ADC_ADACK_SEL 0x03
|
||||
#define VF610_ADC_ADCCLK_MASK 0x03
|
||||
#define VF610_ADC_CLK_DIV2 0x20
|
||||
#define VF610_ADC_CLK_DIV4 0x40
|
||||
#define VF610_ADC_CLK_DIV8 0x60
|
||||
#define VF610_ADC_CLK_MASK 0x60
|
||||
#define VF610_ADC_ADLSMP_LONG 0x10
|
||||
#define VF610_ADC_ADSTS_MASK 0x300
|
||||
#define VF610_ADC_ADLPC_EN 0x80
|
||||
#define VF610_ADC_ADHSC_EN 0x400
|
||||
#define VF610_ADC_REFSEL_VALT 0x100
|
||||
#define VF610_ADC_REFSEL_VBG 0x1000
|
||||
#define VF610_ADC_ADTRG_HARD 0x2000
|
||||
#define VF610_ADC_AVGS_8 0x4000
|
||||
#define VF610_ADC_AVGS_16 0x8000
|
||||
#define VF610_ADC_AVGS_32 0xC000
|
||||
#define VF610_ADC_AVGS_MASK 0xC000
|
||||
#define VF610_ADC_OVWREN 0x10000
|
||||
|
||||
/* General control register field define */
|
||||
#define VF610_ADC_ADACKEN 0x1
|
||||
#define VF610_ADC_DMAEN 0x2
|
||||
#define VF610_ADC_ACREN 0x4
|
||||
#define VF610_ADC_ACFGT 0x8
|
||||
#define VF610_ADC_ACFE 0x10
|
||||
#define VF610_ADC_AVGEN 0x20
|
||||
#define VF610_ADC_ADCON 0x40
|
||||
#define VF610_ADC_CAL 0x80
|
||||
|
||||
/* Other field define */
|
||||
#define VF610_ADC_ADCHC(x) ((x) & 0xF)
|
||||
#define VF610_ADC_AIEN (0x1 << 7)
|
||||
#define VF610_ADC_CONV_DISABLE 0x1F
|
||||
#define VF610_ADC_HS_COCO0 0x1
|
||||
#define VF610_ADC_CALF 0x2
|
||||
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
enum clk_sel {
|
||||
VF610_ADCIOC_BUSCLK_SET,
|
||||
VF610_ADCIOC_ALTCLK_SET,
|
||||
VF610_ADCIOC_ADACK_SET,
|
||||
};
|
||||
|
||||
enum vol_ref {
|
||||
VF610_ADCIOC_VR_VREF_SET,
|
||||
VF610_ADCIOC_VR_VALT_SET,
|
||||
VF610_ADCIOC_VR_VBG_SET,
|
||||
};
|
||||
|
||||
enum average_sel {
|
||||
VF610_ADC_SAMPLE_1,
|
||||
VF610_ADC_SAMPLE_4,
|
||||
VF610_ADC_SAMPLE_8,
|
||||
VF610_ADC_SAMPLE_16,
|
||||
VF610_ADC_SAMPLE_32,
|
||||
};
|
||||
|
||||
struct vf610_adc_feature {
|
||||
enum clk_sel clk_sel;
|
||||
enum vol_ref vol_ref;
|
||||
|
||||
int clk_div;
|
||||
int sample_rate;
|
||||
int res_mode;
|
||||
|
||||
bool lpm;
|
||||
bool calibration;
|
||||
bool ovwren;
|
||||
};
|
||||
|
||||
struct vf610_adc {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
|
||||
u32 vref_uv;
|
||||
u32 value;
|
||||
struct regulator *vref;
|
||||
struct vf610_adc_feature adc_feature;
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
#define VF610_ADC_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(2, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(3, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(4, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(5, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(6, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(7, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(8, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(9, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(10, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(11, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(12, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
/*
|
||||
* ADC sample frequency, unit is ADCK cycles.
|
||||
* ADC clk source is ipg clock, which is the same as bus clock.
|
||||
*
|
||||
* ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
|
||||
* SFCAdder: fixed to 6 ADCK cycles
|
||||
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
|
||||
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
|
||||
* LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
|
||||
*
|
||||
* By default, enable 12 bit resolution mode, clock source
|
||||
* set to ipg clock, So get below frequency group:
|
||||
*/
|
||||
static const u32 vf610_sample_freq_avail[5] =
|
||||
{1941176, 559332, 286957, 145374, 73171};
|
||||
|
||||
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
||||
{
|
||||
/* set default Configuration for ADC controller */
|
||||
info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET;
|
||||
info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET;
|
||||
|
||||
info->adc_feature.calibration = true;
|
||||
info->adc_feature.ovwren = true;
|
||||
|
||||
info->adc_feature.clk_div = 1;
|
||||
info->adc_feature.res_mode = 12;
|
||||
info->adc_feature.sample_rate = 1;
|
||||
info->adc_feature.lpm = true;
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
int cfg_data = 0;
|
||||
int gc_data = 0;
|
||||
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_ALTCLK_SET:
|
||||
cfg_data |= VF610_ADC_ALTCLK_SEL;
|
||||
break;
|
||||
case VF610_ADCIOC_ADACK_SET:
|
||||
cfg_data |= VF610_ADC_ADACK_SEL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* low power set for calibration */
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
/* enable high speed for calibration */
|
||||
cfg_data |= VF610_ADC_ADHSC_EN;
|
||||
|
||||
/* voltage reference */
|
||||
switch (adc_feature->vol_ref) {
|
||||
case VF610_ADCIOC_VR_VREF_SET:
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VALT_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VALT;
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VBG_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VBG;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error voltage reference\n");
|
||||
}
|
||||
|
||||
/* data overwrite enable */
|
||||
if (adc_feature->ovwren)
|
||||
cfg_data |= VF610_ADC_OVWREN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_calibration(struct vf610_adc *info)
|
||||
{
|
||||
int adc_gc, hc_cfg;
|
||||
int timeout;
|
||||
|
||||
if (!info->adc_feature.calibration)
|
||||
return;
|
||||
|
||||
/* enable calibration interrupt */
|
||||
hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
|
||||
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (timeout == 0)
|
||||
dev_err(info->dev, "Timeout for adc calibration\n");
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
|
||||
if (adc_gc & VF610_ADC_CALF)
|
||||
dev_err(info->dev, "ADC calibration failed\n");
|
||||
|
||||
info->adc_feature.calibration = false;
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
|
||||
/* low power configuration */
|
||||
cfg_data &= ~VF610_ADC_ADLPC_EN;
|
||||
if (adc_feature->lpm)
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
/* disable high speed */
|
||||
cfg_data &= ~VF610_ADC_ADHSC_EN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
}
|
||||
|
||||
static void vf610_adc_sample_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data, gc_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
gc_data = readl(info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
/* resolution mode */
|
||||
cfg_data &= ~VF610_ADC_MODE_MASK;
|
||||
switch (adc_feature->res_mode) {
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_MODE_BIT8;
|
||||
break;
|
||||
case 10:
|
||||
cfg_data |= VF610_ADC_MODE_BIT10;
|
||||
break;
|
||||
case 12:
|
||||
cfg_data |= VF610_ADC_MODE_BIT12;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error resolution mode\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* clock select and clock divider */
|
||||
cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
|
||||
switch (adc_feature->clk_div) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
cfg_data |= VF610_ADC_CLK_DIV2;
|
||||
break;
|
||||
case 4:
|
||||
cfg_data |= VF610_ADC_CLK_DIV4;
|
||||
break;
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
case 16:
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_BUSCLK_SET:
|
||||
cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error clk divider\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the short sample mode */
|
||||
cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK);
|
||||
|
||||
/* update hardware average selection */
|
||||
cfg_data &= ~VF610_ADC_AVGS_MASK;
|
||||
gc_data &= ~VF610_ADC_AVGEN;
|
||||
switch (adc_feature->sample_rate) {
|
||||
case VF610_ADC_SAMPLE_1:
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_4:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_8:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_8;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_16:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_16;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_32:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_32;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev,
|
||||
"error hardware sample average select\n");
|
||||
}
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_hw_init(struct vf610_adc *info)
|
||||
{
|
||||
/* CFG: Feature set */
|
||||
vf610_adc_cfg_post_set(info);
|
||||
vf610_adc_sample_set(info);
|
||||
|
||||
/* adc calibration */
|
||||
vf610_adc_calibration(info);
|
||||
|
||||
/* CFG: power and speed set */
|
||||
vf610_adc_cfg_set(info);
|
||||
}
|
||||
|
||||
static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = readl(info->regs + VF610_REG_ADC_R0);
|
||||
|
||||
switch (info->adc_feature.res_mode) {
|
||||
case 8:
|
||||
result &= 0xFF;
|
||||
break;
|
||||
case 10:
|
||||
result &= 0x3FF;
|
||||
break;
|
||||
case 12:
|
||||
result &= 0xFFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct vf610_adc *info = (struct vf610_adc *)dev_id;
|
||||
int coco;
|
||||
|
||||
coco = readl(info->regs + VF610_REG_ADC_HS);
|
||||
if (coco & VF610_ADC_HS_COCO0) {
|
||||
info->value = vf610_adc_read_data(info);
|
||||
complete(&info->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171");
|
||||
|
||||
static struct attribute *vf610_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group vf610_attribute_group = {
|
||||
.attrs = vf610_attributes,
|
||||
};
|
||||
|
||||
static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
unsigned int hc_cfg;
|
||||
long ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
hc_cfg = VF610_ADC_ADCHC(chan->channel);
|
||||
hc_cfg |= VF610_ADC_AIEN;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
ret = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = info->value;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = info->vref_uv / 1000;
|
||||
*val2 = info->adc_feature.res_mode;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = vf610_sample_freq_avail[info->adc_feature.sample_rate];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0;
|
||||
i < ARRAY_SIZE(vf610_sample_freq_avail);
|
||||
i++)
|
||||
if (val == vf610_sample_freq_avail[i]) {
|
||||
info->adc_feature.sample_rate = i;
|
||||
vf610_adc_sample_set(info);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
if ((readval == NULL) ||
|
||||
(!(reg % 4) || (reg > VF610_REG_ADC_PCTL)))
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info vf610_adc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &vf610_read_raw,
|
||||
.write_raw = &vf610_write_raw,
|
||||
.debugfs_reg_access = &vf610_adc_reg_access,
|
||||
.attrs = &vf610_attribute_group,
|
||||
};
|
||||
|
||||
static const struct of_device_id vf610_adc_match[] = {
|
||||
{ .compatible = "fsl,vf610-adc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vf610_adc_match);
|
||||
|
||||
static int vf610_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "Failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
ret = PTR_ERR(info->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref))
|
||||
return PTR_ERR(info->vref);
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->vref_uv = regulator_get_voltage(info->vref);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &vf610_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = vf610_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels);
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the clock.\n");
|
||||
goto error_adc_clk_enable;
|
||||
}
|
||||
|
||||
vf610_adc_cfg_init(info);
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
error_iio_device_register:
|
||||
clk_disable_unprepare(info->clk);
|
||||
error_adc_clk_enable:
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vf610_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(info->vref);
|
||||
clk_disable_unprepare(info->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int vf610_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int hc_cfg;
|
||||
|
||||
/* ADC controller enters to stop mode */
|
||||
hc_cfg = readl(info->regs + VF610_REG_ADC_HC0);
|
||||
hc_cfg |= VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
|
||||
vf610_adc_suspend,
|
||||
vf610_adc_resume);
|
||||
|
||||
static struct platform_driver vf610_adc_driver = {
|
||||
.probe = vf610_adc_probe,
|
||||
.remove = vf610_adc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = vf610_adc_match,
|
||||
.pm = &vf610_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(vf610_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
|
||||
MODULE_DESCRIPTION("Freescale VF610 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -139,8 +139,6 @@ static int vprbrd_adc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
1333
drivers/iio/adc/xilinx-xadc-core.c
Normal file
1333
drivers/iio/adc/xilinx-xadc-core.c
Normal file
File diff suppressed because it is too large
Load diff
254
drivers/iio/adc/xilinx-xadc-events.c
Normal file
254
drivers/iio/adc/xilinx-xadc-events.c
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Xilinx XADC driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clauen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "xilinx-xadc.h"
|
||||
|
||||
static const struct iio_chan_spec *xadc_event_to_channel(
|
||||
struct iio_dev *indio_dev, unsigned int event)
|
||||
{
|
||||
switch (event) {
|
||||
case XADC_THRESHOLD_OT_MAX:
|
||||
case XADC_THRESHOLD_TEMP_MAX:
|
||||
return &indio_dev->channels[0];
|
||||
case XADC_THRESHOLD_VCCINT_MAX:
|
||||
case XADC_THRESHOLD_VCCAUX_MAX:
|
||||
return &indio_dev->channels[event];
|
||||
default:
|
||||
return &indio_dev->channels[event-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event)
|
||||
{
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int offset;
|
||||
|
||||
/* Temperature threshold error, we don't handle this yet */
|
||||
if (event == 0)
|
||||
return;
|
||||
|
||||
if (event < 4)
|
||||
offset = event;
|
||||
else
|
||||
offset = event + 4;
|
||||
|
||||
chan = xadc_event_to_channel(indio_dev, event);
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* The temperature channel only supports over-temperature
|
||||
* events.
|
||||
*/
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
} else {
|
||||
/*
|
||||
* For other channels we don't know whether it is a upper or
|
||||
* lower threshold event. Userspace will have to check the
|
||||
* channel value if it wants to know.
|
||||
*/
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
}
|
||||
|
||||
void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for_each_set_bit(i, &events, 8)
|
||||
xadc_handle_event(indio_dev, i);
|
||||
}
|
||||
|
||||
static unsigned xadc_get_threshold_offset(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
offset = XADC_THRESHOLD_OT_MAX;
|
||||
} else {
|
||||
if (chan->channel < 2)
|
||||
offset = chan->channel + 1;
|
||||
else
|
||||
offset = chan->channel + 6;
|
||||
}
|
||||
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
offset += 4;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan)
|
||||
{
|
||||
if (chan->type == IIO_TEMP) {
|
||||
return XADC_ALARM_OT_MASK;
|
||||
} else {
|
||||
switch (chan->channel) {
|
||||
case 0:
|
||||
return XADC_ALARM_VCCINT_MASK;
|
||||
case 1:
|
||||
return XADC_ALARM_VCCAUX_MASK;
|
||||
case 2:
|
||||
return XADC_ALARM_VCCBRAM_MASK;
|
||||
case 3:
|
||||
return XADC_ALARM_VCCPINT_MASK;
|
||||
case 4:
|
||||
return XADC_ALARM_VCCPAUX_MASK;
|
||||
case 5:
|
||||
return XADC_ALARM_VCCODDR_MASK;
|
||||
default:
|
||||
/* We will never get here */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int xadc_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan));
|
||||
}
|
||||
|
||||
int xadc_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
unsigned int alarm = xadc_get_alarm_mask(chan);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
uint16_t cfg, old_cfg;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
|
||||
if (state)
|
||||
xadc->alarm_mask |= alarm;
|
||||
else
|
||||
xadc->alarm_mask &= ~alarm;
|
||||
|
||||
xadc->ops->update_alarm(xadc, xadc->alarm_mask);
|
||||
|
||||
ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
old_cfg = cfg;
|
||||
cfg |= XADC_CONF1_ALARM_MASK;
|
||||
cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */
|
||||
cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */
|
||||
cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */
|
||||
if (old_cfg != cfg)
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg);
|
||||
|
||||
err_out:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register value is msb aligned, the lower 4 bits are ignored */
|
||||
#define XADC_THRESHOLD_VALUE_SHIFT 4
|
||||
|
||||
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
unsigned int offset = xadc_get_threshold_offset(chan, dir);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
*val = xadc->threshold[offset];
|
||||
break;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
*val = xadc->temp_hysteresis;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
int xadc_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
unsigned int offset = xadc_get_threshold_offset(chan, dir);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
val <<= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
|
||||
if (val < 0 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
xadc->threshold[offset] = val;
|
||||
break;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
xadc->temp_hysteresis = val;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* According to the datasheet we need to set the lower 4 bits to
|
||||
* 0x3, otherwise 125 degree celsius will be used as the
|
||||
* threshold.
|
||||
*/
|
||||
val |= 0x3;
|
||||
|
||||
/*
|
||||
* Since we store the hysteresis as relative (to the threshold)
|
||||
* value, but the hardware expects an absolute value we need to
|
||||
* recalcualte this value whenever the hysteresis or the
|
||||
* threshold changes.
|
||||
*/
|
||||
if (xadc->threshold[offset] < xadc->temp_hysteresis)
|
||||
xadc->threshold[offset + 4] = 0;
|
||||
else
|
||||
xadc->threshold[offset + 4] = xadc->threshold[offset] -
|
||||
xadc->temp_hysteresis;
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4),
|
||||
xadc->threshold[offset + 4]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (info == IIO_EV_INFO_VALUE)
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
209
drivers/iio/adc/xilinx-xadc.h
Normal file
209
drivers/iio/adc/xilinx-xadc.h
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Xilinx XADC driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clauen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef __IIO_XILINX_XADC__
|
||||
#define __IIO_XILINX_XADC__
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct iio_dev;
|
||||
struct clk;
|
||||
struct xadc_ops;
|
||||
struct platform_device;
|
||||
|
||||
void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events);
|
||||
|
||||
int xadc_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir);
|
||||
int xadc_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state);
|
||||
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int *val, int *val2);
|
||||
int xadc_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int val, int val2);
|
||||
|
||||
enum xadc_external_mux_mode {
|
||||
XADC_EXTERNAL_MUX_NONE,
|
||||
XADC_EXTERNAL_MUX_SINGLE,
|
||||
XADC_EXTERNAL_MUX_DUAL,
|
||||
};
|
||||
|
||||
struct xadc {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
|
||||
const struct xadc_ops *ops;
|
||||
|
||||
uint16_t threshold[16];
|
||||
uint16_t temp_hysteresis;
|
||||
unsigned int alarm_mask;
|
||||
|
||||
uint16_t *data;
|
||||
|
||||
struct iio_trigger *trigger;
|
||||
struct iio_trigger *convst_trigger;
|
||||
struct iio_trigger *samplerate_trigger;
|
||||
|
||||
enum xadc_external_mux_mode external_mux_mode;
|
||||
|
||||
unsigned int zynq_alarm;
|
||||
unsigned int zynq_masked_alarm;
|
||||
unsigned int zynq_intmask;
|
||||
struct delayed_work zynq_unmask_work;
|
||||
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
struct xadc_ops {
|
||||
int (*read)(struct xadc *, unsigned int, uint16_t *);
|
||||
int (*write)(struct xadc *, unsigned int, uint16_t);
|
||||
int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev,
|
||||
int irq);
|
||||
void (*update_alarm)(struct xadc *, unsigned int);
|
||||
unsigned long (*get_dclk_rate)(struct xadc *);
|
||||
irqreturn_t (*interrupt_handler)(int, void *);
|
||||
irqreturn_t (*threaded_interrupt_handler)(int, void *);
|
||||
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
lockdep_assert_held(&xadc->mutex);
|
||||
return xadc->ops->read(xadc, reg, val);
|
||||
}
|
||||
|
||||
static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
lockdep_assert_held(&xadc->mutex);
|
||||
return xadc->ops->write(xadc, reg, val);
|
||||
}
|
||||
|
||||
static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
ret = _xadc_read_adc_reg(xadc, reg, val);
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
ret = _xadc_write_adc_reg(xadc, reg, val);
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XADC hardmacro register definitions */
|
||||
#define XADC_REG_TEMP 0x00
|
||||
#define XADC_REG_VCCINT 0x01
|
||||
#define XADC_REG_VCCAUX 0x02
|
||||
#define XADC_REG_VPVN 0x03
|
||||
#define XADC_REG_VREFP 0x04
|
||||
#define XADC_REG_VREFN 0x05
|
||||
#define XADC_REG_VCCBRAM 0x06
|
||||
|
||||
#define XADC_REG_VCCPINT 0x0d
|
||||
#define XADC_REG_VCCPAUX 0x0e
|
||||
#define XADC_REG_VCCO_DDR 0x0f
|
||||
#define XADC_REG_VAUX(x) (0x10 + (x))
|
||||
|
||||
#define XADC_REG_MAX_TEMP 0x20
|
||||
#define XADC_REG_MAX_VCCINT 0x21
|
||||
#define XADC_REG_MAX_VCCAUX 0x22
|
||||
#define XADC_REG_MAX_VCCBRAM 0x23
|
||||
#define XADC_REG_MIN_TEMP 0x24
|
||||
#define XADC_REG_MIN_VCCINT 0x25
|
||||
#define XADC_REG_MIN_VCCAUX 0x26
|
||||
#define XADC_REG_MIN_VCCBRAM 0x27
|
||||
#define XADC_REG_MAX_VCCPINT 0x28
|
||||
#define XADC_REG_MAX_VCCPAUX 0x29
|
||||
#define XADC_REG_MAX_VCCO_DDR 0x2a
|
||||
#define XADC_REG_MIN_VCCPINT 0x2b
|
||||
#define XADC_REG_MIN_VCCPAUX 0x2c
|
||||
#define XADC_REG_MIN_VCCO_DDR 0x2d
|
||||
|
||||
#define XADC_REG_CONF0 0x40
|
||||
#define XADC_REG_CONF1 0x41
|
||||
#define XADC_REG_CONF2 0x42
|
||||
#define XADC_REG_SEQ(x) (0x48 + (x))
|
||||
#define XADC_REG_INPUT_MODE(x) (0x4c + (x))
|
||||
#define XADC_REG_THRESHOLD(x) (0x50 + (x))
|
||||
|
||||
#define XADC_REG_FLAG 0x3f
|
||||
|
||||
#define XADC_CONF0_EC BIT(9)
|
||||
#define XADC_CONF0_ACQ BIT(8)
|
||||
#define XADC_CONF0_MUX BIT(11)
|
||||
#define XADC_CONF0_CHAN(x) (x)
|
||||
|
||||
#define XADC_CONF1_SEQ_MASK (0xf << 12)
|
||||
#define XADC_CONF1_SEQ_DEFAULT (0 << 12)
|
||||
#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12)
|
||||
#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12)
|
||||
#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12)
|
||||
#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12)
|
||||
#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12)
|
||||
#define XADC_CONF1_ALARM_MASK 0x0f0f
|
||||
|
||||
#define XADC_CONF2_DIV_MASK 0xff00
|
||||
#define XADC_CONF2_DIV_OFFSET 8
|
||||
|
||||
#define XADC_CONF2_PD_MASK (0x3 << 4)
|
||||
#define XADC_CONF2_PD_NONE (0x0 << 4)
|
||||
#define XADC_CONF2_PD_ADC_B (0x2 << 4)
|
||||
#define XADC_CONF2_PD_BOTH (0x3 << 4)
|
||||
|
||||
#define XADC_ALARM_TEMP_MASK BIT(0)
|
||||
#define XADC_ALARM_VCCINT_MASK BIT(1)
|
||||
#define XADC_ALARM_VCCAUX_MASK BIT(2)
|
||||
#define XADC_ALARM_OT_MASK BIT(3)
|
||||
#define XADC_ALARM_VCCBRAM_MASK BIT(4)
|
||||
#define XADC_ALARM_VCCPINT_MASK BIT(5)
|
||||
#define XADC_ALARM_VCCPAUX_MASK BIT(6)
|
||||
#define XADC_ALARM_VCCODDR_MASK BIT(7)
|
||||
|
||||
#define XADC_THRESHOLD_TEMP_MAX 0x0
|
||||
#define XADC_THRESHOLD_VCCINT_MAX 0x1
|
||||
#define XADC_THRESHOLD_VCCAUX_MAX 0x2
|
||||
#define XADC_THRESHOLD_OT_MAX 0x3
|
||||
#define XADC_THRESHOLD_TEMP_MIN 0x4
|
||||
#define XADC_THRESHOLD_VCCINT_MIN 0x5
|
||||
#define XADC_THRESHOLD_VCCAUX_MIN 0x6
|
||||
#define XADC_THRESHOLD_OT_MIN 0x7
|
||||
#define XADC_THRESHOLD_VCCBRAM_MAX 0x8
|
||||
#define XADC_THRESHOLD_VCCPINT_MAX 0x9
|
||||
#define XADC_THRESHOLD_VCCPAUX_MAX 0xa
|
||||
#define XADC_THRESHOLD_VCCODDR_MAX 0xb
|
||||
#define XADC_THRESHOLD_VCCBRAM_MIN 0xc
|
||||
#define XADC_THRESHOLD_VCCPINT_MIN 0xd
|
||||
#define XADC_THRESHOLD_VCCPAUX_MIN 0xe
|
||||
#define XADC_THRESHOLD_VCCODDR_MIN 0xf
|
||||
|
||||
#endif
|
|
@ -46,10 +46,8 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
struct iio_channel *chan;
|
||||
|
||||
cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
|
||||
if (cb_buff == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
if (cb_buff == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
iio_buffer_init(&cb_buff->buffer);
|
||||
|
||||
|
@ -91,7 +89,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
iio_channel_release_all(cb_buff->channels);
|
||||
error_free_cb_buff:
|
||||
kfree(cb_buff);
|
||||
error_ret:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
|
||||
|
|
|
@ -92,7 +92,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
|
|||
ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ad7303_get_vref(struct ad7303_state *st,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
|
@ -12,4 +12,14 @@ config DHT11
|
|||
Other sensors should work as well as long as they speak the
|
||||
same protocol.
|
||||
|
||||
config SI7005
|
||||
tristate "SI7005 relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Silabs Si7005 relative
|
||||
humidity and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called si7005.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_SI7005) += si7005.o
|
||||
|
|
189
drivers/iio/humidity/si7005.c
Normal file
189
drivers/iio/humidity/si7005.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* si7005.c - Support for Silabs Si7005 humidity and temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x40)
|
||||
*
|
||||
* TODO: heater, fast mode, processed mode (temp. / linearity compensation)
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define SI7005_STATUS 0x00
|
||||
#define SI7005_DATA 0x01 /* 16-bit, MSB */
|
||||
#define SI7005_CONFIG 0x03
|
||||
#define SI7005_ID 0x11
|
||||
|
||||
#define SI7005_STATUS_NRDY BIT(0)
|
||||
#define SI7005_CONFIG_TEMP BIT(4)
|
||||
#define SI7005_CONFIG_START BIT(0)
|
||||
|
||||
#define SI7005_ID_7005 0x50
|
||||
#define SI7005_ID_7015 0xf0
|
||||
|
||||
struct si7005_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 config;
|
||||
};
|
||||
|
||||
static int si7005_read_measurement(struct si7005_data *data, bool temp)
|
||||
{
|
||||
int tries = 50;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, SI7005_CONFIG,
|
||||
data->config | SI7005_CONFIG_START |
|
||||
(temp ? SI7005_CONFIG_TEMP : 0));
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
while (tries-- > 0) {
|
||||
msleep(20);
|
||||
ret = i2c_smbus_read_byte_data(data->client, SI7005_STATUS);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
if (!(ret & SI7005_STATUS_NRDY))
|
||||
break;
|
||||
}
|
||||
if (tries < 0) {
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, SI7005_DATA);
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int si7005_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct si7005_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = si7005_read_measurement(data, chan->type == IIO_TEMP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_TEMP) {
|
||||
*val = 7;
|
||||
*val2 = 812500;
|
||||
} else {
|
||||
*val = 3;
|
||||
*val2 = 906250;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (chan->type == IIO_TEMP)
|
||||
*val = -50 * 32 * 4;
|
||||
else
|
||||
*val = -24 * 16 * 16;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec si7005_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info si7005_info = {
|
||||
.read_raw = si7005_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int si7005_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct si7005_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &si7005_info;
|
||||
|
||||
indio_dev->channels = si7005_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(si7005_channels);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, SI7005_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != SI7005_ID_7005 && ret != SI7005_ID_7015)
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, SI7005_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->config = ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si7005_id[] = {
|
||||
{ "si7005", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7005_id);
|
||||
|
||||
static struct i2c_driver si7005_driver = {
|
||||
.driver = {
|
||||
.name = "si7005",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = si7005_probe,
|
||||
.id_table = si7005_id,
|
||||
};
|
||||
module_i2c_driver(si7005_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Silabs Si7005 humidity and temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -25,6 +25,8 @@ config ADIS16480
|
|||
Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
|
||||
ADIS16485, ADIS16488 inertial sensors.
|
||||
|
||||
source "drivers/iio/imu/inv_mpu6050/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
config IIO_ADIS_LIB
|
||||
|
@ -38,5 +40,3 @@ config IIO_ADIS_LIB_BUFFER
|
|||
help
|
||||
A set of buffer helper functions for the Analog Devices ADIS* device
|
||||
family.
|
||||
|
||||
source "drivers/iio/imu/inv_mpu6050/Kconfig"
|
||||
|
|
|
@ -281,7 +281,7 @@ static ssize_t adis16400_write_frequency(struct device *dev,
|
|||
st->variant->set_freq(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Power down the device */
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -117,7 +116,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
|
|||
return result;
|
||||
|
||||
if (en) {
|
||||
/* Wait for output stablize */
|
||||
/* Wait for output stabilize */
|
||||
msleep(INV_MPU6050_TEMP_UP_TIME);
|
||||
if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
|
||||
/* switch internal clock to PLL */
|
||||
|
|
|
@ -126,35 +126,35 @@ struct inv_mpu6050_state {
|
|||
#define INV_MPU6050_REG_SAMPLE_RATE_DIV 0x19
|
||||
#define INV_MPU6050_REG_CONFIG 0x1A
|
||||
#define INV_MPU6050_REG_GYRO_CONFIG 0x1B
|
||||
#define INV_MPU6050_REG_ACCEL_CONFIG 0x1C
|
||||
#define INV_MPU6050_REG_ACCEL_CONFIG 0x1C
|
||||
|
||||
#define INV_MPU6050_REG_FIFO_EN 0x23
|
||||
#define INV_MPU6050_BIT_ACCEL_OUT 0x08
|
||||
#define INV_MPU6050_BITS_GYRO_OUT 0x70
|
||||
#define INV_MPU6050_BIT_ACCEL_OUT 0x08
|
||||
#define INV_MPU6050_BITS_GYRO_OUT 0x70
|
||||
|
||||
#define INV_MPU6050_REG_INT_ENABLE 0x38
|
||||
#define INV_MPU6050_BIT_DATA_RDY_EN 0x01
|
||||
#define INV_MPU6050_BIT_DMP_INT_EN 0x02
|
||||
#define INV_MPU6050_BIT_DATA_RDY_EN 0x01
|
||||
#define INV_MPU6050_BIT_DMP_INT_EN 0x02
|
||||
|
||||
#define INV_MPU6050_REG_RAW_ACCEL 0x3B
|
||||
#define INV_MPU6050_REG_TEMPERATURE 0x41
|
||||
#define INV_MPU6050_REG_RAW_GYRO 0x43
|
||||
|
||||
#define INV_MPU6050_REG_USER_CTRL 0x6A
|
||||
#define INV_MPU6050_BIT_FIFO_RST 0x04
|
||||
#define INV_MPU6050_BIT_DMP_RST 0x08
|
||||
#define INV_MPU6050_BIT_I2C_MST_EN 0x20
|
||||
#define INV_MPU6050_BIT_FIFO_EN 0x40
|
||||
#define INV_MPU6050_BIT_DMP_EN 0x80
|
||||
#define INV_MPU6050_BIT_FIFO_RST 0x04
|
||||
#define INV_MPU6050_BIT_DMP_RST 0x08
|
||||
#define INV_MPU6050_BIT_I2C_MST_EN 0x20
|
||||
#define INV_MPU6050_BIT_FIFO_EN 0x40
|
||||
#define INV_MPU6050_BIT_DMP_EN 0x80
|
||||
|
||||
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
|
||||
#define INV_MPU6050_BIT_H_RESET 0x80
|
||||
#define INV_MPU6050_BIT_SLEEP 0x40
|
||||
#define INV_MPU6050_BIT_CLK_MASK 0x7
|
||||
#define INV_MPU6050_BIT_H_RESET 0x80
|
||||
#define INV_MPU6050_BIT_SLEEP 0x40
|
||||
#define INV_MPU6050_BIT_CLK_MASK 0x7
|
||||
|
||||
#define INV_MPU6050_REG_PWR_MGMT_2 0x6C
|
||||
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
|
||||
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
|
||||
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
|
||||
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
|
||||
|
||||
#define INV_MPU6050_REG_FIFO_COUNT_H 0x72
|
||||
#define INV_MPU6050_REG_FIFO_R_W 0x74
|
||||
|
@ -180,10 +180,10 @@ struct inv_mpu6050_state {
|
|||
|
||||
/* init parameters */
|
||||
#define INV_MPU6050_INIT_FIFO_RATE 50
|
||||
#define INV_MPU6050_TIME_STAMP_TOR 5
|
||||
#define INV_MPU6050_MAX_FIFO_RATE 1000
|
||||
#define INV_MPU6050_MIN_FIFO_RATE 4
|
||||
#define INV_MPU6050_ONE_K_HZ 1000
|
||||
#define INV_MPU6050_TIME_STAMP_TOR 5
|
||||
#define INV_MPU6050_MAX_FIFO_RATE 1000
|
||||
#define INV_MPU6050_MIN_FIFO_RATE 4
|
||||
#define INV_MPU6050_ONE_K_HZ 1000
|
||||
|
||||
/* scan element definition */
|
||||
enum inv_mpu6050_scan {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -264,7 +264,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
&indio_dev->dev,
|
||||
&buffer->scan_el_dev_attr_list);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount++;
|
||||
ret = __iio_add_chan_devattr("type",
|
||||
chan,
|
||||
|
@ -275,7 +275,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
&indio_dev->dev,
|
||||
&buffer->scan_el_dev_attr_list);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount++;
|
||||
if (chan->type != IIO_TIMESTAMP)
|
||||
ret = __iio_add_chan_devattr("en",
|
||||
|
@ -296,10 +296,9 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
&indio_dev->dev,
|
||||
&buffer->scan_el_dev_attr_list);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount++;
|
||||
ret = attrcount;
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -553,13 +552,13 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
|||
if (indio_dev->setup_ops->predisable) {
|
||||
ret = indio_dev->setup_ops->predisable(indio_dev);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
if (indio_dev->setup_ops->postdisable) {
|
||||
ret = indio_dev->setup_ops->postdisable(indio_dev);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Keep a copy of current setup to allow roll back */
|
||||
|
@ -613,7 +612,7 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
|||
else {
|
||||
kfree(compound_mask);
|
||||
ret = -EINVAL;
|
||||
goto error_ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -696,13 +695,10 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
|||
if (indio_dev->setup_ops->postdisable)
|
||||
indio_dev->setup_ops->postdisable(indio_dev);
|
||||
error_remove_inserted:
|
||||
|
||||
if (insert_buffer)
|
||||
iio_buffer_deactivate(insert_buffer);
|
||||
indio_dev->active_scan_mask = old_mask;
|
||||
kfree(compound_mask);
|
||||
error_ret:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -540,7 +540,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
enum iio_shared_by shared_by)
|
||||
{
|
||||
int ret = 0;
|
||||
char *name_format = NULL;
|
||||
char *name = NULL;
|
||||
char *full_postfix;
|
||||
sysfs_attr_init(&dev_attr->attr);
|
||||
|
||||
|
@ -558,7 +558,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
->channel2],
|
||||
postfix);
|
||||
} else {
|
||||
if (chan->extend_name == NULL)
|
||||
if (chan->extend_name == NULL || shared_by != IIO_SEPARATE)
|
||||
full_postfix = kstrdup(postfix, GFP_KERNEL);
|
||||
else
|
||||
full_postfix = kasprintf(GFP_KERNEL,
|
||||
|
@ -572,16 +572,15 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
if (chan->differential) { /* Differential can not have modifier */
|
||||
switch (shared_by) {
|
||||
case IIO_SHARED_BY_ALL:
|
||||
name_format = kasprintf(GFP_KERNEL, "%s", full_postfix);
|
||||
name = kasprintf(GFP_KERNEL, "%s", full_postfix);
|
||||
break;
|
||||
case IIO_SHARED_BY_DIR:
|
||||
name_format = kasprintf(GFP_KERNEL, "%s_%s",
|
||||
name = kasprintf(GFP_KERNEL, "%s_%s",
|
||||
iio_direction[chan->output],
|
||||
full_postfix);
|
||||
break;
|
||||
case IIO_SHARED_BY_TYPE:
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
|
||||
name = kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
|
@ -593,8 +592,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
ret = -EINVAL;
|
||||
goto error_free_full_postfix;
|
||||
}
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL,
|
||||
name = kasprintf(GFP_KERNEL,
|
||||
"%s_%s%d-%s%d_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
|
@ -607,16 +605,15 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
} else { /* Single ended */
|
||||
switch (shared_by) {
|
||||
case IIO_SHARED_BY_ALL:
|
||||
name_format = kasprintf(GFP_KERNEL, "%s", full_postfix);
|
||||
name = kasprintf(GFP_KERNEL, "%s", full_postfix);
|
||||
break;
|
||||
case IIO_SHARED_BY_DIR:
|
||||
name_format = kasprintf(GFP_KERNEL, "%s_%s",
|
||||
name = kasprintf(GFP_KERNEL, "%s_%s",
|
||||
iio_direction[chan->output],
|
||||
full_postfix);
|
||||
break;
|
||||
case IIO_SHARED_BY_TYPE:
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
name = kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
full_postfix);
|
||||
|
@ -624,33 +621,24 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
|
||||
case IIO_SEPARATE:
|
||||
if (chan->indexed)
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s%d_%s",
|
||||
name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
chan->channel,
|
||||
full_postfix);
|
||||
else
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
name = kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
full_postfix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name_format == NULL) {
|
||||
if (name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_full_postfix;
|
||||
}
|
||||
dev_attr->attr.name = kasprintf(GFP_KERNEL,
|
||||
name_format,
|
||||
chan->channel,
|
||||
chan->channel2);
|
||||
if (dev_attr->attr.name == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_name_format;
|
||||
}
|
||||
dev_attr->attr.name = name;
|
||||
|
||||
if (readfunc) {
|
||||
dev_attr->attr.mode |= S_IRUGO;
|
||||
|
@ -661,8 +649,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
dev_attr->attr.mode |= S_IWUSR;
|
||||
dev_attr->store = writefunc;
|
||||
}
|
||||
error_free_name_format:
|
||||
kfree(name_format);
|
||||
|
||||
error_free_full_postfix:
|
||||
kfree(full_postfix);
|
||||
|
||||
|
@ -692,10 +679,8 @@ int __iio_add_chan_devattr(const char *postfix,
|
|||
struct iio_dev_attr *iio_attr, *t;
|
||||
|
||||
iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
|
||||
if (iio_attr == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
if (iio_attr == NULL)
|
||||
return -ENOMEM;
|
||||
ret = __iio_device_attr_init(&iio_attr->dev_attr,
|
||||
postfix, chan,
|
||||
readfunc, writefunc, shared_by);
|
||||
|
@ -720,7 +705,6 @@ int __iio_add_chan_devattr(const char *postfix,
|
|||
__iio_device_attr_deinit(&iio_attr->dev_attr);
|
||||
error_iio_dev_attr_free:
|
||||
kfree(iio_attr);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1134,7 +1118,7 @@ int iio_device_register(struct iio_dev *indio_dev)
|
|||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Failed to register debugfs interfaces\n");
|
||||
goto error_ret;
|
||||
return ret;
|
||||
}
|
||||
ret = iio_device_register_sysfs(indio_dev);
|
||||
if (ret) {
|
||||
|
@ -1175,7 +1159,6 @@ int iio_device_register(struct iio_dev *indio_dev)
|
|||
iio_device_unregister_sysfs(indio_dev);
|
||||
error_unreg_debugfs:
|
||||
iio_device_unregister_debugfs(indio_dev);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_device_register);
|
||||
|
|
|
@ -40,6 +40,7 @@ struct iio_event_interface {
|
|||
struct list_head dev_attr_list;
|
||||
unsigned long flags;
|
||||
struct attribute_group group;
|
||||
struct mutex read_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -47,16 +48,17 @@ struct iio_event_interface {
|
|||
* @indio_dev: IIO device structure
|
||||
* @ev_code: What event
|
||||
* @timestamp: When the event occurred
|
||||
*
|
||||
* Note: The caller must make sure that this function is not running
|
||||
* concurrently for the same indio_dev more than once.
|
||||
**/
|
||||
int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
||||
{
|
||||
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
||||
struct iio_event_data ev;
|
||||
unsigned long flags;
|
||||
int copied;
|
||||
|
||||
/* Does anyone care? */
|
||||
spin_lock_irqsave(&ev_int->wait.lock, flags);
|
||||
if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
|
||||
|
||||
ev.id = ev_code;
|
||||
|
@ -64,9 +66,8 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
|||
|
||||
copied = kfifo_put(&ev_int->det_events, ev);
|
||||
if (copied != 0)
|
||||
wake_up_locked_poll(&ev_int->wait, POLLIN);
|
||||
wake_up_poll(&ev_int->wait, POLLIN);
|
||||
}
|
||||
spin_unlock_irqrestore(&ev_int->wait.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -87,10 +88,8 @@ static unsigned int iio_event_poll(struct file *filep,
|
|||
|
||||
poll_wait(filep, &ev_int->wait, wait);
|
||||
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
if (!kfifo_is_empty(&ev_int->det_events))
|
||||
events = POLLIN | POLLRDNORM;
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
@ -111,31 +110,40 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
|
|||
if (count < sizeof(struct iio_event_data))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
if (kfifo_is_empty(&ev_int->det_events)) {
|
||||
if (filep->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
goto error_unlock;
|
||||
}
|
||||
/* Blocking on device; waiting for something to be there */
|
||||
ret = wait_event_interruptible_locked_irq(ev_int->wait,
|
||||
do {
|
||||
if (kfifo_is_empty(&ev_int->det_events)) {
|
||||
if (filep->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = wait_event_interruptible(ev_int->wait,
|
||||
!kfifo_is_empty(&ev_int->det_events) ||
|
||||
indio_dev->info == NULL);
|
||||
if (ret)
|
||||
goto error_unlock;
|
||||
if (indio_dev->info == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto error_unlock;
|
||||
if (ret)
|
||||
return ret;
|
||||
if (indio_dev->info == NULL)
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Single access device so no one else can get the data */
|
||||
}
|
||||
|
||||
ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
|
||||
if (mutex_lock_interruptible(&ev_int->read_lock))
|
||||
return -ERESTARTSYS;
|
||||
ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
|
||||
mutex_unlock(&ev_int->read_lock);
|
||||
|
||||
error_unlock:
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret ? ret : copied;
|
||||
/*
|
||||
* If we couldn't read anything from the fifo (a different
|
||||
* thread might have been faster) we either return -EAGAIN if
|
||||
* the file descriptor is non-blocking, otherwise we go back to
|
||||
* sleep and wait for more data to arrive.
|
||||
*/
|
||||
if (copied == 0 && (filep->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
} while (copied == 0);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
|
||||
|
@ -143,15 +151,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)
|
|||
struct iio_dev *indio_dev = filep->private_data;
|
||||
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
||||
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
||||
/*
|
||||
* In order to maintain a clean state for reopening,
|
||||
* clear out any awaiting events. The mask will prevent
|
||||
* any new __iio_push_event calls running.
|
||||
*/
|
||||
kfifo_reset_out(&ev_int->det_events);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
||||
|
||||
iio_device_put(indio_dev);
|
||||
|
||||
|
@ -174,22 +174,20 @@ int iio_event_getfd(struct iio_dev *indio_dev)
|
|||
if (ev_int == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
if (__test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags))
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
|
||||
iio_device_get(indio_dev);
|
||||
|
||||
fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,
|
||||
indio_dev, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
||||
spin_unlock_irq(&ev_int->wait.lock);
|
||||
clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);
|
||||
iio_device_put(indio_dev);
|
||||
} else {
|
||||
kfifo_reset_out(&ev_int->det_events);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -366,32 +364,31 @@ static int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
|
|||
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
||||
IIO_SEPARATE, &chan->event_spec[i].mask_separate);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
||||
IIO_SHARED_BY_TYPE,
|
||||
&chan->event_spec[i].mask_shared_by_type);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
||||
IIO_SHARED_BY_DIR,
|
||||
&chan->event_spec[i].mask_shared_by_dir);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
ret = iio_device_add_event(indio_dev, chan, i, type, dir,
|
||||
IIO_SHARED_BY_ALL,
|
||||
&chan->event_spec[i].mask_shared_by_all);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
}
|
||||
ret = attrcount;
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -425,6 +422,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int)
|
|||
{
|
||||
INIT_KFIFO(ev_int->det_events);
|
||||
init_waitqueue_head(&ev_int->wait);
|
||||
mutex_init(&ev_int->read_lock);
|
||||
}
|
||||
|
||||
static const char *iio_event_group_name = "events";
|
||||
|
@ -440,10 +438,8 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
|
|||
|
||||
indio_dev->event_interface =
|
||||
kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL);
|
||||
if (indio_dev->event_interface == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
if (indio_dev->event_interface == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list);
|
||||
|
||||
|
@ -489,8 +485,6 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
|
|||
error_free_setup_event_lines:
|
||||
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
|
||||
kfree(indio_dev->event_interface);
|
||||
error_ret:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,10 +62,9 @@ int iio_trigger_register(struct iio_trigger *trig_info)
|
|||
int ret;
|
||||
|
||||
trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL);
|
||||
if (trig_info->id < 0) {
|
||||
ret = trig_info->id;
|
||||
goto error_ret;
|
||||
}
|
||||
if (trig_info->id < 0)
|
||||
return trig_info->id;
|
||||
|
||||
/* Set the name used for the sysfs directory etc */
|
||||
dev_set_name(&trig_info->dev, "trigger%ld",
|
||||
(unsigned long) trig_info->id);
|
||||
|
@ -83,7 +82,6 @@ int iio_trigger_register(struct iio_trigger *trig_info)
|
|||
|
||||
error_unregister_id:
|
||||
ida_simple_remove(&iio_trigger_ida, trig_info->id);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_trigger_register);
|
||||
|
@ -234,13 +232,12 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig,
|
|||
if (trig->ops && trig->ops->set_trigger_state && no_other_users) {
|
||||
ret = trig->ops->set_trigger_state(trig, false);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
}
|
||||
iio_trigger_put_irq(trig, pf->irq);
|
||||
free_irq(pf->irq, pf);
|
||||
module_put(pf->indio_dev->info->driver_module);
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,20 @@ config HID_SENSOR_ALS
|
|||
Say yes here to build support for the HID SENSOR
|
||||
Ambient light sensor.
|
||||
|
||||
config HID_SENSOR_PROX
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID PROX"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Proximity sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hid-sensor-prox.
|
||||
|
||||
config SENSORS_LM3533
|
||||
tristate "LM3533 ambient light sensor"
|
||||
depends on MFD_LM3533
|
||||
|
@ -90,6 +104,18 @@ config SENSORS_LM3533
|
|||
changes. The ALS-control output values can be set per zone for the
|
||||
three current output channels.
|
||||
|
||||
config LTR501
|
||||
tristate "LTR-501ALS-01 light sensor"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for the Lite-On LTR-501ALS-01
|
||||
ambient light and proximity sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ltr501.
|
||||
|
||||
config TCS3472
|
||||
tristate "TAOS TCS3472 color light-to-digital converter"
|
||||
depends on I2C
|
||||
|
|
|
@ -9,7 +9,9 @@ obj-$(CONFIG_CM32181) += cm32181.o
|
|||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_LTR501) += ltr501.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -120,7 +119,6 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
|
|||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adjd_s311_data *data = iio_priv(indio_dev);
|
||||
s64 time_ns = iio_get_time_ns();
|
||||
int len = 0;
|
||||
int i, j = 0;
|
||||
|
||||
int ret = adjd_s311_req_data(indio_dev);
|
||||
|
@ -135,7 +133,6 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
|
|||
goto done;
|
||||
|
||||
data->buffer[j++] = ret & ADJD_S311_DATA_MASK;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, time_ns);
|
||||
|
|
375
drivers/iio/light/hid-sensor-prox.c
Normal file
375
drivers/iio/light/hid-sensor-prox.c
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2014, 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_PRESENCE 0
|
||||
|
||||
struct prox_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info prox_attr;
|
||||
u32 human_presence;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec prox_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_PRESENCE,
|
||||
}
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void prox_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is u32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(u32) * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int prox_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct prox_state *prox_state = iio_priv(indio_dev);
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret;
|
||||
int ret_type;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->scan_index) {
|
||||
case CHANNEL_SCAN_INDEX_PRESENCE:
|
||||
report_id = prox_state->prox_attr.report_id;
|
||||
address =
|
||||
HID_USAGE_SENSOR_HUMAN_PRESENCE;
|
||||
break;
|
||||
default:
|
||||
report_id = -1;
|
||||
break;
|
||||
}
|
||||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
prox_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_PROX, address,
|
||||
report_id);
|
||||
else {
|
||||
*val = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = prox_state->prox_attr.units;
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = hid_sensor_convert_exponent(
|
||||
prox_state->prox_attr.unit_expo);
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_read_samp_freq_value(
|
||||
&prox_state->common_attributes, val, val2);
|
||||
ret_type = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_read_raw_hyst_value(
|
||||
&prox_state->common_attributes, val, val2);
|
||||
ret_type = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret_type = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int prox_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct prox_state *prox_state = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_write_samp_freq_value(
|
||||
&prox_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_write_raw_hyst_value(
|
||||
&prox_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info prox_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &prox_read_raw,
|
||||
.write_raw = &prox_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int prox_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct prox_state *prox_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "prox_proc_event [%d]\n",
|
||||
prox_state->common_attributes.data_ready);
|
||||
if (prox_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
&prox_state->human_presence,
|
||||
sizeof(prox_state->human_presence));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int prox_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
size_t raw_len, char *raw_data,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct prox_state *prox_state = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_HUMAN_PRESENCE:
|
||||
prox_state->human_presence = *(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id*/
|
||||
static int prox_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned usage_id,
|
||||
struct prox_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_HUMAN_PRESENCE,
|
||||
&st->prox_attr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
prox_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESENCE,
|
||||
st->prox_attr.size);
|
||||
|
||||
dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index,
|
||||
st->prox_attr.report_id);
|
||||
|
||||
/* Set Sensitivity field ids, when there is no individual modifier */
|
||||
if (st->common_attributes.sensitivity.index < 0) {
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
|
||||
HID_USAGE_SENSOR_DATA_PRESENCE,
|
||||
&st->common_attributes.sensitivity);
|
||||
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
|
||||
st->common_attributes.sensitivity.index,
|
||||
st->common_attributes.sensitivity.report_id);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int hid_prox_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "prox";
|
||||
struct iio_dev *indio_dev;
|
||||
struct prox_state *prox_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct prox_state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
prox_state = iio_priv(indio_dev);
|
||||
prox_state->common_attributes.hsdev = hsdev;
|
||||
prox_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX,
|
||||
&prox_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(prox_channels, sizeof(prox_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = prox_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_PROX, prox_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(prox_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &prox_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
prox_state->common_attributes.data_ready = false;
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&prox_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
goto error_unreg_buffer_funcs;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
prox_state->callbacks.send_event = prox_proc_event;
|
||||
prox_state->callbacks.capture_sample = prox_capture_sample;
|
||||
prox_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PROX,
|
||||
&prox_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_iio_unreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_iio_unreg:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(&prox_state->common_attributes);
|
||||
error_unreg_buffer_funcs:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_dev_mem:
|
||||
kfree(indio_dev->channels);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_prox_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct prox_state *prox_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PROX);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&prox_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_prox_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200011",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_prox_ids);
|
||||
|
||||
static struct platform_driver hid_prox_platform_driver = {
|
||||
.id_table = hid_prox_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = hid_prox_probe,
|
||||
.remove = hid_prox_remove,
|
||||
};
|
||||
module_platform_driver(hid_prox_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Proximity");
|
||||
MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
445
drivers/iio/light/ltr501.c
Normal file
445
drivers/iio/light/ltr501.c
Normal file
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
* ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor
|
||||
*
|
||||
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* 7-bit I2C slave address 0x23
|
||||
*
|
||||
* TODO: interrupt, threshold, measurement rate, IR LED characteristics
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define LTR501_DRV_NAME "ltr501"
|
||||
|
||||
#define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */
|
||||
#define LTR501_PS_CONTR 0x81 /* PS operation mode */
|
||||
#define LTR501_PART_ID 0x86
|
||||
#define LTR501_MANUFAC_ID 0x87
|
||||
#define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */
|
||||
#define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */
|
||||
#define LTR501_ALS_PS_STATUS 0x8c
|
||||
#define LTR501_PS_DATA 0x8d /* 16-bit, little endian */
|
||||
|
||||
#define LTR501_ALS_CONTR_SW_RESET BIT(2)
|
||||
#define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2))
|
||||
#define LTR501_CONTR_PS_GAIN_SHIFT 2
|
||||
#define LTR501_CONTR_ALS_GAIN_MASK BIT(3)
|
||||
#define LTR501_CONTR_ACTIVE BIT(1)
|
||||
|
||||
#define LTR501_STATUS_ALS_RDY BIT(2)
|
||||
#define LTR501_STATUS_PS_RDY BIT(0)
|
||||
|
||||
#define LTR501_PS_DATA_MASK 0x7ff
|
||||
|
||||
struct ltr501_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock_als, lock_ps;
|
||||
u8 als_contr, ps_contr;
|
||||
};
|
||||
|
||||
static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
|
||||
{
|
||||
int tries = 100;
|
||||
int ret;
|
||||
|
||||
while (tries--) {
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
LTR501_ALS_PS_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((ret & drdy_mask) == drdy_mask)
|
||||
return 0;
|
||||
msleep(25);
|
||||
}
|
||||
|
||||
dev_err(&data->client->dev, "ltr501_drdy() failed, data not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2])
|
||||
{
|
||||
int ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* always read both ALS channels in given order */
|
||||
return i2c_smbus_read_i2c_block_data(data->client,
|
||||
LTR501_ALS_DATA1, 2 * sizeof(__le16), (u8 *) buf);
|
||||
}
|
||||
|
||||
static int ltr501_read_ps(struct ltr501_data *data)
|
||||
{
|
||||
int ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return i2c_smbus_read_word_data(data->client, LTR501_PS_DATA);
|
||||
}
|
||||
|
||||
#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared) { \
|
||||
.type = IIO_INTENSITY, \
|
||||
.modified = 1, \
|
||||
.address = (_addr), \
|
||||
.channel2 = (_mod), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = (_shared), \
|
||||
.scan_index = (_idx), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
} \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltr501_channels[] = {
|
||||
LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0),
|
||||
LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR,
|
||||
BIT(IIO_CHAN_INFO_SCALE)),
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.address = LTR501_PS_DATA,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 2,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 11,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const int ltr501_ps_gain[4][2] = {
|
||||
{1, 0}, {0, 250000}, {0, 125000}, {0, 62500}
|
||||
};
|
||||
|
||||
static int ltr501_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ltr501_data *data = iio_priv(indio_dev);
|
||||
__le16 buf[2];
|
||||
int ret, i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
mutex_lock(&data->lock_als);
|
||||
ret = ltr501_read_als(data, buf);
|
||||
mutex_unlock(&data->lock_als);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
|
||||
buf[0] : buf[1]);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PROXIMITY:
|
||||
mutex_lock(&data->lock_ps);
|
||||
ret = ltr501_read_ps(data);
|
||||
mutex_unlock(&data->lock_ps);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret & LTR501_PS_DATA_MASK;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
if (data->als_contr & LTR501_CONTR_ALS_GAIN_MASK) {
|
||||
*val = 0;
|
||||
*val2 = 5000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
} else {
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
case IIO_PROXIMITY:
|
||||
i = (data->ps_contr & LTR501_CONTR_PS_GAIN_MASK) >>
|
||||
LTR501_CONTR_PS_GAIN_SHIFT;
|
||||
*val = ltr501_ps_gain[i][0];
|
||||
*val2 = ltr501_ps_gain[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ltr501_get_ps_gain_index(int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ltr501_ps_gain); i++)
|
||||
if (val == ltr501_ps_gain[i][0] && val2 == ltr501_ps_gain[i][1])
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ltr501_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct ltr501_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
if (val == 0 && val2 == 5000)
|
||||
data->als_contr |= LTR501_CONTR_ALS_GAIN_MASK;
|
||||
else if (val == 1 && val2 == 0)
|
||||
data->als_contr &= ~LTR501_CONTR_ALS_GAIN_MASK;
|
||||
else
|
||||
return -EINVAL;
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
LTR501_ALS_CONTR, data->als_contr);
|
||||
case IIO_PROXIMITY:
|
||||
i = ltr501_get_ps_gain_index(val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
|
||||
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
LTR501_PS_CONTR, data->ps_contr);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(in_proximity_scale_available, "1 0.25 0.125 0.0625");
|
||||
static IIO_CONST_ATTR(in_intensity_scale_available, "1 0.005");
|
||||
|
||||
static struct attribute *ltr501_attributes[] = {
|
||||
&iio_const_attr_in_proximity_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_in_intensity_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ltr501_attribute_group = {
|
||||
.attrs = ltr501_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ltr501_info = {
|
||||
.read_raw = ltr501_read_raw,
|
||||
.write_raw = ltr501_write_raw,
|
||||
.attrs = <r501_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ltr501_write_contr(struct i2c_client *client, u8 als_val, u8 ps_val)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(client, LTR501_ALS_CONTR, als_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, LTR501_PS_CONTR, ps_val);
|
||||
}
|
||||
|
||||
static irqreturn_t ltr501_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ltr501_data *data = iio_priv(indio_dev);
|
||||
u16 buf[8];
|
||||
__le16 als_buf[2];
|
||||
u8 mask = 0;
|
||||
int j = 0;
|
||||
int ret;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/* figure out which data needs to be ready */
|
||||
if (test_bit(0, indio_dev->active_scan_mask) ||
|
||||
test_bit(1, indio_dev->active_scan_mask))
|
||||
mask |= LTR501_STATUS_ALS_RDY;
|
||||
if (test_bit(2, indio_dev->active_scan_mask))
|
||||
mask |= LTR501_STATUS_PS_RDY;
|
||||
|
||||
ret = ltr501_drdy(data, mask);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
if (mask & LTR501_STATUS_ALS_RDY) {
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
LTR501_ALS_DATA1, sizeof(als_buf), (u8 *) als_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (test_bit(0, indio_dev->active_scan_mask))
|
||||
buf[j++] = le16_to_cpu(als_buf[1]);
|
||||
if (test_bit(1, indio_dev->active_scan_mask))
|
||||
buf[j++] = le16_to_cpu(als_buf[0]);
|
||||
}
|
||||
|
||||
if (mask & LTR501_STATUS_PS_RDY) {
|
||||
ret = i2c_smbus_read_word_data(data->client, LTR501_PS_DATA);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
buf[j++] = ret & LTR501_PS_DATA_MASK;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ltr501_init(struct ltr501_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, LTR501_ALS_CONTR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->als_contr = ret | LTR501_CONTR_ACTIVE;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, LTR501_PS_CONTR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->ps_contr = ret | LTR501_CONTR_ACTIVE;
|
||||
|
||||
return ltr501_write_contr(data->client, data->als_contr,
|
||||
data->ps_contr);
|
||||
}
|
||||
|
||||
static int ltr501_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ltr501_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock_als);
|
||||
mutex_init(&data->lock_ps);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, LTR501_PART_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((ret >> 4) != 0x8)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = <r501_info;
|
||||
indio_dev->channels = ltr501_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ltr501_channels);
|
||||
indio_dev->name = LTR501_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = ltr501_init(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ltr501_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltr501_powerdown(struct ltr501_data *data)
|
||||
{
|
||||
return ltr501_write_contr(data->client,
|
||||
data->als_contr & ~LTR501_CONTR_ACTIVE,
|
||||
data->ps_contr & ~LTR501_CONTR_ACTIVE);
|
||||
}
|
||||
|
||||
static int ltr501_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
ltr501_powerdown(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ltr501_suspend(struct device *dev)
|
||||
{
|
||||
struct ltr501_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
return ltr501_powerdown(data);
|
||||
}
|
||||
|
||||
static int ltr501_resume(struct device *dev)
|
||||
{
|
||||
struct ltr501_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
|
||||
return ltr501_write_contr(data->client, data->als_contr,
|
||||
data->ps_contr);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
|
||||
|
||||
static const struct i2c_device_id ltr501_id[] = {
|
||||
{ "ltr501", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltr501_id);
|
||||
|
||||
static struct i2c_driver ltr501_driver = {
|
||||
.driver = {
|
||||
.name = LTR501_DRV_NAME,
|
||||
.pm = <r501_pm_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ltr501_probe,
|
||||
.remove = ltr501_remove,
|
||||
.id_table = ltr501_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ltr501_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Lite-On LTR501 ambient light and proximity sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -179,7 +179,6 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct tcs3472_data *data = iio_priv(indio_dev);
|
||||
int len = 0;
|
||||
int i, j = 0;
|
||||
|
||||
int ret = tcs3472_req_data(data);
|
||||
|
@ -194,7 +193,6 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p)
|
|||
goto done;
|
||||
|
||||
data->buffer[j++] = ret;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
|
|
|
@ -513,6 +513,7 @@ static int ak8975_probe(struct i2c_client *client,
|
|||
indio_dev->channels = ak8975_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);
|
||||
indio_dev->info = &ak8975_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
|
|
|
@ -183,9 +183,17 @@ static int mag3110_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 1000;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
|
||||
*val = mag3110_samp_freq[i][0];
|
||||
|
@ -270,7 +278,8 @@ static const struct iio_chan_spec mag3110_channels[] = {
|
|||
MAG3110_CHANNEL(Z, 2),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 3,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
|
|
|
@ -5,6 +5,20 @@
|
|||
|
||||
menu "Pressure sensors"
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID PRESS"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Pressure driver
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hid-sensor-press.
|
||||
|
||||
config MPL3115
|
||||
tristate "Freescale MPL3115A2 pressure sensor driver"
|
||||
depends on I2C
|
||||
|
@ -26,7 +40,7 @@ config IIO_ST_PRESS
|
|||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics pressure
|
||||
sensors: LPS001WP, LPS331AP.
|
||||
sensors: LPS001WP, LPS25H, LPS331AP.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_MPL3115) += mpl3115.o
|
||||
obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
|
||||
st_pressure-y := st_pressure_core.o
|
||||
|
|
376
drivers/iio/pressure/hid-sensor-press.c
Normal file
376
drivers/iio/pressure/hid-sensor-press.c
Normal file
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2014, 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_PRESSURE 0
|
||||
|
||||
struct press_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info press_attr;
|
||||
u32 press_data;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec press_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_PRESSURE,
|
||||
}
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void press_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is u32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(u32) * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int press_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret;
|
||||
int ret_type;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->scan_index) {
|
||||
case CHANNEL_SCAN_INDEX_PRESSURE:
|
||||
report_id = press_state->press_attr.report_id;
|
||||
address =
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
|
||||
break;
|
||||
default:
|
||||
report_id = -1;
|
||||
break;
|
||||
}
|
||||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
press_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_PRESSURE, address,
|
||||
report_id);
|
||||
else {
|
||||
*val = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = press_state->press_attr.units;
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = hid_sensor_convert_exponent(
|
||||
press_state->press_attr.unit_expo);
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_read_samp_freq_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
ret_type = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_read_raw_hyst_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
ret_type = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret_type = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int press_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_write_samp_freq_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_write_raw_hyst_value(
|
||||
&press_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info press_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &press_read_raw,
|
||||
.write_raw = &press_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int press_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "press_proc_event [%d]\n",
|
||||
press_state->common_attributes.data_ready);
|
||||
if (press_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
&press_state->press_data,
|
||||
sizeof(press_state->press_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int press_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
size_t raw_len, char *raw_data,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE:
|
||||
press_state->press_data = *(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id*/
|
||||
static int press_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned usage_id,
|
||||
struct press_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE,
|
||||
&st->press_attr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
press_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESSURE,
|
||||
st->press_attr.size);
|
||||
|
||||
dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index,
|
||||
st->press_attr.report_id);
|
||||
|
||||
/* Set Sensitivity field ids, when there is no individual modifier */
|
||||
if (st->common_attributes.sensitivity.index < 0) {
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
|
||||
HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE,
|
||||
&st->common_attributes.sensitivity);
|
||||
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
|
||||
st->common_attributes.sensitivity.index,
|
||||
st->common_attributes.sensitivity.report_id);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int hid_press_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "press";
|
||||
struct iio_dev *indio_dev;
|
||||
struct press_state *press_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct press_state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
press_state = iio_priv(indio_dev);
|
||||
press_state->common_attributes.hsdev = hsdev;
|
||||
press_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_PRESSURE,
|
||||
&press_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(press_channels, sizeof(press_channels), GFP_KERNEL);
|
||||
if (!channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = press_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_PRESSURE, press_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(press_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &press_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
press_state->common_attributes.data_ready = false;
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&press_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
goto error_unreg_buffer_funcs;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
press_state->callbacks.send_event = press_proc_event;
|
||||
press_state->callbacks.capture_sample = press_capture_sample;
|
||||
press_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE,
|
||||
&press_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_iio_unreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_iio_unreg:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(&press_state->common_attributes);
|
||||
error_unreg_buffer_funcs:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_dev_mem:
|
||||
kfree(indio_dev->channels);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_press_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct press_state *press_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&press_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_press_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200031",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_press_ids);
|
||||
|
||||
static struct platform_driver hid_press_platform_driver = {
|
||||
.id_table = hid_press_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = hid_press_probe,
|
||||
.remove = hid_press_remove,
|
||||
};
|
||||
module_platform_driver(hid_press_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Pressure");
|
||||
MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -77,7 +77,7 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mpl3115_data *data = iio_priv(indio_dev);
|
||||
s32 tmp = 0;
|
||||
__be32 tmp = 0;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LPS001WP_PRESS_DEV_NAME "lps001wp"
|
||||
#define LPS25H_PRESS_DEV_NAME "lps25h"
|
||||
#define LPS331AP_PRESS_DEV_NAME "lps331ap"
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
/* FULLSCALE */
|
||||
#define ST_PRESS_FS_AVL_1260MB 1260
|
||||
|
||||
#define ST_PRESS_1_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_1_OUT_L_ADDR 0x2b
|
||||
|
||||
/* CUSTOM VALUES FOR LPS331AP SENSOR */
|
||||
#define ST_PRESS_LPS331AP_WAI_EXP 0xbb
|
||||
#define ST_PRESS_LPS331AP_ODR_ADDR 0x20
|
||||
|
@ -62,8 +65,6 @@
|
|||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_PRESS_LPS331AP_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS331AP_TEMP_OFFSET 42500
|
||||
#define ST_PRESS_LPS331AP_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_LPS331AP_OUT_L_ADDR 0x2b
|
||||
|
||||
/* CUSTOM VALUES FOR LPS001WP SENSOR */
|
||||
#define ST_PRESS_LPS001WP_WAI_EXP 0xba
|
||||
|
@ -80,11 +81,36 @@
|
|||
#define ST_PRESS_LPS001WP_OUT_L_ADDR 0x28
|
||||
#define ST_TEMP_LPS001WP_OUT_L_ADDR 0x2a
|
||||
|
||||
static const struct iio_chan_spec st_press_lps331ap_channels[] = {
|
||||
/* CUSTOM VALUES FOR LPS25H SENSOR */
|
||||
#define ST_PRESS_LPS25H_WAI_EXP 0xbd
|
||||
#define ST_PRESS_LPS25H_ODR_ADDR 0x20
|
||||
#define ST_PRESS_LPS25H_ODR_MASK 0x70
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL 0x02
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL 0x03
|
||||
#define ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL 0x04
|
||||
#define ST_PRESS_LPS25H_PW_ADDR 0x20
|
||||
#define ST_PRESS_LPS25H_PW_MASK 0x80
|
||||
#define ST_PRESS_LPS25H_FS_ADDR 0x00
|
||||
#define ST_PRESS_LPS25H_FS_MASK 0x00
|
||||
#define ST_PRESS_LPS25H_FS_AVL_1260_VAL 0x00
|
||||
#define ST_PRESS_LPS25H_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
|
||||
#define ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
|
||||
#define ST_PRESS_LPS25H_BDU_ADDR 0x20
|
||||
#define ST_PRESS_LPS25H_BDU_MASK 0x04
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_ADDR 0x23
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_PRESS_LPS25H_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS25H_TEMP_OFFSET 42500
|
||||
#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b
|
||||
|
||||
static const struct iio_chan_spec st_press_1_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_PRESS_LPS331AP_OUT_XL_ADDR,
|
||||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = ST_SENSORS_SCAN_X,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
|
@ -99,7 +125,7 @@ static const struct iio_chan_spec st_press_lps331ap_channels[] = {
|
|||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_TEMP_LPS331AP_OUT_L_ADDR,
|
||||
.address = ST_TEMP_1_OUT_L_ADDR,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
|
@ -156,8 +182,8 @@ static const struct st_sensors st_press_sensors[] = {
|
|||
.sensors_supported = {
|
||||
[0] = LPS331AP_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_lps331ap_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_lps331ap_channels),
|
||||
.ch = (struct iio_chan_spec *)st_press_1_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_1_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS331AP_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_ODR_MASK,
|
||||
|
@ -233,6 +259,53 @@ static const struct st_sensors st_press_sensors[] = {
|
|||
.multi_read_bit = ST_PRESS_LPS001WP_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_PRESS_LPS25H_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LPS25H_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_1_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_1_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS25H_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL, },
|
||||
{ 25, ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_LPS25H_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_PRESS_LPS25H_FS_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_PRESS_FS_AVL_1260MB,
|
||||
.value = ST_PRESS_LPS25H_FS_AVL_1260_VAL,
|
||||
.gain = ST_PRESS_LPS25H_FS_AVL_1260_GAIN,
|
||||
.gain2 = ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_LPS25H_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS25H_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_press_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -50,6 +50,7 @@ static int st_press_i2c_remove(struct i2c_client *client)
|
|||
|
||||
static const struct i2c_device_id st_press_id_table[] = {
|
||||
{ LPS001WP_PRESS_DEV_NAME },
|
||||
{ LPS25H_PRESS_DEV_NAME },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -49,6 +49,7 @@ static int st_press_spi_remove(struct spi_device *spi)
|
|||
|
||||
static const struct spi_device_id st_press_id_table[] = {
|
||||
{ LPS001WP_PRESS_DEV_NAME },
|
||||
{ LPS25H_PRESS_DEV_NAME },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
#include <linux/videodev2.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <media/adv7343.h>
|
||||
#include <media/v4l2-async.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-of.h>
|
||||
|
||||
#include "adv7343_regs.h"
|
||||
|
||||
|
@ -410,7 +410,7 @@ adv7343_get_pdata(struct i2c_client *client)
|
|||
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);
|
||||
np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -29,7 +30,6 @@
|
|||
#include <media/mt9p031.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"
|
||||
|
@ -943,7 +943,7 @@ mt9p031_get_pdata(struct i2c_client *client)
|
|||
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);
|
||||
np = of_graph_get_next_endpoint(client->dev.of_node, NULL);
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/media.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -1855,7 +1856,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
node_ep = v4l2_of_get_next_endpoint(node, NULL);
|
||||
node_ep = of_graph_get_next_endpoint(node, NULL);
|
||||
if (!node_ep) {
|
||||
dev_err(dev, "no endpoint defined at node %s\n",
|
||||
node->full_name);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/v4l2-mediabus.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <media/v4l2-async.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
@ -1068,7 +1069,7 @@ tvp514x_get_pdata(struct i2c_client *client)
|
|||
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);
|
||||
endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
|
||||
if (!endpoint)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/videodev2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/v4l2-dv-timings.h>
|
||||
#include <media/tvp7002.h>
|
||||
#include <media/v4l2-async.h>
|
||||
|
@ -957,7 +958,7 @@ tvp7002_get_pdata(struct i2c_client *client)
|
|||
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);
|
||||
endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
|
||||
if (!endpoint)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-of.h>
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
|
||||
#include "media-dev.h"
|
||||
|
@ -167,10 +167,10 @@ static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor,
|
|||
u32 tmp = 0;
|
||||
int ret;
|
||||
|
||||
np = v4l2_of_get_next_endpoint(np, NULL);
|
||||
np = of_graph_get_next_endpoint(np, NULL);
|
||||
if (!np)
|
||||
return -ENXIO;
|
||||
np = v4l2_of_get_remote_port(np);
|
||||
np = of_graph_get_remote_port(np);
|
||||
if (!np)
|
||||
return -ENXIO;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/types.h>
|
||||
|
@ -468,12 +469,12 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
|
|||
return 0;
|
||||
|
||||
v4l2_of_parse_endpoint(ep, &endpoint);
|
||||
if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS)
|
||||
if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS)
|
||||
return -EINVAL;
|
||||
|
||||
pd->mux_id = (endpoint.port - 1) & 0x1;
|
||||
pd->mux_id = (endpoint.base.port - 1) & 0x1;
|
||||
|
||||
rem = v4l2_of_get_remote_port_parent(ep);
|
||||
rem = of_graph_get_remote_port_parent(ep);
|
||||
of_node_put(ep);
|
||||
if (rem == NULL) {
|
||||
v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n",
|
||||
|
@ -493,13 +494,13 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fimc_input_is_parallel(endpoint.port)) {
|
||||
if (fimc_input_is_parallel(endpoint.base.port)) {
|
||||
if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
|
||||
pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
|
||||
else
|
||||
pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;
|
||||
pd->flags = endpoint.bus.parallel.flags;
|
||||
} else if (fimc_input_is_mipi_csi(endpoint.port)) {
|
||||
} else if (fimc_input_is_mipi_csi(endpoint.base.port)) {
|
||||
/*
|
||||
* MIPI CSI-2: only input mux selection and
|
||||
* the sensor's clock frequency is needed.
|
||||
|
@ -507,7 +508,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
|
|||
pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;
|
||||
} else {
|
||||
v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n",
|
||||
endpoint.port, rem->full_name);
|
||||
endpoint.base.port, rem->full_name);
|
||||
}
|
||||
/*
|
||||
* For FIMC-IS handled sensors, that are placed under i2c-isp device
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/memory.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_data/mipi-csis.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -762,7 +763,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
|
|||
&state->max_num_lanes))
|
||||
return -EINVAL;
|
||||
|
||||
node = v4l2_of_get_next_endpoint(node, NULL);
|
||||
node = of_graph_get_next_endpoint(node, NULL);
|
||||
if (!node) {
|
||||
dev_err(&pdev->dev, "No port node at %s\n",
|
||||
pdev->dev.of_node->full_name);
|
||||
|
@ -771,7 +772,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
|
|||
/* Get port node and validate MIPI-CSI channel id. */
|
||||
v4l2_of_parse_endpoint(node, &endpoint);
|
||||
|
||||
state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0;
|
||||
state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
|
||||
if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
|
||||
return -ENXIO;
|
||||
|
||||
|
|
|
@ -127,17 +127,9 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node,
|
|||
int v4l2_of_parse_endpoint(const struct device_node *node,
|
||||
struct v4l2_of_endpoint *endpoint)
|
||||
{
|
||||
struct device_node *port_node = of_get_parent(node);
|
||||
|
||||
memset(endpoint, 0, offsetof(struct v4l2_of_endpoint, head));
|
||||
|
||||
endpoint->local_node = node;
|
||||
/*
|
||||
* It doesn't matter whether the two calls below succeed.
|
||||
* If they don't then the default value 0 is used.
|
||||
*/
|
||||
of_property_read_u32(port_node, "reg", &endpoint->port);
|
||||
of_property_read_u32(node, "reg", &endpoint->id);
|
||||
of_graph_parse_endpoint(node, &endpoint->base);
|
||||
endpoint->bus_type = 0;
|
||||
memset(&endpoint->bus, 0, sizeof(endpoint->bus));
|
||||
|
||||
v4l2_of_parse_csi_bus(node, endpoint);
|
||||
/*
|
||||
|
@ -147,125 +139,6 @@ int v4l2_of_parse_endpoint(const struct device_node *node,
|
|||
if (endpoint->bus.mipi_csi2.flags == 0)
|
||||
v4l2_of_parse_parallel_bus(node, endpoint);
|
||||
|
||||
of_node_put(port_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(v4l2_of_parse_endpoint);
|
||||
|
||||
/**
|
||||
* v4l2_of_get_next_endpoint() - get next endpoint node
|
||||
* @parent: pointer to the parent device node
|
||||
* @prev: previous endpoint node, or NULL to get first
|
||||
*
|
||||
* Return: An 'endpoint' node pointer with refcount incremented. Refcount
|
||||
* of the passed @prev node is not decremented, the caller have to use
|
||||
* of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent,
|
||||
struct device_node *prev)
|
||||
{
|
||||
struct device_node *endpoint;
|
||||
struct device_node *port = NULL;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
if (!prev) {
|
||||
struct device_node *node;
|
||||
/*
|
||||
* It's the first call, we have to find a port subnode
|
||||
* within this node or within an optional 'ports' node.
|
||||
*/
|
||||
node = of_get_child_by_name(parent, "ports");
|
||||
if (node)
|
||||
parent = node;
|
||||
|
||||
port = of_get_child_by_name(parent, "port");
|
||||
|
||||
if (port) {
|
||||
/* Found a port, get an endpoint. */
|
||||
endpoint = of_get_next_child(port, NULL);
|
||||
of_node_put(port);
|
||||
} else {
|
||||
endpoint = NULL;
|
||||
}
|
||||
|
||||
if (!endpoint)
|
||||
pr_err("%s(): no endpoint nodes specified for %s\n",
|
||||
__func__, parent->full_name);
|
||||
of_node_put(node);
|
||||
} else {
|
||||
port = of_get_parent(prev);
|
||||
if (!port)
|
||||
/* Hm, has someone given us the root node ?... */
|
||||
return NULL;
|
||||
|
||||
/* Avoid dropping prev node refcount to 0. */
|
||||
of_node_get(prev);
|
||||
endpoint = of_get_next_child(port, prev);
|
||||
if (endpoint) {
|
||||
of_node_put(port);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/* No more endpoints under this port, try the next one. */
|
||||
do {
|
||||
port = of_get_next_child(parent, port);
|
||||
if (!port)
|
||||
return NULL;
|
||||
} while (of_node_cmp(port->name, "port"));
|
||||
|
||||
/* Pick up the first endpoint in this port. */
|
||||
endpoint = of_get_next_child(port, NULL);
|
||||
of_node_put(port);
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
EXPORT_SYMBOL(v4l2_of_get_next_endpoint);
|
||||
|
||||
/**
|
||||
* v4l2_of_get_remote_port_parent() - get remote port's parent node
|
||||
* @node: pointer to a local endpoint device_node
|
||||
*
|
||||
* Return: Remote device node associated with remote endpoint node linked
|
||||
* to @node. Use of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *v4l2_of_get_remote_port_parent(
|
||||
const struct device_node *node)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned int depth;
|
||||
|
||||
/* Get remote endpoint node. */
|
||||
np = of_parse_phandle(node, "remote-endpoint", 0);
|
||||
|
||||
/* Walk 3 levels up only if there is 'ports' node. */
|
||||
for (depth = 3; depth && np; depth--) {
|
||||
np = of_get_next_parent(np);
|
||||
if (depth == 2 && of_node_cmp(np->name, "ports"))
|
||||
break;
|
||||
}
|
||||
return np;
|
||||
}
|
||||
EXPORT_SYMBOL(v4l2_of_get_remote_port_parent);
|
||||
|
||||
/**
|
||||
* v4l2_of_get_remote_port() - get remote port node
|
||||
* @node: pointer to a local endpoint device_node
|
||||
*
|
||||
* Return: Remote port node associated with remote endpoint node linked
|
||||
* to @node. Use of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *v4l2_of_get_remote_port(const struct device_node *node)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
/* Get remote endpoint node. */
|
||||
np = of_parse_phandle(node, "remote-endpoint", 0);
|
||||
if (!np)
|
||||
return NULL;
|
||||
return of_get_next_parent(np);
|
||||
}
|
||||
EXPORT_SYMBOL(v4l2_of_get_remote_port);
|
||||
|
|
|
@ -526,4 +526,5 @@ source "drivers/misc/mei/Kconfig"
|
|||
source "drivers/misc/vmw_vmci/Kconfig"
|
||||
source "drivers/misc/mic/Kconfig"
|
||||
source "drivers/misc/genwqe/Kconfig"
|
||||
source "drivers/misc/echo/Kconfig"
|
||||
endmenu
|
||||
|
|
|
@ -54,3 +54,4 @@ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
|
|||
obj-$(CONFIG_SRAM) += sram.o
|
||||
obj-y += mic/
|
||||
obj-$(CONFIG_GENWQE) += genwqe/
|
||||
obj-$(CONFIG_ECHO) += echo/
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/cpu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
@ -2014,3 +2015,153 @@ struct device_node *of_find_next_cache_node(const struct device_node *np)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_graph_parse_endpoint() - parse common endpoint node properties
|
||||
* @node: pointer to endpoint device_node
|
||||
* @endpoint: pointer to the OF endpoint data structure
|
||||
*
|
||||
* The caller should hold a reference to @node.
|
||||
*/
|
||||
int of_graph_parse_endpoint(const struct device_node *node,
|
||||
struct of_endpoint *endpoint)
|
||||
{
|
||||
struct device_node *port_node = of_get_parent(node);
|
||||
|
||||
WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
|
||||
__func__, node->full_name);
|
||||
|
||||
memset(endpoint, 0, sizeof(*endpoint));
|
||||
|
||||
endpoint->local_node = node;
|
||||
/*
|
||||
* It doesn't matter whether the two calls below succeed.
|
||||
* If they don't then the default value 0 is used.
|
||||
*/
|
||||
of_property_read_u32(port_node, "reg", &endpoint->port);
|
||||
of_property_read_u32(node, "reg", &endpoint->id);
|
||||
|
||||
of_node_put(port_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(of_graph_parse_endpoint);
|
||||
|
||||
/**
|
||||
* of_graph_get_next_endpoint() - get next endpoint node
|
||||
* @parent: pointer to the parent device node
|
||||
* @prev: previous endpoint node, or NULL to get first
|
||||
*
|
||||
* Return: An 'endpoint' node pointer with refcount incremented. Refcount
|
||||
* of the passed @prev node is not decremented, the caller have to use
|
||||
* of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
|
||||
struct device_node *prev)
|
||||
{
|
||||
struct device_node *endpoint;
|
||||
struct device_node *port = NULL;
|
||||
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
if (!prev) {
|
||||
struct device_node *node;
|
||||
/*
|
||||
* It's the first call, we have to find a port subnode
|
||||
* within this node or within an optional 'ports' node.
|
||||
*/
|
||||
node = of_get_child_by_name(parent, "ports");
|
||||
if (node)
|
||||
parent = node;
|
||||
|
||||
port = of_get_child_by_name(parent, "port");
|
||||
|
||||
if (port) {
|
||||
/* Found a port, get an endpoint. */
|
||||
endpoint = of_get_next_child(port, NULL);
|
||||
of_node_put(port);
|
||||
} else {
|
||||
endpoint = NULL;
|
||||
}
|
||||
|
||||
if (!endpoint)
|
||||
pr_err("%s(): no endpoint nodes specified for %s\n",
|
||||
__func__, parent->full_name);
|
||||
of_node_put(node);
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
port = of_get_parent(prev);
|
||||
if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
|
||||
__func__, prev->full_name))
|
||||
return NULL;
|
||||
|
||||
/* Avoid dropping prev node refcount to 0. */
|
||||
of_node_get(prev);
|
||||
endpoint = of_get_next_child(port, prev);
|
||||
if (endpoint) {
|
||||
of_node_put(port);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/* No more endpoints under this port, try the next one. */
|
||||
do {
|
||||
port = of_get_next_child(parent, port);
|
||||
if (!port)
|
||||
return NULL;
|
||||
} while (of_node_cmp(port->name, "port"));
|
||||
|
||||
/* Pick up the first endpoint in this port. */
|
||||
endpoint = of_get_next_child(port, NULL);
|
||||
of_node_put(port);
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
EXPORT_SYMBOL(of_graph_get_next_endpoint);
|
||||
|
||||
/**
|
||||
* of_graph_get_remote_port_parent() - get remote port's parent node
|
||||
* @node: pointer to a local endpoint device_node
|
||||
*
|
||||
* Return: Remote device node associated with remote endpoint node linked
|
||||
* to @node. Use of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *of_graph_get_remote_port_parent(
|
||||
const struct device_node *node)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned int depth;
|
||||
|
||||
/* Get remote endpoint node. */
|
||||
np = of_parse_phandle(node, "remote-endpoint", 0);
|
||||
|
||||
/* Walk 3 levels up only if there is 'ports' node. */
|
||||
for (depth = 3; depth && np; depth--) {
|
||||
np = of_get_next_parent(np);
|
||||
if (depth == 2 && of_node_cmp(np->name, "ports"))
|
||||
break;
|
||||
}
|
||||
return np;
|
||||
}
|
||||
EXPORT_SYMBOL(of_graph_get_remote_port_parent);
|
||||
|
||||
/**
|
||||
* of_graph_get_remote_port() - get remote port node
|
||||
* @node: pointer to a local endpoint device_node
|
||||
*
|
||||
* Return: Remote port node associated with remote endpoint node linked
|
||||
* to @node. Use of_node_put() on it when done.
|
||||
*/
|
||||
struct device_node *of_graph_get_remote_port(const struct device_node *node)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
/* Get remote endpoint node. */
|
||||
np = of_parse_phandle(node, "remote-endpoint", 0);
|
||||
if (!np)
|
||||
return NULL;
|
||||
return of_get_next_parent(np);
|
||||
}
|
||||
EXPORT_SYMBOL(of_graph_get_remote_port);
|
||||
|
|
|
@ -34,8 +34,6 @@ source "drivers/staging/winbond/Kconfig"
|
|||
|
||||
source "drivers/staging/wlan-ng/Kconfig"
|
||||
|
||||
source "drivers/staging/echo/Kconfig"
|
||||
|
||||
source "drivers/staging/comedi/Kconfig"
|
||||
|
||||
source "drivers/staging/olpc_dcon/Kconfig"
|
||||
|
@ -82,8 +80,6 @@ source "drivers/staging/wlags49_h2/Kconfig"
|
|||
|
||||
source "drivers/staging/wlags49_h25/Kconfig"
|
||||
|
||||
source "drivers/staging/sm7xxfb/Kconfig"
|
||||
|
||||
source "drivers/staging/crystalhd/Kconfig"
|
||||
|
||||
source "drivers/staging/cxt1e1/Kconfig"
|
||||
|
@ -128,8 +124,6 @@ source "drivers/staging/imx-drm/Kconfig"
|
|||
|
||||
source "drivers/staging/dgrp/Kconfig"
|
||||
|
||||
source "drivers/staging/sb105x/Kconfig"
|
||||
|
||||
source "drivers/staging/fwserial/Kconfig"
|
||||
|
||||
source "drivers/staging/goldfish/Kconfig"
|
||||
|
@ -146,4 +140,10 @@ source "drivers/staging/dgnc/Kconfig"
|
|||
|
||||
source "drivers/staging/dgap/Kconfig"
|
||||
|
||||
source "drivers/staging/gs_fpgaboot/Kconfig"
|
||||
|
||||
source "drivers/staging/nokia_h4p/Kconfig"
|
||||
|
||||
source "drivers/staging/unisys/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -9,7 +9,6 @@ obj-$(CONFIG_SLICOSS) += slicoss/
|
|||
obj-$(CONFIG_USBIP_CORE) += usbip/
|
||||
obj-$(CONFIG_W35UND) += winbond/
|
||||
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
|
||||
obj-$(CONFIG_ECHO) += echo/
|
||||
obj-$(CONFIG_COMEDI) += comedi/
|
||||
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
|
||||
obj-$(CONFIG_PANEL) += panel/
|
||||
|
@ -35,7 +34,6 @@ obj-$(CONFIG_DX_SEP) += sep/
|
|||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
|
||||
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
|
||||
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
|
||||
obj-$(CONFIG_CRYSTALHD) += crystalhd/
|
||||
obj-$(CONFIG_CXT1E1) += cxt1e1/
|
||||
obj-$(CONFIG_FB_XGI) += xgifb/
|
||||
|
@ -57,7 +55,6 @@ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
|
|||
obj-$(CONFIG_CED1401) += ced1401/
|
||||
obj-$(CONFIG_DRM_IMX) += imx-drm/
|
||||
obj-$(CONFIG_DGRP) += dgrp/
|
||||
obj-$(CONFIG_SB105X) += sb105x/
|
||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_LUSTRE_FS) += lustre/
|
||||
|
@ -65,3 +62,6 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
|
|||
obj-$(CONFIG_DGNC) += dgnc/
|
||||
obj-$(CONFIG_DGAP) += dgap/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
||||
obj-$(CONFIG_BT_NOKIA_H4P) += nokia_h4p/
|
||||
obj-$(CONFIG_UNISYSSPAR) += unisys/
|
||||
|
|
|
@ -20,6 +20,19 @@ config ANDROID_BINDER_IPC
|
|||
Android process, using Binder to identify, invoke and pass arguments
|
||||
between said processes.
|
||||
|
||||
config ANDROID_BINDER_IPC_32BIT
|
||||
bool
|
||||
depends on !64BIT && ANDROID_BINDER_IPC
|
||||
default y
|
||||
---help---
|
||||
The Binder API has been changed to support both 32 and 64bit
|
||||
applications in a mixed environment.
|
||||
|
||||
Enable this to support an old 32-bit Android user-space (v4.4 and
|
||||
earlier).
|
||||
|
||||
Note that enabling this will break newer Android user-space.
|
||||
|
||||
config ASHMEM
|
||||
bool "Enable the Anonymous Shared Memory Subsystem"
|
||||
default n
|
||||
|
|
|
@ -16,50 +16,10 @@
|
|||
#ifndef _LINUX_ANDROID_ALARM_H
|
||||
#define _LINUX_ANDROID_ALARM_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
enum android_alarm_type {
|
||||
/* return code bit numbers or set alarm arg */
|
||||
ANDROID_ALARM_RTC_WAKEUP,
|
||||
ANDROID_ALARM_RTC,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME,
|
||||
ANDROID_ALARM_SYSTEMTIME,
|
||||
|
||||
ANDROID_ALARM_TYPE_COUNT,
|
||||
|
||||
/* return code bit numbers */
|
||||
/* ANDROID_ALARM_TIME_CHANGE = 16 */
|
||||
};
|
||||
|
||||
enum android_alarm_return_flags {
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP,
|
||||
ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK =
|
||||
1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_MASK =
|
||||
1U << ANDROID_ALARM_ELAPSED_REALTIME,
|
||||
ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,
|
||||
ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
|
||||
};
|
||||
|
||||
/* Disable alarm */
|
||||
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4))
|
||||
|
||||
/* Ack last alarm and wait for next */
|
||||
#define ANDROID_ALARM_WAIT _IO('a', 1)
|
||||
|
||||
#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size)
|
||||
/* Set alarm */
|
||||
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
|
||||
#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
|
||||
#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
|
||||
#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec)
|
||||
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
|
||||
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
|
||||
|
||||
#include "uapi/android_alarm.h"
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#define ANDROID_ALARM_SET_COMPAT(type) ALARM_IOW(2, type, \
|
||||
|
|
|
@ -16,35 +16,7 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#define ASHMEM_NAME_LEN 256
|
||||
|
||||
#define ASHMEM_NAME_DEF "dev/ashmem"
|
||||
|
||||
/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
|
||||
#define ASHMEM_NOT_PURGED 0
|
||||
#define ASHMEM_WAS_PURGED 1
|
||||
|
||||
/* Return values from ASHMEM_GET_PIN_STATUS: Is the mapping pinned? */
|
||||
#define ASHMEM_IS_UNPINNED 0
|
||||
#define ASHMEM_IS_PINNED 1
|
||||
|
||||
struct ashmem_pin {
|
||||
__u32 offset; /* offset into region, in bytes, page-aligned */
|
||||
__u32 len; /* length forward from offset, in bytes, page-aligned */
|
||||
};
|
||||
|
||||
#define __ASHMEMIOC 0x77
|
||||
|
||||
#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
|
||||
#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
|
||||
#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
|
||||
#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
|
||||
#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
|
||||
#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
|
||||
#define ASHMEM_PIN _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
|
||||
#define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin)
|
||||
#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9)
|
||||
#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
|
||||
#include "uapi/ashmem.h"
|
||||
|
||||
/* support of 32bit userspace on 64bit platforms */
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
|
|
@ -228,8 +228,8 @@ struct binder_node {
|
|||
int internal_strong_refs;
|
||||
int local_weak_refs;
|
||||
int local_strong_refs;
|
||||
void __user *ptr;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t ptr;
|
||||
binder_uintptr_t cookie;
|
||||
unsigned has_strong_ref:1;
|
||||
unsigned pending_strong_ref:1;
|
||||
unsigned has_weak_ref:1;
|
||||
|
@ -242,7 +242,7 @@ struct binder_node {
|
|||
|
||||
struct binder_ref_death {
|
||||
struct binder_work work;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
};
|
||||
|
||||
struct binder_ref {
|
||||
|
@ -515,14 +515,14 @@ static void binder_insert_allocated_buffer(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc,
|
||||
void __user *user_ptr)
|
||||
uintptr_t user_ptr)
|
||||
{
|
||||
struct rb_node *n = proc->allocated_buffers.rb_node;
|
||||
struct binder_buffer *buffer;
|
||||
struct binder_buffer *kern_ptr;
|
||||
|
||||
kern_ptr = user_ptr - proc->user_buffer_offset
|
||||
- offsetof(struct binder_buffer, data);
|
||||
kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset
|
||||
- offsetof(struct binder_buffer, data));
|
||||
|
||||
while (n) {
|
||||
buffer = rb_entry(n, struct binder_buffer, rb_node);
|
||||
|
@ -856,7 +856,7 @@ static void binder_free_buf(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
static struct binder_node *binder_get_node(struct binder_proc *proc,
|
||||
void __user *ptr)
|
||||
binder_uintptr_t ptr)
|
||||
{
|
||||
struct rb_node *n = proc->nodes.rb_node;
|
||||
struct binder_node *node;
|
||||
|
@ -875,8 +875,8 @@ static struct binder_node *binder_get_node(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
static struct binder_node *binder_new_node(struct binder_proc *proc,
|
||||
void __user *ptr,
|
||||
void __user *cookie)
|
||||
binder_uintptr_t ptr,
|
||||
binder_uintptr_t cookie)
|
||||
{
|
||||
struct rb_node **p = &proc->nodes.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
@ -908,9 +908,9 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
|
|||
INIT_LIST_HEAD(&node->work.entry);
|
||||
INIT_LIST_HEAD(&node->async_todo);
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
"%d:%d node %d u%p c%p created\n",
|
||||
"%d:%d node %d u%016llx c%016llx created\n",
|
||||
proc->pid, current->pid, node->debug_id,
|
||||
node->ptr, node->cookie);
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -1226,9 +1226,9 @@ static void binder_send_failed_reply(struct binder_transaction *t,
|
|||
|
||||
static void binder_transaction_buffer_release(struct binder_proc *proc,
|
||||
struct binder_buffer *buffer,
|
||||
size_t *failed_at)
|
||||
binder_size_t *failed_at)
|
||||
{
|
||||
size_t *offp, *off_end;
|
||||
binder_size_t *offp, *off_end;
|
||||
int debug_id = buffer->debug_id;
|
||||
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
|
@ -1239,7 +1239,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
if (buffer->target_node)
|
||||
binder_dec_node(buffer->target_node, 1, 0);
|
||||
|
||||
offp = (size_t *)(buffer->data + ALIGN(buffer->data_size, sizeof(void *)));
|
||||
offp = (binder_size_t *)(buffer->data +
|
||||
ALIGN(buffer->data_size, sizeof(void *)));
|
||||
if (failed_at)
|
||||
off_end = failed_at;
|
||||
else
|
||||
|
@ -1249,8 +1250,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
if (*offp > buffer->data_size - sizeof(*fp) ||
|
||||
buffer->data_size < sizeof(*fp) ||
|
||||
!IS_ALIGNED(*offp, sizeof(u32))) {
|
||||
pr_err("transaction release %d bad offset %zd, size %zd\n",
|
||||
debug_id, *offp, buffer->data_size);
|
||||
pr_err("transaction release %d bad offset %lld, size %zd\n",
|
||||
debug_id, (u64)*offp, buffer->data_size);
|
||||
continue;
|
||||
}
|
||||
fp = (struct flat_binder_object *)(buffer->data + *offp);
|
||||
|
@ -1259,13 +1260,13 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
case BINDER_TYPE_WEAK_BINDER: {
|
||||
struct binder_node *node = binder_get_node(proc, fp->binder);
|
||||
if (node == NULL) {
|
||||
pr_err("transaction release %d bad node %p\n",
|
||||
debug_id, fp->binder);
|
||||
pr_err("transaction release %d bad node %016llx\n",
|
||||
debug_id, (u64)fp->binder);
|
||||
break;
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" node %d u%p\n",
|
||||
node->debug_id, node->ptr);
|
||||
" node %d u%016llx\n",
|
||||
node->debug_id, (u64)node->ptr);
|
||||
binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
|
||||
} break;
|
||||
case BINDER_TYPE_HANDLE:
|
||||
|
@ -1303,7 +1304,7 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
{
|
||||
struct binder_transaction *t;
|
||||
struct binder_work *tcomplete;
|
||||
size_t *offp, *off_end;
|
||||
binder_size_t *offp, *off_end;
|
||||
struct binder_proc *target_proc;
|
||||
struct binder_thread *target_thread = NULL;
|
||||
struct binder_node *target_node = NULL;
|
||||
|
@ -1432,18 +1433,20 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
if (reply)
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"%d:%d BC_REPLY %d -> %d:%d, data %p-%p size %zd-%zd\n",
|
||||
"%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
|
||||
proc->pid, thread->pid, t->debug_id,
|
||||
target_proc->pid, target_thread->pid,
|
||||
tr->data.ptr.buffer, tr->data.ptr.offsets,
|
||||
tr->data_size, tr->offsets_size);
|
||||
(u64)tr->data.ptr.buffer,
|
||||
(u64)tr->data.ptr.offsets,
|
||||
(u64)tr->data_size, (u64)tr->offsets_size);
|
||||
else
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"%d:%d BC_TRANSACTION %d -> %d - node %d, data %p-%p size %zd-%zd\n",
|
||||
"%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
|
||||
proc->pid, thread->pid, t->debug_id,
|
||||
target_proc->pid, target_node->debug_id,
|
||||
tr->data.ptr.buffer, tr->data.ptr.offsets,
|
||||
tr->data_size, tr->offsets_size);
|
||||
(u64)tr->data.ptr.buffer,
|
||||
(u64)tr->data.ptr.offsets,
|
||||
(u64)tr->data_size, (u64)tr->offsets_size);
|
||||
|
||||
if (!reply && !(tr->flags & TF_ONE_WAY))
|
||||
t->from = thread;
|
||||
|
@ -1472,23 +1475,26 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
if (target_node)
|
||||
binder_inc_node(target_node, 1, 0, NULL);
|
||||
|
||||
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
|
||||
offp = (binder_size_t *)(t->buffer->data +
|
||||
ALIGN(tr->data_size, sizeof(void *)));
|
||||
|
||||
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
|
||||
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
|
||||
tr->data.ptr.buffer, tr->data_size)) {
|
||||
binder_user_error("%d:%d got transaction with invalid data ptr\n",
|
||||
proc->pid, thread->pid);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_copy_data_failed;
|
||||
}
|
||||
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
|
||||
if (copy_from_user(offp, (const void __user *)(uintptr_t)
|
||||
tr->data.ptr.offsets, tr->offsets_size)) {
|
||||
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
|
||||
proc->pid, thread->pid);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_copy_data_failed;
|
||||
}
|
||||
if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offsets size, %zd\n",
|
||||
proc->pid, thread->pid, tr->offsets_size);
|
||||
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n",
|
||||
proc->pid, thread->pid, (u64)tr->offsets_size);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_bad_offset;
|
||||
}
|
||||
|
@ -1498,8 +1504,8 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
if (*offp > t->buffer->data_size - sizeof(*fp) ||
|
||||
t->buffer->data_size < sizeof(*fp) ||
|
||||
!IS_ALIGNED(*offp, sizeof(u32))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offset, %zd\n",
|
||||
proc->pid, thread->pid, *offp);
|
||||
binder_user_error("%d:%d got transaction with invalid offset, %lld\n",
|
||||
proc->pid, thread->pid, (u64)*offp);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_bad_offset;
|
||||
}
|
||||
|
@ -1519,10 +1525,10 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
|
||||
}
|
||||
if (fp->cookie != node->cookie) {
|
||||
binder_user_error("%d:%d sending u%p node %d, cookie mismatch %p != %p\n",
|
||||
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
fp->binder, node->debug_id,
|
||||
fp->cookie, node->cookie);
|
||||
(u64)fp->binder, node->debug_id,
|
||||
(u64)fp->cookie, (u64)node->cookie);
|
||||
goto err_binder_get_ref_for_node_failed;
|
||||
}
|
||||
ref = binder_get_ref_for_node(target_proc, node);
|
||||
|
@ -1540,9 +1546,9 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
trace_binder_transaction_node_to_ref(t, node, ref);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" node %d u%p -> ref %d desc %d\n",
|
||||
node->debug_id, node->ptr, ref->debug_id,
|
||||
ref->desc);
|
||||
" node %d u%016llx -> ref %d desc %d\n",
|
||||
node->debug_id, (u64)node->ptr,
|
||||
ref->debug_id, ref->desc);
|
||||
} break;
|
||||
case BINDER_TYPE_HANDLE:
|
||||
case BINDER_TYPE_WEAK_HANDLE: {
|
||||
|
@ -1564,9 +1570,9 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
|
||||
trace_binder_transaction_ref_to_node(t, ref);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" ref %d desc %d -> node %d u%p\n",
|
||||
" ref %d desc %d -> node %d u%016llx\n",
|
||||
ref->debug_id, ref->desc, ref->node->debug_id,
|
||||
ref->node->ptr);
|
||||
(u64)ref->node->ptr);
|
||||
} else {
|
||||
struct binder_ref *new_ref;
|
||||
new_ref = binder_get_ref_for_node(target_proc, ref->node);
|
||||
|
@ -1682,9 +1688,9 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
err_invalid_target_handle:
|
||||
err_no_context_mgr_node:
|
||||
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
|
||||
"%d:%d transaction failed %d, size %zd-%zd\n",
|
||||
"%d:%d transaction failed %d, size %lld-%lld\n",
|
||||
proc->pid, thread->pid, return_error,
|
||||
tr->data_size, tr->offsets_size);
|
||||
(u64)tr->data_size, (u64)tr->offsets_size);
|
||||
|
||||
{
|
||||
struct binder_transaction_log_entry *fe;
|
||||
|
@ -1702,9 +1708,11 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
static int binder_thread_write(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
void __user *buffer, size_t size, size_t *consumed)
|
||||
binder_uintptr_t binder_buffer, size_t size,
|
||||
binder_size_t *consumed)
|
||||
{
|
||||
uint32_t cmd;
|
||||
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
|
||||
void __user *ptr = buffer + *consumed;
|
||||
void __user *end = buffer + size;
|
||||
|
||||
|
@ -1773,33 +1781,33 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
}
|
||||
case BC_INCREFS_DONE:
|
||||
case BC_ACQUIRE_DONE: {
|
||||
void __user *node_ptr;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t node_ptr;
|
||||
binder_uintptr_t cookie;
|
||||
struct binder_node *node;
|
||||
|
||||
if (get_user(node_ptr, (void * __user *)ptr))
|
||||
if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
if (get_user(cookie, (void * __user *)ptr))
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
node = binder_get_node(proc, node_ptr);
|
||||
if (node == NULL) {
|
||||
binder_user_error("%d:%d %s u%p no match\n",
|
||||
binder_user_error("%d:%d %s u%016llx no match\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BC_INCREFS_DONE ?
|
||||
"BC_INCREFS_DONE" :
|
||||
"BC_ACQUIRE_DONE",
|
||||
node_ptr);
|
||||
(u64)node_ptr);
|
||||
break;
|
||||
}
|
||||
if (cookie != node->cookie) {
|
||||
binder_user_error("%d:%d %s u%p node %d cookie mismatch %p != %p\n",
|
||||
binder_user_error("%d:%d %s u%016llx node %d cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BC_INCREFS_DONE ?
|
||||
"BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
|
||||
node_ptr, node->debug_id,
|
||||
cookie, node->cookie);
|
||||
(u64)node_ptr, node->debug_id,
|
||||
(u64)cookie, (u64)node->cookie);
|
||||
break;
|
||||
}
|
||||
if (cmd == BC_ACQUIRE_DONE) {
|
||||
|
@ -1835,27 +1843,28 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
return -EINVAL;
|
||||
|
||||
case BC_FREE_BUFFER: {
|
||||
void __user *data_ptr;
|
||||
binder_uintptr_t data_ptr;
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
if (get_user(data_ptr, (void * __user *)ptr))
|
||||
if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
|
||||
buffer = binder_buffer_lookup(proc, data_ptr);
|
||||
if (buffer == NULL) {
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%p no match\n",
|
||||
proc->pid, thread->pid, data_ptr);
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
|
||||
proc->pid, thread->pid, (u64)data_ptr);
|
||||
break;
|
||||
}
|
||||
if (!buffer->allow_user_free) {
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%p matched unreturned buffer\n",
|
||||
proc->pid, thread->pid, data_ptr);
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx matched unreturned buffer\n",
|
||||
proc->pid, thread->pid, (u64)data_ptr);
|
||||
break;
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_FREE_BUFFER,
|
||||
"%d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n",
|
||||
proc->pid, thread->pid, data_ptr, buffer->debug_id,
|
||||
"%d:%d BC_FREE_BUFFER u%016llx found buffer %d for %s transaction\n",
|
||||
proc->pid, thread->pid, (u64)data_ptr,
|
||||
buffer->debug_id,
|
||||
buffer->transaction ? "active" : "finished");
|
||||
|
||||
if (buffer->transaction) {
|
||||
|
@ -1925,16 +1934,16 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
case BC_REQUEST_DEATH_NOTIFICATION:
|
||||
case BC_CLEAR_DEATH_NOTIFICATION: {
|
||||
uint32_t target;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
struct binder_ref *ref;
|
||||
struct binder_ref_death *death;
|
||||
|
||||
if (get_user(target, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (get_user(cookie, (void __user * __user *)ptr))
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
ref = binder_get_ref(proc, target);
|
||||
if (ref == NULL) {
|
||||
binder_user_error("%d:%d %s invalid ref %d\n",
|
||||
|
@ -1947,12 +1956,12 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
|
||||
"%d:%d %s %p ref %d desc %d s %d w %d for node %d\n",
|
||||
"%d:%d %s %016llx ref %d desc %d s %d w %d for node %d\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BC_REQUEST_DEATH_NOTIFICATION ?
|
||||
"BC_REQUEST_DEATH_NOTIFICATION" :
|
||||
"BC_CLEAR_DEATH_NOTIFICATION",
|
||||
cookie, ref->debug_id, ref->desc,
|
||||
(u64)cookie, ref->debug_id, ref->desc,
|
||||
ref->strong, ref->weak, ref->node->debug_id);
|
||||
|
||||
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
|
||||
|
@ -1990,9 +1999,10 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
}
|
||||
death = ref->death;
|
||||
if (death->cookie != cookie) {
|
||||
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch %p != %p\n",
|
||||
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
death->cookie, cookie);
|
||||
(u64)death->cookie,
|
||||
(u64)cookie);
|
||||
break;
|
||||
}
|
||||
ref->death = NULL;
|
||||
|
@ -2012,9 +2022,9 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
} break;
|
||||
case BC_DEAD_BINDER_DONE: {
|
||||
struct binder_work *w;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
struct binder_ref_death *death = NULL;
|
||||
if (get_user(cookie, (void __user * __user *)ptr))
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
|
||||
ptr += sizeof(void *);
|
||||
|
@ -2026,11 +2036,12 @@ static int binder_thread_write(struct binder_proc *proc,
|
|||
}
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||
"%d:%d BC_DEAD_BINDER_DONE %p found %p\n",
|
||||
proc->pid, thread->pid, cookie, death);
|
||||
"%d:%d BC_DEAD_BINDER_DONE %016llx found %p\n",
|
||||
proc->pid, thread->pid, (u64)cookie,
|
||||
death);
|
||||
if (death == NULL) {
|
||||
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %p not found\n",
|
||||
proc->pid, thread->pid, cookie);
|
||||
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n",
|
||||
proc->pid, thread->pid, (u64)cookie);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2082,9 +2093,10 @@ static int binder_has_thread_work(struct binder_thread *thread)
|
|||
|
||||
static int binder_thread_read(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
void __user *buffer, size_t size,
|
||||
size_t *consumed, int non_block)
|
||||
binder_uintptr_t binder_buffer, size_t size,
|
||||
binder_size_t *consumed, int non_block)
|
||||
{
|
||||
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
|
||||
void __user *ptr = buffer + *consumed;
|
||||
void __user *end = buffer + size;
|
||||
|
||||
|
@ -2229,32 +2241,40 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(node->ptr, (void * __user *)ptr))
|
||||
if (put_user(node->ptr,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
if (put_user(node->cookie, (void * __user *)ptr))
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
if (put_user(node->cookie,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_USER_REFS,
|
||||
"%d:%d %s %d u%p c%p\n",
|
||||
proc->pid, thread->pid, cmd_name, node->debug_id, node->ptr, node->cookie);
|
||||
"%d:%d %s %d u%016llx c%016llx\n",
|
||||
proc->pid, thread->pid, cmd_name,
|
||||
node->debug_id,
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
} else {
|
||||
list_del_init(&w->entry);
|
||||
if (!weak && !strong) {
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
"%d:%d node %d u%p c%p deleted\n",
|
||||
proc->pid, thread->pid, node->debug_id,
|
||||
node->ptr, node->cookie);
|
||||
"%d:%d node %d u%016llx c%016llx deleted\n",
|
||||
proc->pid, thread->pid,
|
||||
node->debug_id,
|
||||
(u64)node->ptr,
|
||||
(u64)node->cookie);
|
||||
rb_erase(&node->rb_node, &proc->nodes);
|
||||
kfree(node);
|
||||
binder_stats_deleted(BINDER_STAT_NODE);
|
||||
} else {
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
"%d:%d node %d u%p c%p state unchanged\n",
|
||||
proc->pid, thread->pid, node->debug_id, node->ptr,
|
||||
node->cookie);
|
||||
"%d:%d node %d u%016llx c%016llx state unchanged\n",
|
||||
proc->pid, thread->pid,
|
||||
node->debug_id,
|
||||
(u64)node->ptr,
|
||||
(u64)node->cookie);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
@ -2272,17 +2292,18 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(death->cookie, (void * __user *)ptr))
|
||||
if (put_user(death->cookie,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
|
||||
"%d:%d %s %p\n",
|
||||
"%d:%d %s %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BR_DEAD_BINDER ?
|
||||
"BR_DEAD_BINDER" :
|
||||
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
|
||||
death->cookie);
|
||||
(u64)death->cookie);
|
||||
|
||||
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
|
||||
list_del(&w->entry);
|
||||
|
@ -2312,8 +2333,8 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
binder_set_nice(target_node->min_priority);
|
||||
cmd = BR_TRANSACTION;
|
||||
} else {
|
||||
tr.target.ptr = NULL;
|
||||
tr.cookie = NULL;
|
||||
tr.target.ptr = 0;
|
||||
tr.cookie = 0;
|
||||
cmd = BR_REPLY;
|
||||
}
|
||||
tr.code = t->code;
|
||||
|
@ -2330,8 +2351,9 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
|
||||
tr.data_size = t->buffer->data_size;
|
||||
tr.offsets_size = t->buffer->offsets_size;
|
||||
tr.data.ptr.buffer = (void *)t->buffer->data +
|
||||
proc->user_buffer_offset;
|
||||
tr.data.ptr.buffer = (binder_uintptr_t)(
|
||||
(uintptr_t)t->buffer->data +
|
||||
proc->user_buffer_offset);
|
||||
tr.data.ptr.offsets = tr.data.ptr.buffer +
|
||||
ALIGN(t->buffer->data_size,
|
||||
sizeof(void *));
|
||||
|
@ -2346,14 +2368,14 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
trace_binder_transaction_received(t);
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %p-%p\n",
|
||||
"%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
|
||||
"BR_REPLY",
|
||||
t->debug_id, t->from ? t->from->proc->pid : 0,
|
||||
t->from ? t->from->pid : 0, cmd,
|
||||
t->buffer->data_size, t->buffer->offsets_size,
|
||||
tr.data.ptr.buffer, tr.data.ptr.offsets);
|
||||
(u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
|
||||
|
||||
list_del(&t->work.entry);
|
||||
t->buffer->allow_user_free = 1;
|
||||
|
@ -2423,8 +2445,8 @@ static void binder_release_work(struct list_head *list)
|
|||
|
||||
death = container_of(w, struct binder_ref_death, work);
|
||||
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
|
||||
"undelivered death notification, %p\n",
|
||||
death->cookie);
|
||||
"undelivered death notification, %016llx\n",
|
||||
(u64)death->cookie);
|
||||
kfree(death);
|
||||
binder_stats_deleted(BINDER_STAT_DEATH);
|
||||
} break;
|
||||
|
@ -2580,12 +2602,16 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
goto err;
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_READ_WRITE,
|
||||
"%d:%d write %zd at %016lx, read %zd at %016lx\n",
|
||||
proc->pid, thread->pid, bwr.write_size,
|
||||
bwr.write_buffer, bwr.read_size, bwr.read_buffer);
|
||||
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
(u64)bwr.write_size, (u64)bwr.write_buffer,
|
||||
(u64)bwr.read_size, (u64)bwr.read_buffer);
|
||||
|
||||
if (bwr.write_size > 0) {
|
||||
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
|
||||
ret = binder_thread_write(proc, thread,
|
||||
bwr.write_buffer,
|
||||
bwr.write_size,
|
||||
&bwr.write_consumed);
|
||||
trace_binder_write_done(ret);
|
||||
if (ret < 0) {
|
||||
bwr.read_consumed = 0;
|
||||
|
@ -2595,7 +2621,10 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
}
|
||||
if (bwr.read_size > 0) {
|
||||
ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
|
||||
ret = binder_thread_read(proc, thread, bwr.read_buffer,
|
||||
bwr.read_size,
|
||||
&bwr.read_consumed,
|
||||
filp->f_flags & O_NONBLOCK);
|
||||
trace_binder_read_done(ret);
|
||||
if (!list_empty(&proc->todo))
|
||||
wake_up_interruptible(&proc->wait);
|
||||
|
@ -2606,9 +2635,10 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_READ_WRITE,
|
||||
"%d:%d wrote %zd of %zd, read return %zd of %zd\n",
|
||||
proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
|
||||
bwr.read_consumed, bwr.read_size);
|
||||
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
|
||||
proc->pid, thread->pid,
|
||||
(u64)bwr.write_consumed, (u64)bwr.write_size,
|
||||
(u64)bwr.read_consumed, (u64)bwr.read_size);
|
||||
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
|
||||
ret = -EFAULT;
|
||||
goto err;
|
||||
|
@ -2637,7 +2667,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
} else
|
||||
binder_context_mgr_uid = current->cred->euid;
|
||||
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
|
||||
binder_context_mgr_node = binder_new_node(proc, 0, 0);
|
||||
if (binder_context_mgr_node == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
|
@ -3132,8 +3162,9 @@ static void print_binder_work(struct seq_file *m, const char *prefix,
|
|||
break;
|
||||
case BINDER_WORK_NODE:
|
||||
node = container_of(w, struct binder_node, work);
|
||||
seq_printf(m, "%snode work %d: u%p c%p\n",
|
||||
prefix, node->debug_id, node->ptr, node->cookie);
|
||||
seq_printf(m, "%snode work %d: u%016llx c%016llx\n",
|
||||
prefix, node->debug_id,
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
break;
|
||||
case BINDER_WORK_DEAD_BINDER:
|
||||
seq_printf(m, "%shas dead binder\n", prefix);
|
||||
|
@ -3193,8 +3224,8 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
|
|||
hlist_for_each_entry(ref, &node->refs, node_entry)
|
||||
count++;
|
||||
|
||||
seq_printf(m, " node %d: u%p c%p hs %d hw %d ls %d lw %d is %d iw %d",
|
||||
node->debug_id, node->ptr, node->cookie,
|
||||
seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d",
|
||||
node->debug_id, (u64)node->ptr, (u64)node->cookie,
|
||||
node->has_strong_ref, node->has_weak_ref,
|
||||
node->local_strong_refs, node->local_weak_refs,
|
||||
node->internal_strong_refs, count);
|
||||
|
@ -3496,6 +3527,7 @@ static const struct file_operations binder_fops = {
|
|||
.owner = THIS_MODULE,
|
||||
.poll = binder_poll,
|
||||
.unlocked_ioctl = binder_ioctl,
|
||||
.compat_ioctl = binder_ioctl,
|
||||
.mmap = binder_mmap,
|
||||
.open = binder_open,
|
||||
.flush = binder_flush,
|
||||
|
|
|
@ -20,311 +20,11 @@
|
|||
#ifndef _LINUX_BINDER_H
|
||||
#define _LINUX_BINDER_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
|
||||
#define BINDER_IPC_32BIT 1
|
||||
#endif
|
||||
|
||||
#define B_PACK_CHARS(c1, c2, c3, c4) \
|
||||
((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
|
||||
#define B_TYPE_LARGE 0x85
|
||||
|
||||
enum {
|
||||
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
|
||||
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
|
||||
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
|
||||
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
|
||||
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
|
||||
};
|
||||
|
||||
enum {
|
||||
FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
|
||||
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the flattened representation of a Binder object for transfer
|
||||
* between processes. The 'offsets' supplied as part of a binder transaction
|
||||
* contains offsets into the data where these structures occur. The Binder
|
||||
* driver takes care of re-writing the structure type and data as it moves
|
||||
* between processes.
|
||||
*/
|
||||
struct flat_binder_object {
|
||||
/* 8 bytes for large_flat_header. */
|
||||
__u32 type;
|
||||
__u32 flags;
|
||||
|
||||
/* 8 bytes of data. */
|
||||
union {
|
||||
void __user *binder; /* local object */
|
||||
__u32 handle; /* remote object */
|
||||
};
|
||||
|
||||
/* extra data associated with local object */
|
||||
void __user *cookie;
|
||||
};
|
||||
|
||||
/*
|
||||
* On 64-bit platforms where user code may run in 32-bits the driver must
|
||||
* translate the buffer (and local binder) addresses appropriately.
|
||||
*/
|
||||
|
||||
struct binder_write_read {
|
||||
size_t write_size; /* bytes to write */
|
||||
size_t write_consumed; /* bytes consumed by driver */
|
||||
unsigned long write_buffer;
|
||||
size_t read_size; /* bytes to read */
|
||||
size_t read_consumed; /* bytes consumed by driver */
|
||||
unsigned long read_buffer;
|
||||
};
|
||||
|
||||
/* Use with BINDER_VERSION, driver fills in fields. */
|
||||
struct binder_version {
|
||||
/* driver protocol version -- increment with incompatible change */
|
||||
__s32 protocol_version;
|
||||
};
|
||||
|
||||
/* This is the current protocol version. */
|
||||
#define BINDER_CURRENT_PROTOCOL_VERSION 7
|
||||
|
||||
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
|
||||
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
|
||||
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
|
||||
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32)
|
||||
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
|
||||
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
|
||||
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
|
||||
|
||||
/*
|
||||
* NOTE: Two special error codes you should check for when calling
|
||||
* in to the driver are:
|
||||
*
|
||||
* EINTR -- The operation has been interupted. This should be
|
||||
* handled by retrying the ioctl() until a different error code
|
||||
* is returned.
|
||||
*
|
||||
* ECONNREFUSED -- The driver is no longer accepting operations
|
||||
* from your process. That is, the process is being destroyed.
|
||||
* You should handle this by exiting from your process. Note
|
||||
* that once this error code is returned, all further calls to
|
||||
* the driver from any thread will return this same code.
|
||||
*/
|
||||
|
||||
enum transaction_flags {
|
||||
TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */
|
||||
TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
|
||||
TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
|
||||
TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
|
||||
};
|
||||
|
||||
struct binder_transaction_data {
|
||||
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
|
||||
* identifying the target and contents of the transaction.
|
||||
*/
|
||||
union {
|
||||
__u32 handle; /* target descriptor of command transaction */
|
||||
void *ptr; /* target descriptor of return transaction */
|
||||
} target;
|
||||
void *cookie; /* target object cookie */
|
||||
__u32 code; /* transaction command */
|
||||
|
||||
/* General information about the transaction. */
|
||||
__u32 flags;
|
||||
pid_t sender_pid;
|
||||
uid_t sender_euid;
|
||||
size_t data_size; /* number of bytes of data */
|
||||
size_t offsets_size; /* number of bytes of offsets */
|
||||
|
||||
/* If this transaction is inline, the data immediately
|
||||
* follows here; otherwise, it ends with a pointer to
|
||||
* the data buffer.
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
/* transaction data */
|
||||
const void __user *buffer;
|
||||
/* offsets from buffer to flat_binder_object structs */
|
||||
const void __user *offsets;
|
||||
} ptr;
|
||||
__u8 buf[8];
|
||||
} data;
|
||||
};
|
||||
|
||||
struct binder_ptr_cookie {
|
||||
void *ptr;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
struct binder_pri_desc {
|
||||
__s32 priority;
|
||||
__u32 desc;
|
||||
};
|
||||
|
||||
struct binder_pri_ptr_cookie {
|
||||
__s32 priority;
|
||||
void *ptr;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
enum binder_driver_return_protocol {
|
||||
BR_ERROR = _IOR('r', 0, __s32),
|
||||
/*
|
||||
* int: error code
|
||||
*/
|
||||
|
||||
BR_OK = _IO('r', 1),
|
||||
/* No parameters! */
|
||||
|
||||
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
|
||||
BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
|
||||
/*
|
||||
* binder_transaction_data: the received command.
|
||||
*/
|
||||
|
||||
BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
|
||||
/*
|
||||
* not currently supported
|
||||
* int: 0 if the last bcATTEMPT_ACQUIRE was not successful.
|
||||
* Else the remote object has acquired a primary reference.
|
||||
*/
|
||||
|
||||
BR_DEAD_REPLY = _IO('r', 5),
|
||||
/*
|
||||
* The target of the last transaction (either a bcTRANSACTION or
|
||||
* a bcATTEMPT_ACQUIRE) is no longer with us. No parameters.
|
||||
*/
|
||||
|
||||
BR_TRANSACTION_COMPLETE = _IO('r', 6),
|
||||
/*
|
||||
* No parameters... always refers to the last transaction requested
|
||||
* (including replies). Note that this will be sent even for
|
||||
* asynchronous transactions.
|
||||
*/
|
||||
|
||||
BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
|
||||
BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
|
||||
BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
|
||||
BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie),
|
||||
/*
|
||||
* void *: ptr to binder
|
||||
* void *: cookie for binder
|
||||
*/
|
||||
|
||||
BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
|
||||
/*
|
||||
* not currently supported
|
||||
* int: priority
|
||||
* void *: ptr to binder
|
||||
* void *: cookie for binder
|
||||
*/
|
||||
|
||||
BR_NOOP = _IO('r', 12),
|
||||
/*
|
||||
* No parameters. Do nothing and examine the next command. It exists
|
||||
* primarily so that we can replace it with a BR_SPAWN_LOOPER command.
|
||||
*/
|
||||
|
||||
BR_SPAWN_LOOPER = _IO('r', 13),
|
||||
/*
|
||||
* No parameters. The driver has determined that a process has no
|
||||
* threads waiting to service incoming transactions. When a process
|
||||
* receives this command, it must spawn a new service thread and
|
||||
* register it via bcENTER_LOOPER.
|
||||
*/
|
||||
|
||||
BR_FINISHED = _IO('r', 14),
|
||||
/*
|
||||
* not currently supported
|
||||
* stop threadpool thread
|
||||
*/
|
||||
|
||||
BR_DEAD_BINDER = _IOR('r', 15, void *),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BR_FAILED_REPLY = _IO('r', 17),
|
||||
/*
|
||||
* The the last transaction (either a bcTRANSACTION or
|
||||
* a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters.
|
||||
*/
|
||||
};
|
||||
|
||||
enum binder_driver_command_protocol {
|
||||
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
|
||||
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
|
||||
/*
|
||||
* binder_transaction_data: the sent command.
|
||||
*/
|
||||
|
||||
BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),
|
||||
/*
|
||||
* not currently supported
|
||||
* int: 0 if the last BR_ATTEMPT_ACQUIRE was not successful.
|
||||
* Else you have acquired a primary reference on the object.
|
||||
*/
|
||||
|
||||
BC_FREE_BUFFER = _IOW('c', 3, void *),
|
||||
/*
|
||||
* void *: ptr to transaction data received on a read
|
||||
*/
|
||||
|
||||
BC_INCREFS = _IOW('c', 4, __u32),
|
||||
BC_ACQUIRE = _IOW('c', 5, __u32),
|
||||
BC_RELEASE = _IOW('c', 6, __u32),
|
||||
BC_DECREFS = _IOW('c', 7, __u32),
|
||||
/*
|
||||
* int: descriptor
|
||||
*/
|
||||
|
||||
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
|
||||
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
|
||||
/*
|
||||
* void *: ptr to binder
|
||||
* void *: cookie for binder
|
||||
*/
|
||||
|
||||
BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
|
||||
/*
|
||||
* not currently supported
|
||||
* int: priority
|
||||
* int: descriptor
|
||||
*/
|
||||
|
||||
BC_REGISTER_LOOPER = _IO('c', 11),
|
||||
/*
|
||||
* No parameters.
|
||||
* Register a spawned looper thread with the device.
|
||||
*/
|
||||
|
||||
BC_ENTER_LOOPER = _IO('c', 12),
|
||||
BC_EXIT_LOOPER = _IO('c', 13),
|
||||
/*
|
||||
* No parameters.
|
||||
* These two commands are sent as an application-level thread
|
||||
* enters and exits the binder loop, respectively. They are
|
||||
* used so the binder can have an accurate count of the number
|
||||
* of looping threads it has available.
|
||||
*/
|
||||
|
||||
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie),
|
||||
/*
|
||||
* void *: ptr to binder
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie),
|
||||
/*
|
||||
* void *: ptr to binder
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BC_DEAD_BINDER_DONE = _IOW('c', 16, void *),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
};
|
||||
#include "uapi/binder.h"
|
||||
|
||||
#endif /* _LINUX_BINDER_H */
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ TRACE_EVENT(binder_transaction_node_to_ref,
|
|||
TP_STRUCT__entry(
|
||||
__field(int, debug_id)
|
||||
__field(int, node_debug_id)
|
||||
__field(void __user *, node_ptr)
|
||||
__field(binder_uintptr_t, node_ptr)
|
||||
__field(int, ref_debug_id)
|
||||
__field(uint32_t, ref_desc)
|
||||
),
|
||||
|
@ -163,8 +163,9 @@ TRACE_EVENT(binder_transaction_node_to_ref,
|
|||
__entry->ref_debug_id = ref->debug_id;
|
||||
__entry->ref_desc = ref->desc;
|
||||
),
|
||||
TP_printk("transaction=%d node=%d src_ptr=0x%p ==> dest_ref=%d dest_desc=%d",
|
||||
__entry->debug_id, __entry->node_debug_id, __entry->node_ptr,
|
||||
TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d",
|
||||
__entry->debug_id, __entry->node_debug_id,
|
||||
(u64)__entry->node_ptr,
|
||||
__entry->ref_debug_id, __entry->ref_desc)
|
||||
);
|
||||
|
||||
|
@ -177,7 +178,7 @@ TRACE_EVENT(binder_transaction_ref_to_node,
|
|||
__field(int, ref_debug_id)
|
||||
__field(uint32_t, ref_desc)
|
||||
__field(int, node_debug_id)
|
||||
__field(void __user *, node_ptr)
|
||||
__field(binder_uintptr_t, node_ptr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->debug_id = t->debug_id;
|
||||
|
@ -186,9 +187,10 @@ TRACE_EVENT(binder_transaction_ref_to_node,
|
|||
__entry->node_debug_id = ref->node->debug_id;
|
||||
__entry->node_ptr = ref->node->ptr;
|
||||
),
|
||||
TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%p",
|
||||
TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx",
|
||||
__entry->debug_id, __entry->node_debug_id,
|
||||
__entry->ref_debug_id, __entry->ref_desc, __entry->node_ptr)
|
||||
__entry->ref_debug_id, __entry->ref_desc,
|
||||
(u64)__entry->node_ptr)
|
||||
);
|
||||
|
||||
TRACE_EVENT(binder_transaction_ref_to_ref,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/fs.h>
|
||||
|
@ -55,10 +56,12 @@ struct ion_device {
|
|||
struct mutex buffer_lock;
|
||||
struct rw_semaphore lock;
|
||||
struct plist_head heaps;
|
||||
long (*custom_ioctl) (struct ion_client *client, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
struct rb_root clients;
|
||||
struct dentry *debug_root;
|
||||
struct dentry *heaps_debug_root;
|
||||
struct dentry *clients_debug_root;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -69,6 +72,8 @@ struct ion_device {
|
|||
* @idr: an idr space for allocating handle ids
|
||||
* @lock: lock protecting the tree of handles
|
||||
* @name: used for debugging
|
||||
* @display_name: used for debugging (unique version of @name)
|
||||
* @display_serial: used for debugging (to make display_name unique)
|
||||
* @task: used for debugging
|
||||
*
|
||||
* A client represents a list of buffers this client may access.
|
||||
|
@ -82,6 +87,8 @@ struct ion_client {
|
|||
struct idr idr;
|
||||
struct mutex lock;
|
||||
const char *name;
|
||||
char *display_name;
|
||||
int display_serial;
|
||||
struct task_struct *task;
|
||||
pid_t pid;
|
||||
struct dentry *debug_root;
|
||||
|
@ -208,7 +215,7 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
|
|||
if (IS_ERR(table)) {
|
||||
heap->ops->free(buffer);
|
||||
kfree(buffer);
|
||||
return ERR_PTR(PTR_ERR(table));
|
||||
return ERR_CAST(table);
|
||||
}
|
||||
buffer->sg_table = table;
|
||||
if (ion_buffer_fault_user_mappings(buffer)) {
|
||||
|
@ -429,7 +436,7 @@ static bool ion_handle_validate(struct ion_client *client,
|
|||
struct ion_handle *handle)
|
||||
{
|
||||
WARN_ON(!mutex_is_locked(&client->lock));
|
||||
return (idr_find(&client->idr, handle->id) == handle);
|
||||
return idr_find(&client->idr, handle->id) == handle;
|
||||
}
|
||||
|
||||
static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
|
||||
|
@ -501,7 +508,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
|
|||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (IS_ERR(buffer))
|
||||
return ERR_PTR(PTR_ERR(buffer));
|
||||
return ERR_CAST(buffer);
|
||||
|
||||
handle = ion_handle_create(client, buffer);
|
||||
|
||||
|
@ -708,6 +715,21 @@ static const struct file_operations debug_client_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static int ion_get_client_serial(const struct rb_root *root,
|
||||
const unsigned char *name)
|
||||
{
|
||||
int serial = -1;
|
||||
struct rb_node *node;
|
||||
for (node = rb_first(root); node; node = rb_next(node)) {
|
||||
struct ion_client *client = rb_entry(node, struct ion_client,
|
||||
node);
|
||||
if (strcmp(client->name, name))
|
||||
continue;
|
||||
serial = max(serial, client->display_serial);
|
||||
}
|
||||
return serial + 1;
|
||||
}
|
||||
|
||||
struct ion_client *ion_client_create(struct ion_device *dev,
|
||||
const char *name)
|
||||
{
|
||||
|
@ -716,9 +738,13 @@ struct ion_client *ion_client_create(struct ion_device *dev,
|
|||
struct rb_node **p;
|
||||
struct rb_node *parent = NULL;
|
||||
struct ion_client *entry;
|
||||
char debug_name[64];
|
||||
pid_t pid;
|
||||
|
||||
if (!name) {
|
||||
pr_err("%s: Name cannot be null\n", __func__);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
get_task_struct(current->group_leader);
|
||||
task_lock(current->group_leader);
|
||||
pid = task_pid_nr(current->group_leader);
|
||||
|
@ -733,21 +759,27 @@ struct ion_client *ion_client_create(struct ion_device *dev,
|
|||
task_unlock(current->group_leader);
|
||||
|
||||
client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
|
||||
if (!client) {
|
||||
if (task)
|
||||
put_task_struct(current->group_leader);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
if (!client)
|
||||
goto err_put_task_struct;
|
||||
|
||||
client->dev = dev;
|
||||
client->handles = RB_ROOT;
|
||||
idr_init(&client->idr);
|
||||
mutex_init(&client->lock);
|
||||
client->name = name;
|
||||
client->task = task;
|
||||
client->pid = pid;
|
||||
client->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!client->name)
|
||||
goto err_free_client;
|
||||
|
||||
down_write(&dev->lock);
|
||||
client->display_serial = ion_get_client_serial(&dev->clients, name);
|
||||
client->display_name = kasprintf(
|
||||
GFP_KERNEL, "%s-%d", name, client->display_serial);
|
||||
if (!client->display_name) {
|
||||
up_write(&dev->lock);
|
||||
goto err_free_client_name;
|
||||
}
|
||||
p = &dev->clients.rb_node;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
|
@ -761,13 +793,28 @@ struct ion_client *ion_client_create(struct ion_device *dev,
|
|||
rb_link_node(&client->node, parent, p);
|
||||
rb_insert_color(&client->node, &dev->clients);
|
||||
|
||||
snprintf(debug_name, 64, "%u", client->pid);
|
||||
client->debug_root = debugfs_create_file(debug_name, 0664,
|
||||
dev->debug_root, client,
|
||||
&debug_client_fops);
|
||||
client->debug_root = debugfs_create_file(client->display_name, 0664,
|
||||
dev->clients_debug_root,
|
||||
client, &debug_client_fops);
|
||||
if (!client->debug_root) {
|
||||
char buf[256], *path;
|
||||
path = dentry_path(dev->clients_debug_root, buf, 256);
|
||||
pr_err("Failed to create client debugfs at %s/%s\n",
|
||||
path, client->display_name);
|
||||
}
|
||||
|
||||
up_write(&dev->lock);
|
||||
|
||||
return client;
|
||||
|
||||
err_free_client_name:
|
||||
kfree(client->name);
|
||||
err_free_client:
|
||||
kfree(client);
|
||||
err_put_task_struct:
|
||||
if (task)
|
||||
put_task_struct(current->group_leader);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
EXPORT_SYMBOL(ion_client_create);
|
||||
|
||||
|
@ -792,6 +839,8 @@ void ion_client_destroy(struct ion_client *client)
|
|||
debugfs_remove_recursive(client->debug_root);
|
||||
up_write(&dev->lock);
|
||||
|
||||
kfree(client->display_name);
|
||||
kfree(client->name);
|
||||
kfree(client);
|
||||
}
|
||||
EXPORT_SYMBOL(ion_client_destroy);
|
||||
|
@ -954,8 +1003,8 @@ static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
|||
int ret = 0;
|
||||
|
||||
if (!buffer->heap->ops->map_user) {
|
||||
pr_err("%s: this heap does not define a method for mapping "
|
||||
"to userspace\n", __func__);
|
||||
pr_err("%s: this heap does not define a method for mapping to userspace\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1017,9 +1066,7 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start,
|
|||
mutex_lock(&buffer->lock);
|
||||
vaddr = ion_buffer_kmap_get(buffer);
|
||||
mutex_unlock(&buffer->lock);
|
||||
if (IS_ERR(vaddr))
|
||||
return PTR_ERR(vaddr);
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(vaddr);
|
||||
}
|
||||
|
||||
static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start,
|
||||
|
@ -1100,7 +1147,7 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
|
|||
|
||||
dmabuf = dma_buf_get(fd);
|
||||
if (IS_ERR(dmabuf))
|
||||
return ERR_PTR(PTR_ERR(dmabuf));
|
||||
return ERR_CAST(dmabuf);
|
||||
/* if this memory came from ion */
|
||||
|
||||
if (dmabuf->ops != &dma_buf_ops) {
|
||||
|
@ -1293,9 +1340,11 @@ static int ion_open(struct inode *inode, struct file *file)
|
|||
struct miscdevice *miscdev = file->private_data;
|
||||
struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
|
||||
struct ion_client *client;
|
||||
char debug_name[64];
|
||||
|
||||
pr_debug("%s: %d\n", __func__, __LINE__);
|
||||
client = ion_client_create(dev, "user");
|
||||
snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
|
||||
client = ion_client_create(dev, debug_name);
|
||||
if (IS_ERR(client))
|
||||
return PTR_ERR(client);
|
||||
file->private_data = client;
|
||||
|
@ -1338,7 +1387,7 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
size_t total_orphaned_size = 0;
|
||||
|
||||
seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size");
|
||||
seq_printf(s, "----------------------------------------------------\n");
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
|
||||
for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
|
||||
struct ion_client *client = rb_entry(n, struct ion_client,
|
||||
|
@ -1357,9 +1406,8 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
client->pid, size);
|
||||
}
|
||||
}
|
||||
seq_printf(s, "----------------------------------------------------\n");
|
||||
seq_printf(s, "orphaned allocations (info is from last known client):"
|
||||
"\n");
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
seq_puts(s, "orphaned allocations (info is from last known client):\n");
|
||||
mutex_lock(&dev->buffer_lock);
|
||||
for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
|
||||
struct ion_buffer *buffer = rb_entry(n, struct ion_buffer,
|
||||
|
@ -1376,14 +1424,14 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
|
|||
}
|
||||
}
|
||||
mutex_unlock(&dev->buffer_lock);
|
||||
seq_printf(s, "----------------------------------------------------\n");
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
seq_printf(s, "%16.s %16zu\n", "total orphaned",
|
||||
total_orphaned_size);
|
||||
seq_printf(s, "%16.s %16zu\n", "total ", total_size);
|
||||
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
|
||||
seq_printf(s, "%16.s %16zu\n", "deferred free",
|
||||
heap->free_list_size);
|
||||
seq_printf(s, "----------------------------------------------------\n");
|
||||
seq_puts(s, "----------------------------------------------------\n");
|
||||
|
||||
if (heap->debug_show)
|
||||
heap->debug_show(heap, s, unused);
|
||||
|
@ -1443,6 +1491,8 @@ DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get,
|
|||
|
||||
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
|
||||
{
|
||||
struct dentry *debug_file;
|
||||
|
||||
if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
|
||||
!heap->ops->unmap_dma)
|
||||
pr_err("%s: can not add heap with invalid ops struct.\n",
|
||||
|
@ -1451,21 +1501,40 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
|
|||
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
|
||||
ion_heap_init_deferred_free(heap);
|
||||
|
||||
if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink)
|
||||
ion_heap_init_shrinker(heap);
|
||||
|
||||
heap->dev = dev;
|
||||
down_write(&dev->lock);
|
||||
/* use negative heap->id to reverse the priority -- when traversing
|
||||
the list later attempt higher id numbers first */
|
||||
plist_node_init(&heap->node, -heap->id);
|
||||
plist_add(&heap->node, &dev->heaps);
|
||||
debugfs_create_file(heap->name, 0664, dev->debug_root, heap,
|
||||
&debug_heap_fops);
|
||||
debug_file = debugfs_create_file(heap->name, 0664,
|
||||
dev->heaps_debug_root, heap,
|
||||
&debug_heap_fops);
|
||||
|
||||
if (!debug_file) {
|
||||
char buf[256], *path;
|
||||
path = dentry_path(dev->heaps_debug_root, buf, 256);
|
||||
pr_err("Failed to create heap debugfs at %s/%s\n",
|
||||
path, heap->name);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_HEAP_SHRINKER
|
||||
if (heap->shrinker.shrink) {
|
||||
char debug_name[64];
|
||||
|
||||
snprintf(debug_name, 64, "%s_shrink", heap->name);
|
||||
debugfs_create_file(debug_name, 0644, dev->debug_root, heap,
|
||||
&debug_shrink_fops);
|
||||
debug_file = debugfs_create_file(
|
||||
debug_name, 0644, dev->heaps_debug_root, heap,
|
||||
&debug_shrink_fops);
|
||||
if (!debug_file) {
|
||||
char buf[256], *path;
|
||||
path = dentry_path(dev->heaps_debug_root, buf, 256);
|
||||
pr_err("Failed to create heap shrinker debugfs at %s/%s\n",
|
||||
path, debug_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
up_write(&dev->lock);
|
||||
|
@ -1494,8 +1563,21 @@ struct ion_device *ion_device_create(long (*custom_ioctl)
|
|||
}
|
||||
|
||||
idev->debug_root = debugfs_create_dir("ion", NULL);
|
||||
if (!idev->debug_root)
|
||||
pr_err("ion: failed to create debug files.\n");
|
||||
if (!idev->debug_root) {
|
||||
pr_err("ion: failed to create debugfs root directory.\n");
|
||||
goto debugfs_done;
|
||||
}
|
||||
idev->heaps_debug_root = debugfs_create_dir("heaps", idev->debug_root);
|
||||
if (!idev->heaps_debug_root) {
|
||||
pr_err("ion: failed to create debugfs heaps directory.\n");
|
||||
goto debugfs_done;
|
||||
}
|
||||
idev->clients_debug_root = debugfs_create_dir("clients",
|
||||
idev->debug_root);
|
||||
if (!idev->clients_debug_root)
|
||||
pr_err("ion: failed to create debugfs clients directory.\n");
|
||||
|
||||
debugfs_done:
|
||||
|
||||
idev->custom_ioctl = custom_ioctl;
|
||||
idev->buffers = RB_ROOT;
|
||||
|
@ -1509,6 +1591,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl)
|
|||
void ion_device_destroy(struct ion_device *dev)
|
||||
{
|
||||
misc_deregister(&dev->dev);
|
||||
debugfs_remove_recursive(dev->debug_root);
|
||||
/* XXX need to free the heaps and clients ? */
|
||||
kfree(dev);
|
||||
}
|
||||
|
@ -1527,8 +1610,7 @@ void __init ion_reserve(struct ion_platform_data *data)
|
|||
data->heaps[i].align,
|
||||
MEMBLOCK_ALLOC_ANYWHERE);
|
||||
if (!paddr) {
|
||||
pr_err("%s: error allocating memblock for "
|
||||
"heap %d\n",
|
||||
pr_err("%s: error allocating memblock for heap %d\n",
|
||||
__func__, i);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
|
|||
struct device *dev = cma_heap->dev;
|
||||
struct ion_cma_buffer_info *info = buffer->priv_virt;
|
||||
|
||||
dev_dbg(dev, "Return buffer %p physical address 0x%pa\n", buffer,
|
||||
dev_dbg(dev, "Return buffer %p physical address %pa\n", buffer,
|
||||
&info->handle);
|
||||
|
||||
*addr = info->handle;
|
||||
|
|
|
@ -25,13 +25,13 @@
|
|||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
|
||||
struct ion_device *idev;
|
||||
struct ion_heap **heaps;
|
||||
static struct ion_device *idev;
|
||||
static struct ion_heap **heaps;
|
||||
|
||||
void *carveout_ptr;
|
||||
void *chunk_ptr;
|
||||
static void *carveout_ptr;
|
||||
static void *chunk_ptr;
|
||||
|
||||
struct ion_platform_heap dummy_heaps[] = {
|
||||
static struct ion_platform_heap dummy_heaps[] = {
|
||||
{
|
||||
.id = ION_HEAP_TYPE_SYSTEM,
|
||||
.type = ION_HEAP_TYPE_SYSTEM,
|
||||
|
@ -58,7 +58,7 @@ struct ion_platform_heap dummy_heaps[] = {
|
|||
},
|
||||
};
|
||||
|
||||
struct ion_platform_data dummy_ion_pdata = {
|
||||
static struct ion_platform_data dummy_ion_pdata = {
|
||||
.nr = ARRAY_SIZE(dummy_heaps),
|
||||
.heaps = dummy_heaps,
|
||||
};
|
||||
|
|
|
@ -178,7 +178,8 @@ size_t ion_heap_freelist_size(struct ion_heap *heap)
|
|||
return size;
|
||||
}
|
||||
|
||||
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
|
||||
static size_t _ion_heap_freelist_drain(struct ion_heap *heap, size_t size,
|
||||
bool skip_pools)
|
||||
{
|
||||
struct ion_buffer *buffer;
|
||||
size_t total_drained = 0;
|
||||
|
@ -197,6 +198,8 @@ size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
|
|||
list);
|
||||
list_del(&buffer->list);
|
||||
heap->free_list_size -= buffer->size;
|
||||
if (skip_pools)
|
||||
buffer->private_flags |= ION_PRIV_FLAG_SHRINKER_FREE;
|
||||
total_drained += buffer->size;
|
||||
spin_unlock(&heap->free_lock);
|
||||
ion_buffer_destroy(buffer);
|
||||
|
@ -207,6 +210,16 @@ size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
|
|||
return total_drained;
|
||||
}
|
||||
|
||||
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
|
||||
{
|
||||
return _ion_heap_freelist_drain(heap, size, false);
|
||||
}
|
||||
|
||||
size_t ion_heap_freelist_shrink(struct ion_heap *heap, size_t size)
|
||||
{
|
||||
return _ion_heap_freelist_drain(heap, size, true);
|
||||
}
|
||||
|
||||
static int ion_heap_deferred_free(void *data)
|
||||
{
|
||||
struct ion_heap *heap = data;
|
||||
|
@ -246,12 +259,62 @@ int ion_heap_init_deferred_free(struct ion_heap *heap)
|
|||
if (IS_ERR(heap->task)) {
|
||||
pr_err("%s: creating thread for deferred free failed\n",
|
||||
__func__);
|
||||
return PTR_RET(heap->task);
|
||||
return PTR_ERR_OR_ZERO(heap->task);
|
||||
}
|
||||
sched_setscheduler(heap->task, SCHED_IDLE, ¶m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long ion_heap_shrink_count(struct shrinker *shrinker,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
int total = 0;
|
||||
|
||||
total = ion_heap_freelist_size(heap) / PAGE_SIZE;
|
||||
if (heap->ops->shrink)
|
||||
total += heap->ops->shrink(heap, sc->gfp_mask, 0);
|
||||
return total;
|
||||
}
|
||||
|
||||
static unsigned long ion_heap_shrink_scan(struct shrinker *shrinker,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
int freed = 0;
|
||||
int to_scan = sc->nr_to_scan;
|
||||
|
||||
if (to_scan == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* shrink the free list first, no point in zeroing the memory if we're
|
||||
* just going to reclaim it. Also, skip any possible page pooling.
|
||||
*/
|
||||
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
|
||||
freed = ion_heap_freelist_shrink(heap, to_scan * PAGE_SIZE) /
|
||||
PAGE_SIZE;
|
||||
|
||||
to_scan -= freed;
|
||||
if (to_scan <= 0)
|
||||
return freed;
|
||||
|
||||
if (heap->ops->shrink)
|
||||
freed += heap->ops->shrink(heap, sc->gfp_mask, to_scan);
|
||||
return freed;
|
||||
}
|
||||
|
||||
void ion_heap_init_shrinker(struct ion_heap *heap)
|
||||
{
|
||||
heap->shrinker.count_objects = ion_heap_shrink_count;
|
||||
heap->shrinker.scan_objects = ion_heap_shrink_scan;
|
||||
heap->shrinker.seeks = DEFAULT_SEEKS;
|
||||
heap->shrinker.batch = 0;
|
||||
register_shrinker(&heap->shrinker);
|
||||
}
|
||||
|
||||
struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
|
||||
{
|
||||
struct ion_heap *heap = NULL;
|
||||
|
|
|
@ -130,8 +130,7 @@ static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
|
|||
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
int nr_freed = 0;
|
||||
int i;
|
||||
int freed;
|
||||
bool high;
|
||||
|
||||
high = !!(gfp_mask & __GFP_HIGHMEM);
|
||||
|
@ -139,7 +138,7 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
|||
if (nr_to_scan == 0)
|
||||
return ion_page_pool_total(pool, high);
|
||||
|
||||
for (i = 0; i < nr_to_scan; i++) {
|
||||
for (freed = 0; freed < nr_to_scan; freed++) {
|
||||
struct page *page;
|
||||
|
||||
mutex_lock(&pool->mutex);
|
||||
|
@ -153,10 +152,9 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
|||
}
|
||||
mutex_unlock(&pool->mutex);
|
||||
ion_page_pool_free_pages(pool, page);
|
||||
nr_freed += (1 << pool->order);
|
||||
}
|
||||
|
||||
return nr_freed;
|
||||
return freed;
|
||||
}
|
||||
|
||||
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
|
||||
|
|
|
@ -38,6 +38,7 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
|
|||
* @dev: back pointer to the ion_device
|
||||
* @heap: back pointer to the heap the buffer came from
|
||||
* @flags: buffer specific flags
|
||||
* @private_flags: internal buffer specific flags
|
||||
* @size: size of the buffer
|
||||
* @priv_virt: private data to the buffer representable as
|
||||
* a void *
|
||||
|
@ -66,6 +67,7 @@ struct ion_buffer {
|
|||
struct ion_device *dev;
|
||||
struct ion_heap *heap;
|
||||
unsigned long flags;
|
||||
unsigned long private_flags;
|
||||
size_t size;
|
||||
union {
|
||||
void *priv_virt;
|
||||
|
@ -98,22 +100,27 @@ void ion_buffer_destroy(struct ion_buffer *buffer);
|
|||
* @map_user map memory to userspace
|
||||
*
|
||||
* allocate, phys, and map_user return 0 on success, -errno on error.
|
||||
* map_dma and map_kernel return pointer on success, ERR_PTR on error.
|
||||
* map_dma and map_kernel return pointer on success, ERR_PTR on
|
||||
* error. @free will be called with ION_PRIV_FLAG_SHRINKER_FREE set in
|
||||
* the buffer's private_flags when called from a shrinker. In that
|
||||
* case, the pages being free'd must be truly free'd back to the
|
||||
* system, not put in a page pool or otherwise cached.
|
||||
*/
|
||||
struct ion_heap_ops {
|
||||
int (*allocate) (struct ion_heap *heap,
|
||||
struct ion_buffer *buffer, unsigned long len,
|
||||
unsigned long align, unsigned long flags);
|
||||
void (*free) (struct ion_buffer *buffer);
|
||||
int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
ion_phys_addr_t *addr, size_t *len);
|
||||
struct sg_table *(*map_dma) (struct ion_heap *heap,
|
||||
struct ion_buffer *buffer);
|
||||
void (*unmap_dma) (struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer,
|
||||
struct vm_area_struct *vma);
|
||||
int (*allocate)(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer, unsigned long len,
|
||||
unsigned long align, unsigned long flags);
|
||||
void (*free)(struct ion_buffer *buffer);
|
||||
int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
|
||||
ion_phys_addr_t *addr, size_t *len);
|
||||
struct sg_table * (*map_dma)(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer);
|
||||
void (*unmap_dma)(struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
|
||||
int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
|
||||
struct vm_area_struct *vma);
|
||||
int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -121,6 +128,17 @@ struct ion_heap_ops {
|
|||
*/
|
||||
#define ION_HEAP_FLAG_DEFER_FREE (1 << 0)
|
||||
|
||||
/**
|
||||
* private flags - flags internal to ion
|
||||
*/
|
||||
/*
|
||||
* Buffer is being freed from a shrinker function. Skip any possible
|
||||
* heap-specific caching mechanism (e.g. page pools). Guarantees that
|
||||
* any buffer storage that came from the system allocator will be
|
||||
* returned to the system allocator.
|
||||
*/
|
||||
#define ION_PRIV_FLAG_SHRINKER_FREE (1 << 0)
|
||||
|
||||
/**
|
||||
* struct ion_heap - represents a heap in the system
|
||||
* @node: rb node to put the heap on the device's tree of heaps
|
||||
|
@ -132,10 +150,7 @@ struct ion_heap_ops {
|
|||
* allocating. These are specified by platform data and
|
||||
* MUST be unique
|
||||
* @name: used for debugging
|
||||
* @shrinker: a shrinker for the heap, if the heap caches system
|
||||
* memory, it must define a shrinker to return it on low
|
||||
* memory conditions, this includes system memory cached
|
||||
* in the deferred free lists for heaps that support it
|
||||
* @shrinker: a shrinker for the heap
|
||||
* @free_list: free list head if deferred free is used
|
||||
* @free_list_size size of the deferred free list in bytes
|
||||
* @lock: protects the free list
|
||||
|
@ -218,6 +233,16 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
|
|||
int ion_heap_buffer_zero(struct ion_buffer *buffer);
|
||||
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
|
||||
|
||||
/**
|
||||
* ion_heap_init_shrinker
|
||||
* @heap: the heap
|
||||
*
|
||||
* If a heap sets the ION_HEAP_FLAG_DEFER_FREE flag or defines the shrink op
|
||||
* this function will be called to setup a shrinker to shrink the freelists
|
||||
* and call the heap's shrink op.
|
||||
*/
|
||||
void ion_heap_init_shrinker(struct ion_heap *heap);
|
||||
|
||||
/**
|
||||
* ion_heap_init_deferred_free -- initialize deferred free functionality
|
||||
* @heap: the heap
|
||||
|
@ -249,6 +274,29 @@ void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer);
|
|||
*/
|
||||
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size);
|
||||
|
||||
/**
|
||||
* ion_heap_freelist_shrink - drain the deferred free
|
||||
* list, skipping any heap-specific
|
||||
* pooling or caching mechanisms
|
||||
*
|
||||
* @heap: the heap
|
||||
* @size: amount of memory to drain in bytes
|
||||
*
|
||||
* Drains the indicated amount of memory from the deferred freelist immediately.
|
||||
* Returns the total amount freed. The total freed may be higher depending
|
||||
* on the size of the items in the list, or lower if there is insufficient
|
||||
* total memory on the freelist.
|
||||
*
|
||||
* Unlike with @ion_heap_freelist_drain, don't put any pages back into
|
||||
* page pools or otherwise cache the pages. Everything must be
|
||||
* genuinely free'd back to the system. If you're free'ing from a
|
||||
* shrinker you probably want to use this. Note that this relies on
|
||||
* the heap.ops.free callback honoring the ION_PRIV_FLAG_SHRINKER_FREE
|
||||
* flag.
|
||||
*/
|
||||
size_t ion_heap_freelist_shrink(struct ion_heap *heap,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* ion_heap_freelist_size - returns the size of the freelist in bytes
|
||||
* @heap: the heap
|
||||
|
@ -305,13 +353,8 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
|
|||
* @low_count: number of lowmem items in the pool
|
||||
* @high_items: list of highmem items
|
||||
* @low_items: list of lowmem items
|
||||
* @shrinker: a shrinker for the items
|
||||
* @mutex: lock protecting this struct and especially the count
|
||||
* item list
|
||||
* @alloc: function to be used to allocate pageory when the pool
|
||||
* is empty
|
||||
* @free: function to be used to free pageory back to the system
|
||||
* when the shrinker fires
|
||||
* @gfp_mask: gfp_mask to use from alloc
|
||||
* @order: order of pages in the pool
|
||||
* @list: plist node for list of pools
|
||||
|
|
|
@ -90,7 +90,7 @@ static void free_buffer_page(struct ion_system_heap *heap,
|
|||
{
|
||||
bool cached = ion_buffer_cached(buffer);
|
||||
|
||||
if (!cached) {
|
||||
if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE)) {
|
||||
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
|
||||
ion_page_pool_free(pool, page);
|
||||
} else {
|
||||
|
@ -209,7 +209,7 @@ static void ion_system_heap_free(struct ion_buffer *buffer)
|
|||
|
||||
/* uncached pages come from the page pools, zero them before returning
|
||||
for security purposes (other allocations are zerod at alloc time */
|
||||
if (!cached)
|
||||
if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))
|
||||
ion_heap_buffer_zero(buffer);
|
||||
|
||||
for_each_sg(table->sgl, sg, table->nents, i)
|
||||
|
@ -231,6 +231,23 @@ static void ion_system_heap_unmap_dma(struct ion_heap *heap,
|
|||
return;
|
||||
}
|
||||
|
||||
static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
struct ion_system_heap *sys_heap;
|
||||
int nr_total = 0;
|
||||
int i;
|
||||
|
||||
sys_heap = container_of(heap, struct ion_system_heap, heap);
|
||||
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
nr_total += ion_page_pool_shrink(pool, gfp_mask, nr_to_scan);
|
||||
}
|
||||
|
||||
return nr_total;
|
||||
}
|
||||
|
||||
static struct ion_heap_ops system_heap_ops = {
|
||||
.allocate = ion_system_heap_allocate,
|
||||
.free = ion_system_heap_free,
|
||||
|
@ -239,67 +256,9 @@ static struct ion_heap_ops system_heap_ops = {
|
|||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
.map_user = ion_heap_map_user,
|
||||
.shrink = ion_system_heap_shrink,
|
||||
};
|
||||
|
||||
static unsigned long ion_system_heap_shrink_count(struct shrinker *shrinker,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
struct ion_system_heap *sys_heap = container_of(heap,
|
||||
struct ion_system_heap,
|
||||
heap);
|
||||
int nr_total = 0;
|
||||
int i;
|
||||
|
||||
/* total number of items is whatever the page pools are holding
|
||||
plus whatever's in the freelist */
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
nr_total += ion_page_pool_shrink(pool, sc->gfp_mask, 0);
|
||||
}
|
||||
nr_total += ion_heap_freelist_size(heap) / PAGE_SIZE;
|
||||
return nr_total;
|
||||
|
||||
}
|
||||
|
||||
static unsigned long ion_system_heap_shrink_scan(struct shrinker *shrinker,
|
||||
struct shrink_control *sc)
|
||||
{
|
||||
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
struct ion_system_heap *sys_heap = container_of(heap,
|
||||
struct ion_system_heap,
|
||||
heap);
|
||||
int nr_freed = 0;
|
||||
int i;
|
||||
|
||||
if (sc->nr_to_scan == 0)
|
||||
goto end;
|
||||
|
||||
/* shrink the free list first, no point in zeroing the memory if
|
||||
we're just going to reclaim it */
|
||||
nr_freed += ion_heap_freelist_drain(heap, sc->nr_to_scan * PAGE_SIZE) /
|
||||
PAGE_SIZE;
|
||||
|
||||
if (nr_freed >= sc->nr_to_scan)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
|
||||
nr_freed += ion_page_pool_shrink(pool, sc->gfp_mask,
|
||||
sc->nr_to_scan);
|
||||
if (nr_freed >= sc->nr_to_scan)
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
return nr_freed;
|
||||
|
||||
}
|
||||
|
||||
static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
|
||||
void *unused)
|
||||
{
|
||||
|
@ -347,11 +306,6 @@ struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
|
|||
heap->pools[i] = pool;
|
||||
}
|
||||
|
||||
heap->heap.shrinker.scan_objects = ion_system_heap_shrink_scan;
|
||||
heap->heap.shrinker.count_objects = ion_system_heap_shrink_count;
|
||||
heap->heap.shrinker.seeks = DEFAULT_SEEKS;
|
||||
heap->heap.shrinker.batch = 0;
|
||||
register_shrinker(&heap->heap.shrinker);
|
||||
heap->heap.debug_show = ion_system_heap_debug_show;
|
||||
return &heap->heap;
|
||||
err_create_pool:
|
||||
|
|
|
@ -32,13 +32,13 @@ static int tegra_ion_probe(struct platform_device *pdev)
|
|||
|
||||
num_heaps = pdata->nr;
|
||||
|
||||
heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL);
|
||||
heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_heap *) * pdata->nr,
|
||||
GFP_KERNEL);
|
||||
|
||||
idev = ion_device_create(NULL);
|
||||
if (IS_ERR_OR_NULL(idev)) {
|
||||
kfree(heaps);
|
||||
if (IS_ERR_OR_NULL(idev))
|
||||
return PTR_ERR(idev);
|
||||
}
|
||||
|
||||
/* create the heaps as specified in the board file */
|
||||
for (i = 0; i < num_heaps; i++) {
|
||||
|
@ -58,7 +58,6 @@ static int tegra_ion_probe(struct platform_device *pdev)
|
|||
if (heaps[i])
|
||||
ion_heap_destroy(heaps[i]);
|
||||
}
|
||||
kfree(heaps);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -70,7 +69,6 @@ static int tegra_ion_remove(struct platform_device *pdev)
|
|||
ion_device_destroy(idev);
|
||||
for (i = 0; i < num_heaps; i++)
|
||||
ion_heap_destroy(heaps[i]);
|
||||
kfree(heaps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
|
|||
int array_size = ARRAY_SIZE(lowmem_adj);
|
||||
int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
|
||||
int other_file = global_page_state(NR_FILE_PAGES) -
|
||||
global_page_state(NR_SHMEM);
|
||||
global_page_state(NR_SHMEM) -
|
||||
total_swapcache_pages();
|
||||
|
||||
if (lowmem_adj_size < array_size)
|
||||
array_size = lowmem_adj_size;
|
||||
|
@ -159,8 +160,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
|
|||
selected->pid, selected->comm,
|
||||
selected_oom_score_adj, selected_tasksize);
|
||||
lowmem_deathpending_timeout = jiffies + HZ;
|
||||
send_sig(SIGKILL, selected, 0);
|
||||
set_tsk_thread_flag(selected, TIF_MEMDIE);
|
||||
send_sig(SIGKILL, selected, 0);
|
||||
rem += selected_tasksize;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,9 @@
|
|||
#define _LINUX_SW_SYNC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/kconfig.h>
|
||||
#include "sync.h"
|
||||
#include "uapi/sw_sync.h"
|
||||
|
||||
struct sw_sync_timeline {
|
||||
struct sync_timeline obj;
|
||||
|
@ -57,19 +56,4 @@ static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj,
|
|||
}
|
||||
#endif /* IS_ENABLED(CONFIG_SW_SYNC) */
|
||||
|
||||
#endif /* __KERNEL __ */
|
||||
|
||||
struct sw_sync_create_fence_data {
|
||||
__u32 value;
|
||||
char name[32];
|
||||
__s32 fence; /* fd of new fence */
|
||||
};
|
||||
|
||||
#define SW_SYNC_IOC_MAGIC 'W'
|
||||
|
||||
#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\
|
||||
struct sw_sync_create_fence_data)
|
||||
#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
|
||||
|
||||
|
||||
#endif /* _LINUX_SW_SYNC_H */
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
#define _LINUX_SYNC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "uapi/sync.h"
|
||||
|
||||
struct sync_timeline;
|
||||
struct sync_pt;
|
||||
struct sync_fence;
|
||||
|
@ -53,7 +53,7 @@ struct sync_timeline_ops {
|
|||
const char *driver_name;
|
||||
|
||||
/* required */
|
||||
struct sync_pt *(*dup)(struct sync_pt *pt);
|
||||
struct sync_pt * (*dup)(struct sync_pt *pt);
|
||||
|
||||
/* required */
|
||||
int (*has_signaled)(struct sync_pt *pt);
|
||||
|
@ -341,86 +341,4 @@ int sync_fence_cancel_async(struct sync_fence *fence,
|
|||
*/
|
||||
int sync_fence_wait(struct sync_fence *fence, long timeout);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/**
|
||||
* struct sync_merge_data - data passed to merge ioctl
|
||||
* @fd2: file descriptor of second fence
|
||||
* @name: name of new fence
|
||||
* @fence: returns the fd of the new fence to userspace
|
||||
*/
|
||||
struct sync_merge_data {
|
||||
__s32 fd2; /* fd of second fence */
|
||||
char name[32]; /* name of new fence */
|
||||
__s32 fence; /* fd on newly created fence */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_pt_info - detailed sync_pt information
|
||||
* @len: length of sync_pt_info including any driver_data
|
||||
* @obj_name: name of parent sync_timeline
|
||||
* @driver_name: name of driver implementing the parent
|
||||
* @status: status of the sync_pt 0:active 1:signaled <0:error
|
||||
* @timestamp_ns: timestamp of status change in nanoseconds
|
||||
* @driver_data: any driver dependent data
|
||||
*/
|
||||
struct sync_pt_info {
|
||||
__u32 len;
|
||||
char obj_name[32];
|
||||
char driver_name[32];
|
||||
__s32 status;
|
||||
__u64 timestamp_ns;
|
||||
|
||||
__u8 driver_data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sync_fence_info_data - data returned from fence info ioctl
|
||||
* @len: ioctl caller writes the size of the buffer its passing in.
|
||||
* ioctl returns length of sync_fence_data returned to userspace
|
||||
* including pt_info.
|
||||
* @name: name of fence
|
||||
* @status: status of fence. 1: signaled 0:active <0:error
|
||||
* @pt_info: a sync_pt_info struct for every sync_pt in the fence
|
||||
*/
|
||||
struct sync_fence_info_data {
|
||||
__u32 len;
|
||||
char name[32];
|
||||
__s32 status;
|
||||
|
||||
__u8 pt_info[0];
|
||||
};
|
||||
|
||||
#define SYNC_IOC_MAGIC '>'
|
||||
|
||||
/**
|
||||
* DOC: SYNC_IOC_WAIT - wait for a fence to signal
|
||||
*
|
||||
* pass timeout in milliseconds. Waits indefinitely timeout < 0.
|
||||
*/
|
||||
#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
|
||||
|
||||
/**
|
||||
* DOC: SYNC_IOC_MERGE - merge two fences
|
||||
*
|
||||
* Takes a struct sync_merge_data. Creates a new fence containing copies of
|
||||
* the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
|
||||
* new fence's fd in sync_merge_data.fence
|
||||
*/
|
||||
#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
|
||||
|
||||
/**
|
||||
* DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence
|
||||
*
|
||||
* Takes a struct sync_fence_info_data with extra space allocated for pt_info.
|
||||
* Caller should write the size of the buffer into len. On return, len is
|
||||
* updated to reflect the total size of the sync_fence_info_data including
|
||||
* pt_info.
|
||||
*
|
||||
* pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
|
||||
* To iterate over the sync_pt_infos, use the sync_pt_info.len field.
|
||||
*/
|
||||
#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
|
||||
struct sync_fence_info_data)
|
||||
|
||||
#endif /* _LINUX_SYNC_H */
|
||||
|
|
|
@ -90,8 +90,9 @@ static int timed_gpio_probe(struct platform_device *pdev)
|
|||
if (!pdata)
|
||||
return -EBUSY;
|
||||
|
||||
gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios,
|
||||
GFP_KERNEL);
|
||||
gpio_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct timed_gpio_data) * pdata->num_gpios,
|
||||
GFP_KERNEL);
|
||||
if (!gpio_data)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -131,7 +132,6 @@ static int timed_gpio_probe(struct platform_device *pdev)
|
|||
timed_output_dev_unregister(&gpio_data[i].dev);
|
||||
gpio_free(gpio_data[i].gpio);
|
||||
}
|
||||
kfree(gpio_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -147,8 +147,6 @@ static int timed_gpio_remove(struct platform_device *pdev)
|
|||
gpio_free(gpio_data[i].gpio);
|
||||
}
|
||||
|
||||
kfree(gpio_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,10 +20,10 @@ struct timed_output_dev {
|
|||
const char *name;
|
||||
|
||||
/* enable the output and set the timer */
|
||||
void (*enable)(struct timed_output_dev *sdev, int timeout);
|
||||
void (*enable)(struct timed_output_dev *sdev, int timeout);
|
||||
|
||||
/* returns the current number of milliseconds remaining on the timer */
|
||||
int (*get_time)(struct timed_output_dev *sdev);
|
||||
int (*get_time)(struct timed_output_dev *sdev);
|
||||
|
||||
/* private data */
|
||||
struct device *dev;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue