mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
Merge tag 'staging-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging and IIO driver updates from Greg KH: "Here is the "big" set of staging and IIO driver patches for 5.12-rc1. Nothing really huge in here, the number of staging tree patches has gone down for a bit, maybe there's only so much churn to happen in here at the moment. The IIO changes are: - new drivers - new DT bindings - new iio driver features with full details in the shortlog. The staging driver patches are just a lot of tiny coding style cleanups, along with some semi-larger hikey driver cleanups as those are _almost_ good enough to get out of the staging tree, but will probably have to wait until 5.13 to have happen. All of these have been in linux-next with no reported issues" * tag 'staging-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (189 commits) staging: hikey9xx: Fix alignment of function parameters staging: greybus: Fixed a misspelling in hid.c staging: wimax/i2400m: fix some byte order issues found by sparse staging: wimax: i2400m: fix some incorrect type warnings staging: greybus: minor code style fix staging:wlan-ng: use memdup_user instead of kmalloc/copy_from_user staging:r8188eu: use IEEE80211_FCTL_* kernel definitions staging: rtl8192e: remove multiple blank lines staging: greybus: Fixed alignment issue in hid.c staging: wfx: remove unused included header files staging: nvec: minor coding style fix staging: wimax: Fix some coding style problem staging: fbtft: add tearing signal detect staging: vt6656: Fixed issue with alignment in rf.c staging: qlge: Remove duplicate word in comment staging: rtl8723bs: remove obsolete commented out code staging: rtl8723bs: fix function comments to follow kernel-doc staging: wfx: avoid defining array of flexible struct staging: rtl8723bs: Replace one-element array with flexible-array member in struct ndis_80211_var_ie staging: Replace lkml.org links with lore ...
This commit is contained in:
@@ -198,6 +198,7 @@ Description:
|
|||||||
Units after application of scale and offset are m/s^2.
|
Units after application of scale and offset are m/s^2.
|
||||||
|
|
||||||
What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw
|
What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_anglY_raw
|
||||||
KernelVersion: 4.17
|
KernelVersion: 4.17
|
||||||
Contact: linux-iio@vger.kernel.org
|
Contact: linux-iio@vger.kernel.org
|
||||||
Description:
|
Description:
|
||||||
@@ -1812,3 +1813,13 @@ Contact: linux-iio@vger.kernel.org
|
|||||||
Description:
|
Description:
|
||||||
Unscaled light intensity according to CIE 1931/DIN 5033 color space.
|
Unscaled light intensity according to CIE 1931/DIN 5033 color space.
|
||||||
Units after application of scale are nano nanowatts per square meter.
|
Units after application of scale are nano nanowatts per square meter.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_anglY_label
|
||||||
|
KernelVersion: 5.12
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Optional symbolic label for channel Y.
|
||||||
|
For Intel hid hinge sensor, the label values are:
|
||||||
|
hinge, keyboard, screen. It means the three channels
|
||||||
|
each correspond respectively to hinge angle, keyboard angle,
|
||||||
|
and screen angle.
|
||||||
|
31
Documentation/ABI/testing/sysfs-bus-iio-dac-ad5766
Normal file
31
Documentation/ABI/testing/sysfs-bus-iio-dac-ad5766
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_enable
|
||||||
|
KernelVersion: 5.12
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Dither enable. Write 1 to enable dither or 0 to disable it.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_invert
|
||||||
|
KernelVersion: 5.12
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Inverts the dither applied to the selected DAC channel. Dither is not
|
||||||
|
inverted by default. Write "1" to invert dither.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_scale_available
|
||||||
|
KernelVersion: 5.12
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Returns possible scalings available for the current channel.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_scale
|
||||||
|
KernelVersion: 5.12
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Scales the dither before it is applied to the selected channel.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_source
|
||||||
|
KernelVersion: 5.12
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Selects dither source applied to the selected channel. Write "0" to
|
||||||
|
select N0 source, write "1" to select N1 source.
|
@@ -20,6 +20,9 @@ properties:
|
|||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
vdd-supply: true
|
||||||
|
vddio-supply: true
|
||||||
|
|
||||||
mount-matrix:
|
mount-matrix:
|
||||||
description: an optional 3x3 mounting rotation matrix.
|
description: an optional 3x3 mounting rotation matrix.
|
||||||
|
|
||||||
|
@@ -1,13 +1,22 @@
|
|||||||
Xilinx XADC device driver
|
Xilinx XADC device driver
|
||||||
|
|
||||||
This binding document describes the bindings for both of them since the
|
This binding document describes the bindings for the Xilinx 7 Series XADC as well
|
||||||
bindings are very similar. The Xilinx XADC is a ADC that can be found in the
|
as the UltraScale/UltraScale+ System Monitor.
|
||||||
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
|
The Xilinx XADC is an ADC that can be found in the Series 7 FPGAs from Xilinx.
|
||||||
available on the ZYNQ family as a hardmacro in the SoC portion of the ZYNQ. The
|
The XADC has a DRP interface for communication. Currently two different
|
||||||
other one is available on all series 7 platforms and is a softmacro with a AXI
|
frontends for the DRP interface exist. One that is only available on the ZYNQ
|
||||||
interface. This binding document describes the bindings for both of them since
|
family as a hardmacro in the SoC portion of the ZYNQ. The other one is available
|
||||||
the bindings are very similar.
|
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.
|
||||||
|
|
||||||
|
The Xilinx System Monitor is an ADC that is found in the UltraScale and
|
||||||
|
UltraScale+ FPGAs from Xilinx. The System Monitor provides a DRP interface for
|
||||||
|
communication. Xilinx provides a standard IP core that can be used to access the
|
||||||
|
System Monitor through an AXI interface in the FPGA fabric. This IP core is
|
||||||
|
called the Xilinx System Management Wizard. This document describes the bindings
|
||||||
|
for this IP.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be one of
|
- compatible: Should be one of
|
||||||
@@ -15,11 +24,14 @@ Required properties:
|
|||||||
configuration interface to interface to the XADC hardmacro.
|
configuration interface to interface to the XADC hardmacro.
|
||||||
* "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
|
* "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
|
||||||
interface to the XADC hardmacro.
|
interface to the XADC hardmacro.
|
||||||
|
* "xlnx,system-management-wiz-1.3": When using the
|
||||||
|
Xilinx System Management Wizard fabric IP core to access the
|
||||||
|
UltraScale and UltraScale+ System Monitor.
|
||||||
- reg: Address and length of the register set for the device
|
- reg: Address and length of the register set for the device
|
||||||
- interrupts: Interrupt for the XADC control interface.
|
- interrupts: Interrupt for the XADC control interface.
|
||||||
- clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
|
- 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
|
when using the axi-xadc or the axi-system-management-wizard this must be
|
||||||
clock to the AXI bus interface of the core.
|
the clock that provides the clock to the AXI bus interface of the core.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- xlnx,external-mux:
|
- xlnx,external-mux:
|
||||||
@@ -110,3 +122,20 @@ Examples:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
adc@80000000 {
|
||||||
|
compatible = "xlnx,system-management-wiz-1.3";
|
||||||
|
reg = <0x80000000 0x1000>;
|
||||||
|
interrupts = <0 81 4>;
|
||||||
|
interrupt-parent = <&gic>;
|
||||||
|
clocks = <&fpga1_clk>;
|
||||||
|
|
||||||
|
xlnx,channels {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
channel@0 {
|
||||||
|
reg = <0>;
|
||||||
|
xlnx,bipolar;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
63
Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml
Normal file
63
Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||||
|
# Copyright 2020 Analog Devices Inc.
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/iio/dac/adi,ad5766.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Analog Devices AD5766 DAC device driver
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Cristian Pop <cristian.pop@analog.com>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Bindings for the Analog Devices AD5766 current DAC device. Datasheet can be
|
||||||
|
found here:
|
||||||
|
https://www.analog.com/media/en/technical-documentation/data-sheets/ad5766-5767.pdf
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- adi,ad5766
|
||||||
|
- adi,ad5767
|
||||||
|
|
||||||
|
output-range-microvolts:
|
||||||
|
description: Select converter output range.
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
spi-max-frequency:
|
||||||
|
maximum: 1000000
|
||||||
|
|
||||||
|
spi-cpol: true
|
||||||
|
|
||||||
|
reset-gpios:
|
||||||
|
description: GPIO spec for the RESET pin. As the line is active low, it
|
||||||
|
should be marked GPIO_ACTIVE_LOW.
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- output-range-microvolts
|
||||||
|
- reg
|
||||||
|
- spi-max-frequency
|
||||||
|
- spi-cpol
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
spi {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
ad5766@0 {
|
||||||
|
compatible = "adi,ad5766";
|
||||||
|
output-range-microvolts = <(-5000) 5000>;
|
||||||
|
reg = <0>;
|
||||||
|
spi-cpol;
|
||||||
|
spi-max-frequency = <1000000>;
|
||||||
|
reset-gpios = <&gpio 22 0>;
|
||||||
|
};
|
||||||
|
};
|
@@ -39,20 +39,39 @@ properties:
|
|||||||
|
|
||||||
allOf:
|
allOf:
|
||||||
- if:
|
- if:
|
||||||
not:
|
properties:
|
||||||
properties:
|
compatible:
|
||||||
compatible:
|
contains:
|
||||||
contains:
|
const: microchip,mcp4725
|
||||||
const: microchip,mcp4726
|
|
||||||
then:
|
then:
|
||||||
properties:
|
properties:
|
||||||
vref-supply: false
|
vref-supply: false
|
||||||
|
required:
|
||||||
|
- vdd-supply
|
||||||
|
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
const: microchip,mcp4726
|
||||||
|
then:
|
||||||
|
anyOf:
|
||||||
|
- required:
|
||||||
|
- vdd-supply
|
||||||
|
- required:
|
||||||
|
- vref-supply
|
||||||
|
|
||||||
|
- if:
|
||||||
|
not:
|
||||||
|
required:
|
||||||
|
- vref-supply
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
microchip,vref-buffered: false
|
microchip,vref-buffered: false
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
- vdd-supply
|
|
||||||
|
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
||||||
|
@@ -19,6 +19,9 @@ properties:
|
|||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
vdd-supply: true
|
||||||
|
vddio-supply: true
|
||||||
|
|
||||||
interrupts:
|
interrupts:
|
||||||
minItems: 1
|
minItems: 1
|
||||||
description:
|
description:
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
Invensense MPU-3050 Gyroscope device tree bindings
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : should be "invensense,mpu3050"
|
|
||||||
- reg : the I2C address of the sensor
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- interrupts : interrupt mapping for the trigger interrupt from the
|
|
||||||
internal oscillator. The following IRQ modes are supported:
|
|
||||||
IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
|
|
||||||
IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
|
|
||||||
for the desired interrupt type.
|
|
||||||
- vdd-supply : supply regulator for the main power voltage.
|
|
||||||
- vlogic-supply : supply regulator for the signal voltage.
|
|
||||||
- mount-matrix : see iio/mount-matrix.txt
|
|
||||||
|
|
||||||
Optional subnodes:
|
|
||||||
- The MPU-3050 will pass through and forward the I2C signals from the
|
|
||||||
incoming I2C bus, alternatively drive traffic to a slave device (usually
|
|
||||||
an accelerometer) on its own initiative. Therefore is supports a subnode
|
|
||||||
i2c gate node. For details see: i2c/i2c-gate.txt
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
mpu3050@68 {
|
|
||||||
compatible = "invensense,mpu3050";
|
|
||||||
reg = <0x68>;
|
|
||||||
interrupt-parent = <&foo>;
|
|
||||||
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
|
|
||||||
vdd-supply = <&bar>;
|
|
||||||
vlogic-supply = <&baz>;
|
|
||||||
|
|
||||||
/* External I2C interface */
|
|
||||||
i2c-gate {
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
|
|
||||||
fnord@18 {
|
|
||||||
compatible = "fnord";
|
|
||||||
reg = <0x18>;
|
|
||||||
interrupt-parent = <&foo>;
|
|
||||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@@ -0,0 +1,70 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/iio/gyroscope/invensense,mpu3050.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Invensense MPU-3050 Gyroscope
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Linus Walleij <linus.walleij@linaro.org>
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: invensense,mpu3050
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
vdd-supply: true
|
||||||
|
|
||||||
|
vlogic-supply: true
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
minItems: 1
|
||||||
|
description:
|
||||||
|
Interrupt mapping for the trigger interrupt from the internal oscillator.
|
||||||
|
|
||||||
|
mount-matrix: true
|
||||||
|
|
||||||
|
i2c-gate:
|
||||||
|
$ref: /schemas/i2c/i2c-controller.yaml
|
||||||
|
unevaluatedProperties: false
|
||||||
|
description: |
|
||||||
|
The MPU-3050 will pass through and forward the I2C signals from the
|
||||||
|
incoming I2C bus, alternatively drive traffic to a slave device (usually
|
||||||
|
an accelerometer) on its own initiative. Therefore is supports an
|
||||||
|
i2c-gate subnode.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
i2c {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
gyroscope@68 {
|
||||||
|
compatible = "invensense,mpu3050";
|
||||||
|
reg = <0x68>;
|
||||||
|
interrupt-parent = <&foo>;
|
||||||
|
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
|
||||||
|
vdd-supply = <&bar>;
|
||||||
|
vlogic-supply = <&baz>;
|
||||||
|
|
||||||
|
i2c-gate {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
magnetometer@c {
|
||||||
|
compatible = "ak,ak8975";
|
||||||
|
reg = <0x0c>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
...
|
@@ -1,67 +0,0 @@
|
|||||||
InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
|
|
||||||
|
|
||||||
http://www.invensense.com/mems/gyro/mpu6050.html
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : should be one of
|
|
||||||
"invensense,mpu6000"
|
|
||||||
"invensense,mpu6050"
|
|
||||||
"invensense,mpu6500"
|
|
||||||
"invensense,mpu6515"
|
|
||||||
"invensense,mpu9150"
|
|
||||||
"invensense,mpu9250"
|
|
||||||
"invensense,mpu9255"
|
|
||||||
"invensense,icm20608"
|
|
||||||
"invensense,icm20609"
|
|
||||||
"invensense,icm20689"
|
|
||||||
"invensense,icm20602"
|
|
||||||
"invensense,icm20690"
|
|
||||||
"invensense,iam20680"
|
|
||||||
- reg : the I2C address of the sensor
|
|
||||||
- interrupts: interrupt mapping for IRQ. It should be configured with flags
|
|
||||||
IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
|
||||||
IRQ_TYPE_EDGE_FALLING.
|
|
||||||
|
|
||||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client node
|
|
||||||
bindings.
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- vdd-supply: regulator phandle for VDD supply
|
|
||||||
- vddio-supply: regulator phandle for VDDIO supply
|
|
||||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
|
||||||
- i2c-gate node. These devices also support an auxiliary i2c bus. This is
|
|
||||||
simple enough to be described using the i2c-gate binding. See
|
|
||||||
i2c/i2c-gate.txt for more details.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
mpu6050@68 {
|
|
||||||
compatible = "invensense,mpu6050";
|
|
||||||
reg = <0x68>;
|
|
||||||
interrupt-parent = <&gpio1>;
|
|
||||||
interrupts = <18 IRQ_TYPE_EDGE_RISING>;
|
|
||||||
mount-matrix = "-0.984807753012208", /* x0 */
|
|
||||||
"0", /* y0 */
|
|
||||||
"-0.173648177666930", /* z0 */
|
|
||||||
"0", /* x1 */
|
|
||||||
"-1", /* y1 */
|
|
||||||
"0", /* z1 */
|
|
||||||
"-0.173648177666930", /* x2 */
|
|
||||||
"0", /* y2 */
|
|
||||||
"0.984807753012208"; /* z2 */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
mpu9250@68 {
|
|
||||||
compatible = "invensense,mpu9250";
|
|
||||||
reg = <0x68>;
|
|
||||||
interrupt-parent = <&gpio3>;
|
|
||||||
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
i2c-gate {
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
ax8975@c {
|
|
||||||
compatible = "ak,ak8975";
|
|
||||||
reg = <0x0c>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
@@ -0,0 +1,104 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/iio/imu/invensense,mpu6050.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
These devices support both I2C and SPI bus interfaces.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- invensense,iam20680
|
||||||
|
- invensense,icm20608
|
||||||
|
- invensense,icm20609
|
||||||
|
- invensense,icm20689
|
||||||
|
- invensense,icm20602
|
||||||
|
- invensense,icm20690
|
||||||
|
- invensense,mpu6000
|
||||||
|
- invensense,mpu6050
|
||||||
|
- invensense,mpu6500
|
||||||
|
- invensense,mpu6515
|
||||||
|
- invensense,mpu6880
|
||||||
|
- invensense,mpu9150
|
||||||
|
- invensense,mpu9250
|
||||||
|
- invensense,mpu9255
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
spi-max-frequency: true
|
||||||
|
|
||||||
|
vdd-supply: true
|
||||||
|
vddio-supply: true
|
||||||
|
|
||||||
|
mount-matrix: true
|
||||||
|
|
||||||
|
i2c-gate:
|
||||||
|
$ref: /schemas/i2c/i2c-controller.yaml
|
||||||
|
unevaluatedProperties: false
|
||||||
|
description: |
|
||||||
|
These devices also support an auxiliary i2c bus via an i2c-gate.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- if:
|
||||||
|
not:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
enum:
|
||||||
|
- invensense,mpu9150
|
||||||
|
- invensense,mpu9250
|
||||||
|
- invensense,mpu9255
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
i2c-gate: false
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
i2c {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
imu@68 {
|
||||||
|
compatible = "invensense,mpu9250";
|
||||||
|
reg = <0x68>;
|
||||||
|
interrupt-parent = <&gpio3>;
|
||||||
|
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
mount-matrix = "-0.984807753012208", /* x0 */
|
||||||
|
"0", /* y0 */
|
||||||
|
"-0.173648177666930", /* z0 */
|
||||||
|
"0", /* x1 */
|
||||||
|
"-1", /* y1 */
|
||||||
|
"0", /* z1 */
|
||||||
|
"-0.173648177666930", /* x2 */
|
||||||
|
"0", /* y2 */
|
||||||
|
"0.984807753012208"; /* z2 */
|
||||||
|
i2c-gate {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
magnetometer@c {
|
||||||
|
compatible = "ak,ak8975";
|
||||||
|
reg = <0x0c>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
...
|
@@ -30,6 +30,9 @@ properties:
|
|||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
vdd-supply: true
|
||||||
|
vddio-supply: true
|
||||||
|
|
||||||
interrupts:
|
interrupts:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
@@ -0,0 +1,112 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/iio/magnetometer/yamaha,yas530.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Yamaha YAS530 family of magnetometer sensors
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Linus Walleij <linus.walleij@linaro.org>
|
||||||
|
|
||||||
|
description:
|
||||||
|
The Yamaha YAS530 magnetometers is a line of 3-axis magnetometers
|
||||||
|
first introduced by Yamaha in 2009 with the YAS530. They are successors
|
||||||
|
of Yamaha's first magnetometer YAS529. Over the years this magnetometer
|
||||||
|
has been miniaturized and appeared in a number of different variants.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
$nodename:
|
||||||
|
pattern: '^magnetometer@[0-9a-f]+$'
|
||||||
|
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
- enum:
|
||||||
|
- yamaha,yas530
|
||||||
|
- yamaha,yas532
|
||||||
|
- yamaha,yas533
|
||||||
|
- yamaha,yas535
|
||||||
|
- yamaha,yas536
|
||||||
|
- yamaha,yas537
|
||||||
|
- yamaha,yas539
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
reset-gpios:
|
||||||
|
maxItems: 1
|
||||||
|
description: The YAS530 sensor has a RSTN pin used to reset
|
||||||
|
the logic inside the sensor. This GPIO line should connect
|
||||||
|
to that pin and be marked as GPIO_ACTIVE_LOW.
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
description: Interrupt for INT pin for interrupt generation.
|
||||||
|
The polarity, whether the interrupt is active on the rising
|
||||||
|
or the falling edge, is software-configurable in the hardware.
|
||||||
|
|
||||||
|
vdd-supply:
|
||||||
|
description: An optional regulator providing core power supply
|
||||||
|
on the VDD pin, typically 1.8 V or 3.0 V.
|
||||||
|
|
||||||
|
iovdd-supply:
|
||||||
|
description: An optional regulator providing I/O power supply
|
||||||
|
for the I2C interface on the IOVDD pin, typically 1.8 V.
|
||||||
|
|
||||||
|
mount-matrix:
|
||||||
|
description: An optional 3x3 mounting rotation matrix.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- if:
|
||||||
|
not:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
const: yamaha,yas530
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
reset-gpios: false
|
||||||
|
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
const: yamaha,yas539
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
interrupts: false
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
i2c-0 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
magnetometer@2e {
|
||||||
|
compatible = "yamaha,yas530";
|
||||||
|
reg = <0x2e>;
|
||||||
|
vdd-supply = <&ldo1_reg>;
|
||||||
|
iovdd-supply = <&ldo2_reg>;
|
||||||
|
reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>;
|
||||||
|
interrupts = <&gpio6 13 IRQ_TYPE_EDGE_RISING>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c-1 {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
magnetometer@2e {
|
||||||
|
compatible = "yamaha,yas539";
|
||||||
|
reg = <0x2e>;
|
||||||
|
vdd-supply = <&ldo1_reg>;
|
||||||
|
};
|
||||||
|
};
|
@@ -148,15 +148,13 @@ properties:
|
|||||||
- maxim,max31730
|
- maxim,max31730
|
||||||
# mCube 3-axis 8-bit digital accelerometer
|
# mCube 3-axis 8-bit digital accelerometer
|
||||||
- mcube,mc3230
|
- mcube,mc3230
|
||||||
# MEMSIC magnetometer
|
|
||||||
- memsic,mmc35240
|
|
||||||
# MEMSIC 2-axis 8-bit digital accelerometer
|
|
||||||
- memsic,mxc6225
|
|
||||||
# Measurement Specialities I2C temperature and humidity sensor
|
# Measurement Specialities I2C temperature and humidity sensor
|
||||||
- meas,htu21
|
- meas,htu21
|
||||||
# Measurement Specialities I2C pressure and temperature sensor
|
# Measurement Specialities I2C pressure and temperature sensor
|
||||||
- meas,ms5637
|
- meas,ms5637
|
||||||
# Measurement Specialities I2C pressure and temperature sensor
|
# Measurement Specialities I2C pressure and temperature sensor
|
||||||
|
- meas,ms5803
|
||||||
|
# Measurement Specialities I2C pressure and temperature sensor
|
||||||
- meas,ms5805
|
- meas,ms5805
|
||||||
# Measurement Specialities I2C pressure and temperature sensor
|
# Measurement Specialities I2C pressure and temperature sensor
|
||||||
- meas,ms5837
|
- meas,ms5837
|
||||||
@@ -166,6 +164,10 @@ properties:
|
|||||||
- meas,ms8607-temppressure
|
- meas,ms8607-temppressure
|
||||||
# Measurement Specialties temperature sensor
|
# Measurement Specialties temperature sensor
|
||||||
- meas,tsys01
|
- meas,tsys01
|
||||||
|
# MEMSIC magnetometer
|
||||||
|
- memsic,mmc35240
|
||||||
|
# MEMSIC 2-axis 8-bit digital accelerometer
|
||||||
|
- memsic,mxc6225
|
||||||
# Microchip differential I2C ADC, 1 Channel, 18 bit
|
# Microchip differential I2C ADC, 1 Channel, 18 bit
|
||||||
- microchip,mcp3421
|
- microchip,mcp3421
|
||||||
# Microchip differential I2C ADC, 2 Channel, 18 bit
|
# Microchip differential I2C ADC, 2 Channel, 18 bit
|
||||||
|
@@ -1260,6 +1260,8 @@ patternProperties:
|
|||||||
description: Shenzhen Xunlong Software CO.,Limited
|
description: Shenzhen Xunlong Software CO.,Limited
|
||||||
"^xylon,.*":
|
"^xylon,.*":
|
||||||
description: Xylon
|
description: Xylon
|
||||||
|
"^yamaha,.*":
|
||||||
|
description: Yamaha Corporation
|
||||||
"^yes-optoelectronics,.*":
|
"^yes-optoelectronics,.*":
|
||||||
description: Yes Optoelectronics Co.,Ltd.
|
description: Yes Optoelectronics Co.,Ltd.
|
||||||
"^ylm,.*":
|
"^ylm,.*":
|
||||||
|
@@ -15,6 +15,7 @@ Contents:
|
|||||||
ethernet/index
|
ethernet/index
|
||||||
fddi/index
|
fddi/index
|
||||||
hamradio/index
|
hamradio/index
|
||||||
|
qlogic/index
|
||||||
wan/index
|
wan/index
|
||||||
wifi/index
|
wifi/index
|
||||||
|
|
||||||
|
18
Documentation/networking/device_drivers/qlogic/index.rst
Normal file
18
Documentation/networking/device_drivers/qlogic/index.rst
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
|
||||||
|
QLogic QLGE Device Drivers
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
qlge
|
||||||
|
|
||||||
|
.. only:: subproject and html
|
||||||
|
|
||||||
|
Indices
|
||||||
|
=======
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
118
Documentation/networking/device_drivers/qlogic/qlge.rst
Normal file
118
Documentation/networking/device_drivers/qlogic/qlge.rst
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
QLogic QLGE 10Gb Ethernet device driver
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
This driver use drgn and devlink for debugging.
|
||||||
|
|
||||||
|
Dump kernel data structures in drgn
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
To dump kernel data structures, the following Python script can be used
|
||||||
|
in drgn:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def align(x, a):
|
||||||
|
"""the alignment a should be a power of 2
|
||||||
|
"""
|
||||||
|
mask = a - 1
|
||||||
|
return (x+ mask) & ~mask
|
||||||
|
|
||||||
|
def struct_size(struct_type):
|
||||||
|
struct_str = "struct {}".format(struct_type)
|
||||||
|
return sizeof(Object(prog, struct_str, address=0x0))
|
||||||
|
|
||||||
|
def netdev_priv(netdevice):
|
||||||
|
NETDEV_ALIGN = 32
|
||||||
|
return netdevice.value_() + align(struct_size("net_device"), NETDEV_ALIGN)
|
||||||
|
|
||||||
|
name = 'xxx'
|
||||||
|
qlge_device = None
|
||||||
|
netdevices = prog['init_net'].dev_base_head.address_of_()
|
||||||
|
for netdevice in list_for_each_entry("struct net_device", netdevices, "dev_list"):
|
||||||
|
if netdevice.name.string_().decode('ascii') == name:
|
||||||
|
print(netdevice.name)
|
||||||
|
|
||||||
|
ql_adapter = Object(prog, "struct ql_adapter", address=netdev_priv(qlge_device))
|
||||||
|
|
||||||
|
The struct ql_adapter will be printed in drgn as follows,
|
||||||
|
|
||||||
|
>>> ql_adapter
|
||||||
|
(struct ql_adapter){
|
||||||
|
.ricb = (struct ricb){
|
||||||
|
.base_cq = (u8)0,
|
||||||
|
.flags = (u8)120,
|
||||||
|
.mask = (__le16)26637,
|
||||||
|
.hash_cq_id = (u8 [1024]){ 172, 142, 255, 255 },
|
||||||
|
.ipv6_hash_key = (__le32 [10]){},
|
||||||
|
.ipv4_hash_key = (__le32 [4]){},
|
||||||
|
},
|
||||||
|
.flags = (unsigned long)0,
|
||||||
|
.wol = (u32)0,
|
||||||
|
.nic_stats = (struct nic_stats){
|
||||||
|
.tx_pkts = (u64)0,
|
||||||
|
.tx_bytes = (u64)0,
|
||||||
|
.tx_mcast_pkts = (u64)0,
|
||||||
|
.tx_bcast_pkts = (u64)0,
|
||||||
|
.tx_ucast_pkts = (u64)0,
|
||||||
|
.tx_ctl_pkts = (u64)0,
|
||||||
|
.tx_pause_pkts = (u64)0,
|
||||||
|
...
|
||||||
|
},
|
||||||
|
.active_vlans = (unsigned long [64]){
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52780853100545, 18446744073709551615,
|
||||||
|
18446619461681283072, 0, 42949673024, 2147483647,
|
||||||
|
},
|
||||||
|
.rx_ring = (struct rx_ring [17]){
|
||||||
|
{
|
||||||
|
.cqicb = (struct cqicb){
|
||||||
|
.msix_vect = (u8)0,
|
||||||
|
.reserved1 = (u8)0,
|
||||||
|
.reserved2 = (u8)0,
|
||||||
|
.flags = (u8)0,
|
||||||
|
.len = (__le16)0,
|
||||||
|
.rid = (__le16)0,
|
||||||
|
...
|
||||||
|
},
|
||||||
|
.cq_base = (void *)0x0,
|
||||||
|
.cq_base_dma = (dma_addr_t)0,
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coredump via devlink
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
|
||||||
|
And the coredump obtained via devlink in json format looks like,
|
||||||
|
|
||||||
|
.. code:: shell
|
||||||
|
|
||||||
|
$ devlink health dump show DEVICE reporter coredump -p -j
|
||||||
|
{
|
||||||
|
"Core Registers": {
|
||||||
|
"segment": 1,
|
||||||
|
"values": [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
|
||||||
|
},
|
||||||
|
"Test Logic Regs": {
|
||||||
|
"segment": 2,
|
||||||
|
"values": [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
|
||||||
|
},
|
||||||
|
"RMII Registers": {
|
||||||
|
"segment": 3,
|
||||||
|
"values": [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
"Sem Registers": {
|
||||||
|
"segment": 50,
|
||||||
|
"values": [ 0,0,0,0 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
When the module parameter qlge_force_coredump is set to be true, the MPI
|
||||||
|
RISC reset before coredumping. So coredumping will much longer since
|
||||||
|
devlink tool has to wait for 5 secs for the resetting to be
|
||||||
|
finished.
|
@@ -14590,6 +14590,12 @@ L: netdev@vger.kernel.org
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/staging/qlge/
|
F: drivers/staging/qlge/
|
||||||
|
|
||||||
|
QLOGIC QLGE 10Gb ETHERNET DRIVER
|
||||||
|
M: Coiby Xu <coiby.xu@gmail.com>
|
||||||
|
L: netdev@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/networking/device_drivers/qlogic/qlge.rst
|
||||||
|
|
||||||
QM1D1B0004 MEDIA DRIVER
|
QM1D1B0004 MEDIA DRIVER
|
||||||
M: Akihiro Tsukada <tskd08@gmail.com>
|
M: Akihiro Tsukada <tskd08@gmail.com>
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
* Copyright (c) 2015, Intel Corporation.
|
* Copyright (c) 2015, Intel Corporation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/ctype.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
|
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
|
||||||
#define HID_CUSTOM_FIFO_SIZE 4096
|
#define HID_CUSTOM_FIFO_SIZE 4096
|
||||||
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
|
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
|
||||||
|
#define HID_SENSOR_USAGE_LENGTH (4 + 1)
|
||||||
|
|
||||||
struct hid_sensor_custom_field {
|
struct hid_sensor_custom_field {
|
||||||
int report_id;
|
int report_id;
|
||||||
@@ -50,6 +52,7 @@ struct hid_sensor_custom {
|
|||||||
struct kfifo data_fifo;
|
struct kfifo data_fifo;
|
||||||
unsigned long misc_opened;
|
unsigned long misc_opened;
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
|
struct platform_device *custom_pdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Header for each sample to user space via dev interface */
|
/* Header for each sample to user space via dev interface */
|
||||||
@@ -746,11 +749,130 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* luid defined in FW (e.g. ISH). Maybe used to identify sensor. */
|
||||||
|
static const char *const known_sensor_luid[] = { "020B000000000000" };
|
||||||
|
|
||||||
|
static int get_luid_table_index(unsigned char *usage_str)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) {
|
||||||
|
if (!strncmp(usage_str, known_sensor_luid[i],
|
||||||
|
strlen(known_sensor_luid[i])))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_known_custom_sensor_index(struct hid_sensor_hub_device *hsdev)
|
||||||
|
{
|
||||||
|
struct hid_sensor_hub_attribute_info sensor_manufacturer = { 0 };
|
||||||
|
struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 };
|
||||||
|
int report_size;
|
||||||
|
int ret;
|
||||||
|
static u16 w_buf[HID_CUSTOM_MAX_FEATURE_BYTES];
|
||||||
|
static char buf[HID_CUSTOM_MAX_FEATURE_BYTES];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(w_buf, 0, sizeof(w_buf));
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
/* get manufacturer info */
|
||||||
|
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||||
|
HID_FEATURE_REPORT, hsdev->usage,
|
||||||
|
HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
report_size =
|
||||||
|
sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id,
|
||||||
|
sensor_manufacturer.index, sizeof(w_buf),
|
||||||
|
w_buf);
|
||||||
|
if (report_size <= 0) {
|
||||||
|
hid_err(hsdev->hdev,
|
||||||
|
"Failed to get sensor manufacturer info %d\n",
|
||||||
|
report_size);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert from wide char to char */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
|
||||||
|
buf[i] = (char)w_buf[i];
|
||||||
|
|
||||||
|
/* ensure it's ISH sensor */
|
||||||
|
if (strncmp(buf, "INTEL", strlen("INTEL")))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
memset(w_buf, 0, sizeof(w_buf));
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
||||||
|
/* get real usage id */
|
||||||
|
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||||
|
HID_FEATURE_REPORT, hsdev->usage,
|
||||||
|
HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id,
|
||||||
|
sensor_luid_info.index, sizeof(w_buf),
|
||||||
|
w_buf);
|
||||||
|
if (report_size <= 0) {
|
||||||
|
hid_err(hsdev->hdev, "Failed to get real usage info %d\n",
|
||||||
|
report_size);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert from wide char to char */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
|
||||||
|
buf[i] = (char)w_buf[i];
|
||||||
|
|
||||||
|
if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) {
|
||||||
|
hid_err(hsdev->hdev,
|
||||||
|
"%s luid length not match %zu != (%zu + 5)\n", __func__,
|
||||||
|
strlen(buf), strlen(known_sensor_luid[0]));
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get table index with luid (not matching 'LUID: ' in luid) */
|
||||||
|
return get_luid_table_index(&buf[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_device *
|
||||||
|
hid_sensor_register_platform_device(struct platform_device *pdev,
|
||||||
|
struct hid_sensor_hub_device *hsdev,
|
||||||
|
int index)
|
||||||
|
{
|
||||||
|
char real_usage[HID_SENSOR_USAGE_LENGTH] = { 0 };
|
||||||
|
struct platform_device *custom_pdev;
|
||||||
|
const char *dev_name;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
/* copy real usage id */
|
||||||
|
memcpy(real_usage, known_sensor_luid[index], 4);
|
||||||
|
|
||||||
|
/* usage id are all lowcase */
|
||||||
|
for (c = real_usage; *c != '\0'; c++)
|
||||||
|
*c = tolower(*c);
|
||||||
|
|
||||||
|
/* HID-SENSOR-INT-REAL_USAGE_ID */
|
||||||
|
dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage);
|
||||||
|
if (!dev_name)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
custom_pdev = platform_device_register_data(pdev->dev.parent, dev_name,
|
||||||
|
PLATFORM_DEVID_NONE, hsdev,
|
||||||
|
sizeof(*hsdev));
|
||||||
|
kfree(dev_name);
|
||||||
|
return custom_pdev;
|
||||||
|
}
|
||||||
|
|
||||||
static int hid_sensor_custom_probe(struct platform_device *pdev)
|
static int hid_sensor_custom_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct hid_sensor_custom *sensor_inst;
|
struct hid_sensor_custom *sensor_inst;
|
||||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||||
int ret;
|
int ret;
|
||||||
|
int index;
|
||||||
|
|
||||||
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
|
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@@ -764,6 +886,22 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
|
|||||||
sensor_inst->pdev = pdev;
|
sensor_inst->pdev = pdev;
|
||||||
mutex_init(&sensor_inst->mutex);
|
mutex_init(&sensor_inst->mutex);
|
||||||
platform_set_drvdata(pdev, sensor_inst);
|
platform_set_drvdata(pdev, sensor_inst);
|
||||||
|
|
||||||
|
index = get_known_custom_sensor_index(hsdev);
|
||||||
|
if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) {
|
||||||
|
sensor_inst->custom_pdev =
|
||||||
|
hid_sensor_register_platform_device(pdev, hsdev, index);
|
||||||
|
|
||||||
|
ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"register_platform_device failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
|
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
|
||||||
&sensor_inst->callbacks);
|
&sensor_inst->callbacks);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -802,6 +940,11 @@ static int hid_sensor_custom_remove(struct platform_device *pdev)
|
|||||||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||||
|
|
||||||
|
if (sensor_inst->custom_pdev) {
|
||||||
|
platform_device_unregister(sensor_inst->custom_pdev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
hid_sensor_custom_dev_if_remove(sensor_inst);
|
hid_sensor_custom_dev_if_remove(sensor_inst);
|
||||||
hid_sensor_custom_remove_attributes(sensor_inst);
|
hid_sensor_custom_remove_attributes(sensor_inst);
|
||||||
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
||||||
|
@@ -23,6 +23,7 @@ enum accel_3d_channel {
|
|||||||
ACCEL_3D_CHANNEL_MAX,
|
ACCEL_3D_CHANNEL_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX
|
||||||
struct accel_3d_state {
|
struct accel_3d_state {
|
||||||
struct hid_sensor_hub_callbacks callbacks;
|
struct hid_sensor_hub_callbacks callbacks;
|
||||||
struct hid_sensor_common common_attributes;
|
struct hid_sensor_common common_attributes;
|
||||||
@@ -75,7 +76,7 @@ static const struct iio_chan_spec accel_3d_channels[] = {
|
|||||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||||
},
|
},
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Channel definitions */
|
/* Channel definitions */
|
||||||
@@ -110,7 +111,8 @@ static const struct iio_chan_spec gravity_channels[] = {
|
|||||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||||
}
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Adjust channel real bits based on report descriptor */
|
/* Adjust channel real bits based on report descriptor */
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
#include <linux/iio/buffer.h>
|
#include <linux/iio/buffer.h>
|
||||||
@@ -133,6 +134,7 @@ enum kx_acpi_type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct kxcjk1013_data {
|
struct kxcjk1013_data {
|
||||||
|
struct regulator_bulk_data regulators[2];
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct iio_trigger *dready_trig;
|
struct iio_trigger *dready_trig;
|
||||||
struct iio_trigger *motion_trig;
|
struct iio_trigger *motion_trig;
|
||||||
@@ -1300,6 +1302,13 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
|
|||||||
return dev_name(dev);
|
return dev_name(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kxcjk1013_disable_regulators(void *d)
|
||||||
|
{
|
||||||
|
struct kxcjk1013_data *data = d;
|
||||||
|
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||||
|
}
|
||||||
|
|
||||||
static int kxcjk1013_probe(struct i2c_client *client,
|
static int kxcjk1013_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
@@ -1330,6 +1339,29 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data->regulators[0].supply = "vdd";
|
||||||
|
data->regulators[1].supply = "vddio";
|
||||||
|
ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(data->regulators),
|
||||||
|
data->regulators);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(&client->dev, ret, "Failed to get regulators\n");
|
||||||
|
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||||
|
data->regulators);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(&client->dev, kxcjk1013_disable_regulators, data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A typical delay of 10ms is required for powering up
|
||||||
|
* according to the data sheets of supported chips.
|
||||||
|
* Hence double that to play safe.
|
||||||
|
*/
|
||||||
|
msleep(20);
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
data->chipset = (enum kx_chipset)(id->driver_data);
|
data->chipset = (enum kx_chipset)(id->driver_data);
|
||||||
name = id->name;
|
name = id->name;
|
||||||
|
@@ -1228,8 +1228,15 @@ config XILINX_XADC
|
|||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
help
|
help
|
||||||
Say yes here to have support for the Xilinx XADC. The driver does support
|
Say yes here to have support for the Xilinx 7 Series XADC or
|
||||||
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
|
UltraScale/UltraScale+ System Management Wizard.
|
||||||
|
|
||||||
|
For the 7 Series the driver does support both the ZYNQ interface
|
||||||
|
to the XADC as well as the AXI-XADC interface.
|
||||||
|
|
||||||
|
The driver also support the Xilinx System Management Wizard IP core
|
||||||
|
that can be used to access the System Monitor ADC on the Xilinx
|
||||||
|
UltraScale and UltraScale+ FPGAs.
|
||||||
|
|
||||||
The driver can also be build as a module. If so, the module will be called
|
The driver can also be build as a module. If so, the module will be called
|
||||||
xilinx-xadc.
|
xilinx-xadc.
|
||||||
|
@@ -1108,10 +1108,14 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||||||
return gpadc->irq_sw;
|
return gpadc->irq_sw;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
if (is_ab8500(gpadc->ab8500)) {
|
||||||
if (gpadc->irq_hw < 0) {
|
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||||
dev_err(dev, "failed to get platform hw_conv_end irq\n");
|
if (gpadc->irq_hw < 0) {
|
||||||
return gpadc->irq_hw;
|
dev_err(dev, "failed to get platform hw_conv_end irq\n");
|
||||||
|
return gpadc->irq_hw;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gpadc->irq_hw = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize completion used to notify completion of conversion */
|
/* Initialize completion used to notify completion of conversion */
|
||||||
@@ -1128,14 +1132,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
|
if (gpadc->irq_hw) {
|
||||||
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
|
||||||
"ab8500-gpadc-hw", gpadc);
|
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||||
if (ret < 0) {
|
"ab8500-gpadc-hw", gpadc);
|
||||||
dev_err(dev,
|
if (ret < 0) {
|
||||||
"Failed to request hw conversion irq: %d\n",
|
dev_err(dev,
|
||||||
gpadc->irq_hw);
|
"Failed to request hw conversion irq: %d\n",
|
||||||
return ret;
|
gpadc->irq_hw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The VTVout LDO used to power the AB8500 GPADC */
|
/* The VTVout LDO used to power the AB8500 GPADC */
|
||||||
|
@@ -67,6 +67,7 @@ enum ad7476_supported_device_ids {
|
|||||||
ID_ADS7866,
|
ID_ADS7866,
|
||||||
ID_ADS7867,
|
ID_ADS7867,
|
||||||
ID_ADS7868,
|
ID_ADS7868,
|
||||||
|
ID_LTC2314_14,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ad7091_convst(struct ad7476_state *st)
|
static void ad7091_convst(struct ad7476_state *st)
|
||||||
@@ -250,6 +251,10 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
|||||||
.channel[0] = ADS786X_CHAN(8),
|
.channel[0] = ADS786X_CHAN(8),
|
||||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||||
},
|
},
|
||||||
|
[ID_LTC2314_14] = {
|
||||||
|
.channel[0] = AD7940_CHAN(14),
|
||||||
|
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct iio_info ad7476_info = {
|
static const struct iio_info ad7476_info = {
|
||||||
@@ -365,6 +370,7 @@ static const struct spi_device_id ad7476_id[] = {
|
|||||||
{"ads7866", ID_ADS7866},
|
{"ads7866", ID_ADS7866},
|
||||||
{"ads7867", ID_ADS7867},
|
{"ads7867", ID_ADS7867},
|
||||||
{"ads7868", ID_ADS7868},
|
{"ads7868", ID_ADS7868},
|
||||||
|
{"ltc2314-14", ID_LTC2314_14},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@@ -21,8 +22,6 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#include "qcom-vadc-common.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Definitions for the "user processor" registers lifted from the v3.4
|
* Definitions for the "user processor" registers lifted from the v3.4
|
||||||
* Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:
|
* Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@@ -14,12 +15,12 @@
|
|||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||||
#include "qcom-vadc-common.h"
|
|
||||||
|
|
||||||
#define ADC5_USR_REVISION1 0x0
|
#define ADC5_USR_REVISION1 0x0
|
||||||
#define ADC5_USR_STATUS1 0x8
|
#define ADC5_USR_STATUS1 0x8
|
||||||
@@ -154,18 +155,6 @@ struct adc5_chip {
|
|||||||
const struct adc5_data *data;
|
const struct adc5_data *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
|
|
||||||
{.num = 1, .den = 1},
|
|
||||||
{.num = 1, .den = 3},
|
|
||||||
{.num = 1, .den = 4},
|
|
||||||
{.num = 1, .den = 6},
|
|
||||||
{.num = 1, .den = 20},
|
|
||||||
{.num = 1, .den = 8},
|
|
||||||
{.num = 10, .den = 81},
|
|
||||||
{.num = 1, .den = 10},
|
|
||||||
{.num = 1, .den = 16}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||||
{
|
{
|
||||||
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
|
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
|
||||||
@@ -181,55 +170,6 @@ static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
|
|||||||
return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
|
return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adc5_prescaling_from_dt(u32 num, u32 den)
|
|
||||||
{
|
|
||||||
unsigned int pre;
|
|
||||||
|
|
||||||
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
|
|
||||||
if (adc5_prescale_ratios[pre].num == num &&
|
|
||||||
adc5_prescale_ratios[pre].den == den)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int adc5_hw_settle_time_from_dt(u32 value,
|
|
||||||
const unsigned int *hw_settle)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
|
|
||||||
if (value == hw_settle[i])
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int adc5_avg_samples_from_dt(u32 value)
|
|
||||||
{
|
|
||||||
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return __ffs(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int adc5_decimation_from_dt(u32 value,
|
|
||||||
const unsigned int *decimation)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
|
|
||||||
if (value == decimation[i])
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -511,7 +451,7 @@ static int adc_read_raw_common(struct iio_dev *indio_dev,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
|
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
|
||||||
&adc5_prescale_ratios[prop->prescale],
|
prop->prescale,
|
||||||
adc->data,
|
adc->data,
|
||||||
adc_code_volt, val);
|
adc_code_volt, val);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -717,7 +657,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||||||
|
|
||||||
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = adc5_decimation_from_dt(value, data->decimation);
|
ret = qcom_adc5_decimation_from_dt(value, data->decimation);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%02x invalid decimation %d\n",
|
dev_err(dev, "%02x invalid decimation %d\n",
|
||||||
chan, value);
|
chan, value);
|
||||||
@@ -730,7 +670,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||||||
|
|
||||||
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
|
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = adc5_prescaling_from_dt(varr[0], varr[1]);
|
ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
|
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
|
||||||
chan, varr[0], varr[1]);
|
chan, varr[0], varr[1]);
|
||||||
@@ -759,11 +699,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||||||
if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
|
if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
|
||||||
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
|
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
|
||||||
adc->data->info == &adc7_info)
|
adc->data->info == &adc7_info)
|
||||||
ret = adc5_hw_settle_time_from_dt(value,
|
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2);
|
||||||
data->hw_settle_2);
|
|
||||||
else
|
else
|
||||||
ret = adc5_hw_settle_time_from_dt(value,
|
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
|
||||||
data->hw_settle_1);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%02x invalid hw-settle-time %d us\n",
|
dev_err(dev, "%02x invalid hw-settle-time %d us\n",
|
||||||
@@ -777,7 +715,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||||||
|
|
||||||
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
|
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = adc5_avg_samples_from_dt(value);
|
ret = qcom_adc5_avg_samples_from_dt(value);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%02x invalid avg-samples %d\n",
|
dev_err(dev, "%02x invalid avg-samples %d\n",
|
||||||
chan, value);
|
chan, value);
|
||||||
@@ -870,8 +808,6 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
|||||||
struct adc5_channel_prop prop, *chan_props;
|
struct adc5_channel_prop prop, *chan_props;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
unsigned int index = 0;
|
unsigned int index = 0;
|
||||||
const struct of_device_id *id;
|
|
||||||
const struct adc5_data *data;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
adc->nchannels = of_get_available_child_count(node);
|
adc->nchannels = of_get_available_child_count(node);
|
||||||
@@ -890,24 +826,21 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
|||||||
|
|
||||||
chan_props = adc->chan_props;
|
chan_props = adc->chan_props;
|
||||||
iio_chan = adc->iio_chans;
|
iio_chan = adc->iio_chans;
|
||||||
id = of_match_node(adc5_match_table, node);
|
adc->data = of_device_get_match_data(adc->dev);
|
||||||
if (id)
|
if (!adc->data)
|
||||||
data = id->data;
|
adc->data = &adc5_data_pmic;
|
||||||
else
|
|
||||||
data = &adc5_data_pmic;
|
|
||||||
adc->data = data;
|
|
||||||
|
|
||||||
for_each_available_child_of_node(node, child) {
|
for_each_available_child_of_node(node, child) {
|
||||||
ret = adc5_get_dt_channel_data(adc, &prop, child, data);
|
ret = adc5_get_dt_channel_data(adc, &prop, child, adc->data);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
of_node_put(child);
|
of_node_put(child);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
prop.scale_fn_type =
|
prop.scale_fn_type =
|
||||||
data->adc_chans[prop.channel].scale_fn_type;
|
adc->data->adc_chans[prop.channel].scale_fn_type;
|
||||||
*chan_props = prop;
|
*chan_props = prop;
|
||||||
adc_chan = &data->adc_chans[prop.channel];
|
adc_chan = &adc->data->adc_chans[prop.channel];
|
||||||
|
|
||||||
iio_chan->channel = prop.channel;
|
iio_chan->channel = prop.channel;
|
||||||
iio_chan->datasheet_name = prop.datasheet_name;
|
iio_chan->datasheet_name = prop.datasheet_name;
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
@@ -20,8 +21,6 @@
|
|||||||
|
|
||||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||||
|
|
||||||
#include "qcom-vadc-common.h"
|
|
||||||
|
|
||||||
/* VADC register and bit definitions */
|
/* VADC register and bit definitions */
|
||||||
#define VADC_REVISION2 0x1
|
#define VADC_REVISION2 0x1
|
||||||
#define VADC_REVISION2_SUPPORTED_VADC 1
|
#define VADC_REVISION2_SUPPORTED_VADC 1
|
||||||
|
@@ -2,50 +2,61 @@
|
|||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/fixp-arith.h>
|
||||||
|
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/units.h>
|
#include <linux/units.h>
|
||||||
|
|
||||||
#include "qcom-vadc-common.h"
|
/**
|
||||||
|
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||||
|
* @x: Represent the ADC digitized code.
|
||||||
|
* @y: Represent the physical data which can be temperature, voltage,
|
||||||
|
* resistance.
|
||||||
|
*/
|
||||||
|
struct vadc_map_pt {
|
||||||
|
s32 x;
|
||||||
|
s32 y;
|
||||||
|
};
|
||||||
|
|
||||||
/* Voltage to temperature */
|
/* Voltage to temperature */
|
||||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||||
{1758, -40},
|
{1758, -40000 },
|
||||||
{1742, -35},
|
{1742, -35000 },
|
||||||
{1719, -30},
|
{1719, -30000 },
|
||||||
{1691, -25},
|
{1691, -25000 },
|
||||||
{1654, -20},
|
{1654, -20000 },
|
||||||
{1608, -15},
|
{1608, -15000 },
|
||||||
{1551, -10},
|
{1551, -10000 },
|
||||||
{1483, -5},
|
{1483, -5000 },
|
||||||
{1404, 0},
|
{1404, 0 },
|
||||||
{1315, 5},
|
{1315, 5000 },
|
||||||
{1218, 10},
|
{1218, 10000 },
|
||||||
{1114, 15},
|
{1114, 15000 },
|
||||||
{1007, 20},
|
{1007, 20000 },
|
||||||
{900, 25},
|
{900, 25000 },
|
||||||
{795, 30},
|
{795, 30000 },
|
||||||
{696, 35},
|
{696, 35000 },
|
||||||
{605, 40},
|
{605, 40000 },
|
||||||
{522, 45},
|
{522, 45000 },
|
||||||
{448, 50},
|
{448, 50000 },
|
||||||
{383, 55},
|
{383, 55000 },
|
||||||
{327, 60},
|
{327, 60000 },
|
||||||
{278, 65},
|
{278, 65000 },
|
||||||
{237, 70},
|
{237, 70000 },
|
||||||
{202, 75},
|
{202, 75000 },
|
||||||
{172, 80},
|
{172, 80000 },
|
||||||
{146, 85},
|
{146, 85000 },
|
||||||
{125, 90},
|
{125, 90000 },
|
||||||
{107, 95},
|
{107, 95000 },
|
||||||
{92, 100},
|
{92, 100000 },
|
||||||
{79, 105},
|
{79, 105000 },
|
||||||
{68, 110},
|
{68, 110000 },
|
||||||
{59, 115},
|
{59, 115000 },
|
||||||
{51, 120},
|
{51, 120000 },
|
||||||
{44, 125}
|
{44, 125000 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -90,18 +101,18 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct vadc_map_pt adcmap7_die_temp[] = {
|
static const struct vadc_map_pt adcmap7_die_temp[] = {
|
||||||
{ 433700, 1967},
|
{ 857300, 160000 },
|
||||||
{ 473100, 1964},
|
{ 820100, 140000 },
|
||||||
{ 512400, 1957},
|
{ 782500, 120000 },
|
||||||
{ 551500, 1949},
|
{ 744600, 100000 },
|
||||||
{ 590500, 1940},
|
{ 706400, 80000 },
|
||||||
{ 629300, 1930},
|
{ 667900, 60000 },
|
||||||
{ 667900, 1921},
|
{ 629300, 40000 },
|
||||||
{ 706400, 1910},
|
{ 590500, 20000 },
|
||||||
{ 744600, 1896},
|
{ 551500, 0 },
|
||||||
{ 782500, 1878},
|
{ 512400, -20000 },
|
||||||
{ 820100, 1859},
|
{ 473100, -40000 },
|
||||||
{ 857300, 0},
|
{ 433700, -60000 },
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -278,6 +289,18 @@ static const struct vadc_map_pt adcmap7_100k[] = {
|
|||||||
{ 2420, 130048 }
|
{ 2420, 130048 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
|
||||||
|
{.num = 1, .den = 1},
|
||||||
|
{.num = 1, .den = 3},
|
||||||
|
{.num = 1, .den = 4},
|
||||||
|
{.num = 1, .den = 6},
|
||||||
|
{.num = 1, .den = 20},
|
||||||
|
{.num = 1, .den = 8},
|
||||||
|
{.num = 10, .den = 81},
|
||||||
|
{.num = 1, .den = 10},
|
||||||
|
{.num = 1, .den = 16}
|
||||||
|
};
|
||||||
|
|
||||||
static int qcom_vadc_scale_hw_calib_volt(
|
static int qcom_vadc_scale_hw_calib_volt(
|
||||||
const struct vadc_prescale_ratio *prescale,
|
const struct vadc_prescale_ratio *prescale,
|
||||||
const struct adc5_data *data,
|
const struct adc5_data *data,
|
||||||
@@ -323,43 +346,23 @@ static struct qcom_adc5_scale_type scale_adc5_fn[] = {
|
|||||||
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||||
u32 tablesize, s32 input, int *output)
|
u32 tablesize, s32 input, int *output)
|
||||||
{
|
{
|
||||||
bool descending = 1;
|
|
||||||
u32 i = 0;
|
u32 i = 0;
|
||||||
|
|
||||||
if (!pts)
|
if (!pts)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Check if table is descending or ascending */
|
while (i < tablesize && pts[i].x > input)
|
||||||
if (tablesize > 1) {
|
|
||||||
if (pts[0].x < pts[1].x)
|
|
||||||
descending = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (i < tablesize) {
|
|
||||||
if ((descending) && (pts[i].x < input)) {
|
|
||||||
/* table entry is less than measured*/
|
|
||||||
/* value and table is descending, stop */
|
|
||||||
break;
|
|
||||||
} else if ((!descending) &&
|
|
||||||
(pts[i].x > input)) {
|
|
||||||
/* table entry is greater than measured*/
|
|
||||||
/*value and table is ascending, stop */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
i++;
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
*output = pts[0].y;
|
*output = pts[0].y;
|
||||||
} else if (i == tablesize) {
|
} else if (i == tablesize) {
|
||||||
*output = pts[tablesize - 1].y;
|
*output = pts[tablesize - 1].y;
|
||||||
} else {
|
} else {
|
||||||
/* result is between search_index and search_index-1 */
|
|
||||||
/* interpolate linearly */
|
/* interpolate linearly */
|
||||||
*output = (((s32)((pts[i].y - pts[i - 1].y) *
|
*output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y,
|
||||||
(input - pts[i - 1].x)) /
|
pts[i].x, pts[i].y,
|
||||||
(pts[i].x - pts[i - 1].x)) +
|
input);
|
||||||
pts[i - 1].y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -415,8 +418,6 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*result_mdec *= 1000;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,33 +564,13 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
|
|||||||
u16 adc_code, int *result_mdec)
|
u16 adc_code, int *result_mdec)
|
||||||
{
|
{
|
||||||
|
|
||||||
int voltage, vtemp0, temp, i;
|
int voltage;
|
||||||
|
|
||||||
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
|
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||||
prescale, data, 1);
|
prescale, data, 1);
|
||||||
|
|
||||||
if (adcmap7_die_temp[0].x > voltage) {
|
return qcom_vadc_map_voltage_temp(adcmap7_die_temp, ARRAY_SIZE(adcmap7_die_temp),
|
||||||
*result_mdec = DIE_TEMP_ADC7_SCALE_1;
|
voltage, result_mdec);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adcmap7_die_temp[ARRAY_SIZE(adcmap7_die_temp) - 1].x <= voltage) {
|
|
||||||
*result_mdec = DIE_TEMP_ADC7_MAX;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
|
|
||||||
if (adcmap7_die_temp[i].x > voltage)
|
|
||||||
break;
|
|
||||||
|
|
||||||
vtemp0 = adcmap7_die_temp[i - 1].x;
|
|
||||||
voltage = voltage - vtemp0;
|
|
||||||
temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
|
|
||||||
adcmap7_die_temp[i - 1].y);
|
|
||||||
temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
|
|
||||||
*result_mdec = temp;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcom_vadc_scale_hw_smb_temp(
|
static int qcom_vadc_scale_hw_smb_temp(
|
||||||
@@ -647,10 +628,12 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
|||||||
EXPORT_SYMBOL(qcom_vadc_scale);
|
EXPORT_SYMBOL(qcom_vadc_scale);
|
||||||
|
|
||||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||||
const struct vadc_prescale_ratio *prescale,
|
unsigned int prescale_ratio,
|
||||||
const struct adc5_data *data,
|
const struct adc5_data *data,
|
||||||
u16 adc_code, int *result)
|
u16 adc_code, int *result)
|
||||||
{
|
{
|
||||||
|
const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
|
||||||
|
|
||||||
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
|
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
|
||||||
scaletype < SCALE_HW_CALIB_INVALID)) {
|
scaletype < SCALE_HW_CALIB_INVALID)) {
|
||||||
pr_err("Invalid scale type %d\n", scaletype);
|
pr_err("Invalid scale type %d\n", scaletype);
|
||||||
@@ -662,6 +645,58 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qcom_adc5_hw_scale);
|
EXPORT_SYMBOL(qcom_adc5_hw_scale);
|
||||||
|
|
||||||
|
int qcom_adc5_prescaling_from_dt(u32 num, u32 den)
|
||||||
|
{
|
||||||
|
unsigned int pre;
|
||||||
|
|
||||||
|
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
|
||||||
|
if (adc5_prescale_ratios[pre].num == num &&
|
||||||
|
adc5_prescale_ratios[pre].den == den)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return pre;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_adc5_prescaling_from_dt);
|
||||||
|
|
||||||
|
int qcom_adc5_hw_settle_time_from_dt(u32 value,
|
||||||
|
const unsigned int *hw_settle)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
|
||||||
|
if (value == hw_settle[i])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_adc5_hw_settle_time_from_dt);
|
||||||
|
|
||||||
|
int qcom_adc5_avg_samples_from_dt(u32 value)
|
||||||
|
{
|
||||||
|
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return __ffs(value);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_adc5_avg_samples_from_dt);
|
||||||
|
|
||||||
|
int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
|
||||||
|
if (value == decimation[i])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_adc5_decimation_from_dt);
|
||||||
|
|
||||||
int qcom_vadc_decimation_from_dt(u32 value)
|
int qcom_vadc_decimation_from_dt(u32 value)
|
||||||
{
|
{
|
||||||
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
||||||
|
@@ -307,7 +307,7 @@ static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel,
|
|||||||
|
|
||||||
sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator);
|
sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator);
|
||||||
|
|
||||||
return (volt * denominator + numerator / 2) / numerator;
|
return DIV_ROUND_CLOSEST(volt * denominator, numerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,
|
static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,
|
||||||
|
@@ -535,20 +535,16 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||||||
goto err_switches_dis;
|
goto err_switches_dis;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->bclk) {
|
ret = clk_prepare_enable(priv->bclk);
|
||||||
ret = clk_prepare_enable(priv->bclk);
|
if (ret < 0) {
|
||||||
if (ret < 0) {
|
dev_err(dev, "bus clk enable failed\n");
|
||||||
dev_err(dev, "bus clk enable failed\n");
|
goto err_regulator_disable;
|
||||||
goto err_regulator_disable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->aclk) {
|
ret = clk_prepare_enable(priv->aclk);
|
||||||
ret = clk_prepare_enable(priv->aclk);
|
if (ret < 0) {
|
||||||
if (ret < 0) {
|
dev_err(dev, "adc clk enable failed\n");
|
||||||
dev_err(dev, "adc clk enable failed\n");
|
goto err_bclk_disable;
|
||||||
goto err_bclk_disable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
|
writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
|
||||||
@@ -556,8 +552,7 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_bclk_disable:
|
err_bclk_disable:
|
||||||
if (priv->bclk)
|
clk_disable_unprepare(priv->bclk);
|
||||||
clk_disable_unprepare(priv->bclk);
|
|
||||||
err_regulator_disable:
|
err_regulator_disable:
|
||||||
regulator_disable(priv->vref);
|
regulator_disable(priv->vref);
|
||||||
err_switches_dis:
|
err_switches_dis:
|
||||||
@@ -575,10 +570,8 @@ static void stm32_adc_core_hw_stop(struct device *dev)
|
|||||||
|
|
||||||
/* Backup CCR that may be lost (depends on power state to achieve) */
|
/* Backup CCR that may be lost (depends on power state to achieve) */
|
||||||
priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
|
priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
|
||||||
if (priv->aclk)
|
clk_disable_unprepare(priv->aclk);
|
||||||
clk_disable_unprepare(priv->aclk);
|
clk_disable_unprepare(priv->bclk);
|
||||||
if (priv->bclk)
|
|
||||||
clk_disable_unprepare(priv->bclk);
|
|
||||||
regulator_disable(priv->vref);
|
regulator_disable(priv->vref);
|
||||||
stm32_adc_core_switches_supply_dis(priv);
|
stm32_adc_core_switches_supply_dis(priv);
|
||||||
regulator_disable(priv->vdda);
|
regulator_disable(priv->vdda);
|
||||||
|
@@ -546,8 +546,7 @@ static int stm32_adc_hw_stop(struct device *dev)
|
|||||||
if (adc->cfg->unprepare)
|
if (adc->cfg->unprepare)
|
||||||
adc->cfg->unprepare(indio_dev);
|
adc->cfg->unprepare(indio_dev);
|
||||||
|
|
||||||
if (adc->clk)
|
clk_disable_unprepare(adc->clk);
|
||||||
clk_disable_unprepare(adc->clk);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -558,11 +557,9 @@ static int stm32_adc_hw_start(struct device *dev)
|
|||||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (adc->clk) {
|
ret = clk_prepare_enable(adc->clk);
|
||||||
ret = clk_prepare_enable(adc->clk);
|
if (ret)
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
stm32_adc_set_res(adc);
|
stm32_adc_set_res(adc);
|
||||||
|
|
||||||
@@ -575,8 +572,7 @@ static int stm32_adc_hw_start(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_clk_dis:
|
err_clk_dis:
|
||||||
if (adc->clk)
|
clk_disable_unprepare(adc->clk);
|
||||||
clk_disable_unprepare(adc->clk);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -117,8 +117,7 @@ static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
|
|||||||
{
|
{
|
||||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||||
|
|
||||||
if (priv->aclk)
|
clk_disable_unprepare(priv->aclk);
|
||||||
clk_disable_unprepare(priv->aclk);
|
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
@@ -92,7 +93,12 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500;
|
|||||||
#define XADC_AXI_REG_GIER 0x5c
|
#define XADC_AXI_REG_GIER 0x5c
|
||||||
#define XADC_AXI_REG_IPISR 0x60
|
#define XADC_AXI_REG_IPISR 0x60
|
||||||
#define XADC_AXI_REG_IPIER 0x68
|
#define XADC_AXI_REG_IPIER 0x68
|
||||||
#define XADC_AXI_ADC_REG_OFFSET 0x200
|
|
||||||
|
/* 7 Series */
|
||||||
|
#define XADC_7S_AXI_ADC_REG_OFFSET 0x200
|
||||||
|
|
||||||
|
/* UltraScale */
|
||||||
|
#define XADC_US_AXI_ADC_REG_OFFSET 0x400
|
||||||
|
|
||||||
#define XADC_AXI_RESET_MAGIC 0xa
|
#define XADC_AXI_RESET_MAGIC 0xa
|
||||||
#define XADC_AXI_GIER_ENABLE BIT(31)
|
#define XADC_AXI_GIER_ENABLE BIT(31)
|
||||||
@@ -447,6 +453,12 @@ static const struct xadc_ops xadc_zynq_ops = {
|
|||||||
.get_dclk_rate = xadc_zynq_get_dclk_rate,
|
.get_dclk_rate = xadc_zynq_get_dclk_rate,
|
||||||
.interrupt_handler = xadc_zynq_interrupt_handler,
|
.interrupt_handler = xadc_zynq_interrupt_handler,
|
||||||
.update_alarm = xadc_zynq_update_alarm,
|
.update_alarm = xadc_zynq_update_alarm,
|
||||||
|
.type = XADC_TYPE_S7,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int xadc_axi_reg_offsets[] = {
|
||||||
|
[XADC_TYPE_S7] = XADC_7S_AXI_ADC_REG_OFFSET,
|
||||||
|
[XADC_TYPE_US] = XADC_US_AXI_ADC_REG_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||||
@@ -454,7 +466,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
|||||||
{
|
{
|
||||||
uint32_t val32;
|
uint32_t val32;
|
||||||
|
|
||||||
xadc_read_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, &val32);
|
xadc_read_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
|
||||||
|
&val32);
|
||||||
*val = val32 & 0xffff;
|
*val = val32 & 0xffff;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -463,7 +476,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
|||||||
static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||||
uint16_t val)
|
uint16_t val)
|
||||||
{
|
{
|
||||||
xadc_write_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, val);
|
xadc_write_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
|
||||||
|
val);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -541,7 +555,7 @@ static unsigned long xadc_axi_get_dclk(struct xadc *xadc)
|
|||||||
return clk_get_rate(xadc->clk);
|
return clk_get_rate(xadc->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct xadc_ops xadc_axi_ops = {
|
static const struct xadc_ops xadc_7s_axi_ops = {
|
||||||
.read = xadc_axi_read_adc_reg,
|
.read = xadc_axi_read_adc_reg,
|
||||||
.write = xadc_axi_write_adc_reg,
|
.write = xadc_axi_write_adc_reg,
|
||||||
.setup = xadc_axi_setup,
|
.setup = xadc_axi_setup,
|
||||||
@@ -549,6 +563,18 @@ static const struct xadc_ops xadc_axi_ops = {
|
|||||||
.update_alarm = xadc_axi_update_alarm,
|
.update_alarm = xadc_axi_update_alarm,
|
||||||
.interrupt_handler = xadc_axi_interrupt_handler,
|
.interrupt_handler = xadc_axi_interrupt_handler,
|
||||||
.flags = XADC_FLAGS_BUFFERED,
|
.flags = XADC_FLAGS_BUFFERED,
|
||||||
|
.type = XADC_TYPE_S7,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct xadc_ops xadc_us_axi_ops = {
|
||||||
|
.read = xadc_axi_read_adc_reg,
|
||||||
|
.write = xadc_axi_write_adc_reg,
|
||||||
|
.setup = xadc_axi_setup,
|
||||||
|
.get_dclk_rate = xadc_axi_get_dclk,
|
||||||
|
.update_alarm = xadc_axi_update_alarm,
|
||||||
|
.interrupt_handler = xadc_axi_interrupt_handler,
|
||||||
|
.flags = XADC_FLAGS_BUFFERED,
|
||||||
|
.type = XADC_TYPE_US,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg,
|
static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||||
@@ -585,15 +611,22 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev,
|
|||||||
const unsigned long *mask)
|
const unsigned long *mask)
|
||||||
{
|
{
|
||||||
struct xadc *xadc = iio_priv(indio_dev);
|
struct xadc *xadc = iio_priv(indio_dev);
|
||||||
unsigned int n;
|
size_t new_size, n;
|
||||||
|
void *data;
|
||||||
|
|
||||||
n = bitmap_weight(mask, indio_dev->masklength);
|
n = bitmap_weight(mask, indio_dev->masklength);
|
||||||
|
|
||||||
kfree(xadc->data);
|
if (check_mul_overflow(n, sizeof(*xadc->data), &new_size))
|
||||||
xadc->data = kcalloc(n, sizeof(*xadc->data), GFP_KERNEL);
|
|
||||||
if (!xadc->data)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
data = devm_krealloc(indio_dev->dev.parent, xadc->data,
|
||||||
|
new_size, GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memset(data, 0, new_size);
|
||||||
|
xadc->data = data;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,11 +738,12 @@ static const struct iio_trigger_ops xadc_trigger_ops = {
|
|||||||
static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
|
static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
|
struct device *dev = indio_dev->dev.parent;
|
||||||
struct iio_trigger *trig;
|
struct iio_trigger *trig;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
trig = iio_trigger_alloc("%s%d-%s", indio_dev->name,
|
trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name,
|
||||||
indio_dev->id, name);
|
indio_dev->id, name);
|
||||||
if (trig == NULL)
|
if (trig == NULL)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
@@ -717,21 +751,26 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
|
|||||||
trig->ops = &xadc_trigger_ops;
|
trig->ops = &xadc_trigger_ops;
|
||||||
iio_trigger_set_drvdata(trig, iio_priv(indio_dev));
|
iio_trigger_set_drvdata(trig, iio_priv(indio_dev));
|
||||||
|
|
||||||
ret = iio_trigger_register(trig);
|
ret = devm_iio_trigger_register(dev, trig);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_free_trig;
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
return trig;
|
return trig;
|
||||||
|
|
||||||
error_free_trig:
|
|
||||||
iio_trigger_free(trig);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode)
|
static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode)
|
||||||
{
|
{
|
||||||
uint16_t val;
|
uint16_t val;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As per datasheet the power-down bits are don't care in the
|
||||||
|
* UltraScale, but as per reality setting the power-down bit for the
|
||||||
|
* non-existing ADC-B powers down the main ADC, so just return and don't
|
||||||
|
* do anything.
|
||||||
|
*/
|
||||||
|
if (xadc->ops->type == XADC_TYPE_US)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Powerdown the ADC-B when it is not needed. */
|
/* Powerdown the ADC-B when it is not needed. */
|
||||||
switch (seq_mode) {
|
switch (seq_mode) {
|
||||||
case XADC_CONF1_SEQ_SIMULTANEOUS:
|
case XADC_CONF1_SEQ_SIMULTANEOUS:
|
||||||
@@ -751,6 +790,10 @@ static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode)
|
|||||||
{
|
{
|
||||||
unsigned int aux_scan_mode = scan_mode >> 16;
|
unsigned int aux_scan_mode = scan_mode >> 16;
|
||||||
|
|
||||||
|
/* UltraScale has only one ADC and supports only continuous mode */
|
||||||
|
if (xadc->ops->type == XADC_TYPE_US)
|
||||||
|
return XADC_CONF1_SEQ_CONTINUOUS;
|
||||||
|
|
||||||
if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL)
|
if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL)
|
||||||
return XADC_CONF1_SEQ_SIMULTANEOUS;
|
return XADC_CONF1_SEQ_SIMULTANEOUS;
|
||||||
|
|
||||||
@@ -863,6 +906,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||||
{
|
{
|
||||||
struct xadc *xadc = iio_priv(indio_dev);
|
struct xadc *xadc = iio_priv(indio_dev);
|
||||||
|
unsigned int bits = chan->scan_type.realbits;
|
||||||
uint16_t val16;
|
uint16_t val16;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -874,17 +918,17 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
val16 >>= 4;
|
val16 >>= chan->scan_type.shift;
|
||||||
if (chan->scan_type.sign == 'u')
|
if (chan->scan_type.sign == 'u')
|
||||||
*val = val16;
|
*val = val16;
|
||||||
else
|
else
|
||||||
*val = sign_extend32(val16, 11);
|
*val = sign_extend32(val16, bits - 1);
|
||||||
|
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_VOLTAGE:
|
case IIO_VOLTAGE:
|
||||||
/* V = (val * 3.0) / 4096 */
|
/* V = (val * 3.0) / 2**bits */
|
||||||
switch (chan->address) {
|
switch (chan->address) {
|
||||||
case XADC_REG_VCCINT:
|
case XADC_REG_VCCINT:
|
||||||
case XADC_REG_VCCAUX:
|
case XADC_REG_VCCAUX:
|
||||||
@@ -900,19 +944,19 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||||||
*val = 1000;
|
*val = 1000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*val2 = 12;
|
*val2 = chan->scan_type.realbits;
|
||||||
return IIO_VAL_FRACTIONAL_LOG2;
|
return IIO_VAL_FRACTIONAL_LOG2;
|
||||||
case IIO_TEMP:
|
case IIO_TEMP:
|
||||||
/* Temp in C = (val * 503.975) / 4096 - 273.15 */
|
/* Temp in C = (val * 503.975) / 2**bits - 273.15 */
|
||||||
*val = 503975;
|
*val = 503975;
|
||||||
*val2 = 12;
|
*val2 = bits;
|
||||||
return IIO_VAL_FRACTIONAL_LOG2;
|
return IIO_VAL_FRACTIONAL_LOG2;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
case IIO_CHAN_INFO_OFFSET:
|
case IIO_CHAN_INFO_OFFSET:
|
||||||
/* Only the temperature channel has an offset */
|
/* Only the temperature channel has an offset */
|
||||||
*val = -((273150 << 12) / 503975);
|
*val = -((273150 << bits) / 503975);
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
ret = xadc_read_samplerate(xadc);
|
ret = xadc_read_samplerate(xadc);
|
||||||
@@ -1001,7 +1045,7 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#define XADC_CHAN_TEMP(_chan, _scan_index, _addr) { \
|
#define XADC_CHAN_TEMP(_chan, _scan_index, _addr, _bits) { \
|
||||||
.type = IIO_TEMP, \
|
.type = IIO_TEMP, \
|
||||||
.indexed = 1, \
|
.indexed = 1, \
|
||||||
.channel = (_chan), \
|
.channel = (_chan), \
|
||||||
@@ -1015,14 +1059,14 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||||||
.scan_index = (_scan_index), \
|
.scan_index = (_scan_index), \
|
||||||
.scan_type = { \
|
.scan_type = { \
|
||||||
.sign = 'u', \
|
.sign = 'u', \
|
||||||
.realbits = 12, \
|
.realbits = (_bits), \
|
||||||
.storagebits = 16, \
|
.storagebits = 16, \
|
||||||
.shift = 4, \
|
.shift = 16 - (_bits), \
|
||||||
.endianness = IIO_CPU, \
|
.endianness = IIO_CPU, \
|
||||||
}, \
|
}, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) { \
|
#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _bits, _ext, _alarm) { \
|
||||||
.type = IIO_VOLTAGE, \
|
.type = IIO_VOLTAGE, \
|
||||||
.indexed = 1, \
|
.indexed = 1, \
|
||||||
.channel = (_chan), \
|
.channel = (_chan), \
|
||||||
@@ -1035,41 +1079,82 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||||||
.scan_index = (_scan_index), \
|
.scan_index = (_scan_index), \
|
||||||
.scan_type = { \
|
.scan_type = { \
|
||||||
.sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
|
.sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
|
||||||
.realbits = 12, \
|
.realbits = (_bits), \
|
||||||
.storagebits = 16, \
|
.storagebits = 16, \
|
||||||
.shift = 4, \
|
.shift = 16 - (_bits), \
|
||||||
.endianness = IIO_CPU, \
|
.endianness = IIO_CPU, \
|
||||||
}, \
|
}, \
|
||||||
.extend_name = _ext, \
|
.extend_name = _ext, \
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iio_chan_spec xadc_channels[] = {
|
/* 7 Series */
|
||||||
XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
#define XADC_7S_CHAN_TEMP(_chan, _scan_index, _addr) \
|
||||||
XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 12)
|
||||||
XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
#define XADC_7S_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
|
||||||
XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 12, _ext, _alarm)
|
||||||
XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
|
|
||||||
XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
|
static const struct iio_chan_spec xadc_7s_channels[] = {
|
||||||
XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
|
XADC_7S_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||||
XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
XADC_7S_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||||
XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
XADC_7S_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||||
XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
XADC_7S_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||||
XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
|
||||||
XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
|
||||||
XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
|
||||||
XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||||
XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||||
XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
||||||
XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
XADC_7S_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
||||||
|
XADC_7S_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
||||||
|
XADC_7S_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
||||||
|
XADC_7S_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
||||||
|
XADC_7S_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
||||||
|
XADC_7S_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
||||||
|
XADC_7S_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* UltraScale */
|
||||||
|
#define XADC_US_CHAN_TEMP(_chan, _scan_index, _addr) \
|
||||||
|
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 10)
|
||||||
|
#define XADC_US_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
|
||||||
|
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 10, _ext, _alarm)
|
||||||
|
|
||||||
|
static const struct iio_chan_spec xadc_us_channels[] = {
|
||||||
|
XADC_US_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||||
|
XADC_US_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||||
|
XADC_US_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||||
|
XADC_US_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||||
|
XADC_US_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpsintlp", true),
|
||||||
|
XADC_US_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpsintfp", true),
|
||||||
|
XADC_US_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccpsaux", true),
|
||||||
|
XADC_US_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
||||||
|
XADC_US_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct iio_info xadc_info = {
|
static const struct iio_info xadc_info = {
|
||||||
@@ -1083,8 +1168,16 @@ static const struct iio_info xadc_info = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id xadc_of_match_table[] = {
|
static const struct of_device_id xadc_of_match_table[] = {
|
||||||
{ .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops },
|
{
|
||||||
{ .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops },
|
.compatible = "xlnx,zynq-xadc-1.00.a",
|
||||||
|
.data = &xadc_zynq_ops
|
||||||
|
}, {
|
||||||
|
.compatible = "xlnx,axi-xadc-1.00.a",
|
||||||
|
.data = &xadc_7s_axi_ops
|
||||||
|
}, {
|
||||||
|
.compatible = "xlnx,system-management-wiz-1.3",
|
||||||
|
.data = &xadc_us_axi_ops
|
||||||
|
},
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, xadc_of_match_table);
|
MODULE_DEVICE_TABLE(of, xadc_of_match_table);
|
||||||
@@ -1094,8 +1187,10 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||||||
{
|
{
|
||||||
struct device *dev = indio_dev->dev.parent;
|
struct device *dev = indio_dev->dev.parent;
|
||||||
struct xadc *xadc = iio_priv(indio_dev);
|
struct xadc *xadc = iio_priv(indio_dev);
|
||||||
|
const struct iio_chan_spec *channel_templates;
|
||||||
struct iio_chan_spec *channels, *chan;
|
struct iio_chan_spec *channels, *chan;
|
||||||
struct device_node *chan_node, *child;
|
struct device_node *chan_node, *child;
|
||||||
|
unsigned int max_channels;
|
||||||
unsigned int num_channels;
|
unsigned int num_channels;
|
||||||
const char *external_mux;
|
const char *external_mux;
|
||||||
u32 ext_mux_chan;
|
u32 ext_mux_chan;
|
||||||
@@ -1136,9 +1231,15 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||||||
|
|
||||||
*conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan);
|
*conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan);
|
||||||
}
|
}
|
||||||
|
if (xadc->ops->type == XADC_TYPE_S7) {
|
||||||
channels = devm_kmemdup(dev, xadc_channels,
|
channel_templates = xadc_7s_channels;
|
||||||
sizeof(xadc_channels), GFP_KERNEL);
|
max_channels = ARRAY_SIZE(xadc_7s_channels);
|
||||||
|
} else {
|
||||||
|
channel_templates = xadc_us_channels;
|
||||||
|
max_channels = ARRAY_SIZE(xadc_us_channels);
|
||||||
|
}
|
||||||
|
channels = devm_kmemdup(dev, channel_templates,
|
||||||
|
sizeof(channels[0]) * max_channels, GFP_KERNEL);
|
||||||
if (!channels)
|
if (!channels)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1148,7 +1249,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||||||
chan_node = of_get_child_by_name(np, "xlnx,channels");
|
chan_node = of_get_child_by_name(np, "xlnx,channels");
|
||||||
if (chan_node) {
|
if (chan_node) {
|
||||||
for_each_child_of_node(chan_node, child) {
|
for_each_child_of_node(chan_node, child) {
|
||||||
if (num_channels >= ARRAY_SIZE(xadc_channels)) {
|
if (num_channels >= max_channels) {
|
||||||
of_node_put(child);
|
of_node_put(child);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1184,8 +1285,28 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char * const xadc_type_names[] = {
|
||||||
|
[XADC_TYPE_S7] = "xadc",
|
||||||
|
[XADC_TYPE_US] = "xilinx-system-monitor",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xadc_clk_disable_unprepare(void *data)
|
||||||
|
{
|
||||||
|
struct clk *clk = data;
|
||||||
|
|
||||||
|
clk_disable_unprepare(clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xadc_cancel_delayed_work(void *data)
|
||||||
|
{
|
||||||
|
struct delayed_work *work = data;
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(work);
|
||||||
|
}
|
||||||
|
|
||||||
static int xadc_probe(struct platform_device *pdev)
|
static int xadc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
const struct of_device_id *id;
|
const struct of_device_id *id;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
unsigned int bipolar_mask;
|
unsigned int bipolar_mask;
|
||||||
@@ -1195,10 +1316,10 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
int irq;
|
int irq;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!pdev->dev.of_node)
|
if (!dev->of_node)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
id = of_match_node(xadc_of_match_table, pdev->dev.of_node);
|
id = of_match_node(xadc_of_match_table, dev->of_node);
|
||||||
if (!id)
|
if (!id)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@@ -1206,7 +1327,7 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
if (irq <= 0)
|
if (irq <= 0)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc));
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1222,43 +1343,44 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(xadc->base))
|
if (IS_ERR(xadc->base))
|
||||||
return PTR_ERR(xadc->base);
|
return PTR_ERR(xadc->base);
|
||||||
|
|
||||||
indio_dev->name = "xadc";
|
indio_dev->name = xadc_type_names[xadc->ops->type];
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->info = &xadc_info;
|
indio_dev->info = &xadc_info;
|
||||||
|
|
||||||
ret = xadc_parse_dt(indio_dev, pdev->dev.of_node, &conf0);
|
ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||||
ret = iio_triggered_buffer_setup(indio_dev,
|
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||||
&iio_pollfunc_store_time, &xadc_trigger_handler,
|
&iio_pollfunc_store_time,
|
||||||
&xadc_buffer_ops);
|
&xadc_trigger_handler,
|
||||||
|
&xadc_buffer_ops);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst");
|
xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst");
|
||||||
if (IS_ERR(xadc->convst_trigger)) {
|
if (IS_ERR(xadc->convst_trigger))
|
||||||
ret = PTR_ERR(xadc->convst_trigger);
|
return PTR_ERR(xadc->convst_trigger);
|
||||||
goto err_triggered_buffer_cleanup;
|
|
||||||
}
|
|
||||||
xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev,
|
xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev,
|
||||||
"samplerate");
|
"samplerate");
|
||||||
if (IS_ERR(xadc->samplerate_trigger)) {
|
if (IS_ERR(xadc->samplerate_trigger))
|
||||||
ret = PTR_ERR(xadc->samplerate_trigger);
|
return PTR_ERR(xadc->samplerate_trigger);
|
||||||
goto err_free_convst_trigger;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xadc->clk = devm_clk_get(&pdev->dev, NULL);
|
xadc->clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(xadc->clk)) {
|
if (IS_ERR(xadc->clk))
|
||||||
ret = PTR_ERR(xadc->clk);
|
return PTR_ERR(xadc->clk);
|
||||||
goto err_free_samplerate_trigger;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(xadc->clk);
|
ret = clk_prepare_enable(xadc->clk);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_samplerate_trigger;
|
return ret;
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(dev,
|
||||||
|
xadc_clk_disable_unprepare, xadc->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure not to exceed the maximum samplerate since otherwise the
|
* Make sure not to exceed the maximum samplerate since otherwise the
|
||||||
@@ -1267,22 +1389,28 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||||
ret = xadc_read_samplerate(xadc);
|
ret = xadc_read_samplerate(xadc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_free_samplerate_trigger;
|
return ret;
|
||||||
|
|
||||||
if (ret > XADC_MAX_SAMPLERATE) {
|
if (ret > XADC_MAX_SAMPLERATE) {
|
||||||
ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
|
ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_free_samplerate_trigger;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
|
ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||||
dev_name(&pdev->dev), indio_dev);
|
dev_name(dev), indio_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_clk_disable_unprepare;
|
return ret;
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
|
||||||
|
&xadc->zynq_unmask_work);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
|
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_irq;
|
return ret;
|
||||||
|
|
||||||
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
||||||
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||||
@@ -1290,7 +1418,7 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0);
|
ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_irq;
|
return ret;
|
||||||
|
|
||||||
bipolar_mask = 0;
|
bipolar_mask = 0;
|
||||||
for (i = 0; i < indio_dev->num_channels; i++) {
|
for (i = 0; i < indio_dev->num_channels; i++) {
|
||||||
@@ -1300,17 +1428,18 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask);
|
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_irq;
|
return ret;
|
||||||
|
|
||||||
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1),
|
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1),
|
||||||
bipolar_mask >> 16);
|
bipolar_mask >> 16);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_irq;
|
return ret;
|
||||||
|
|
||||||
/* Disable all alarms */
|
/* Disable all alarms */
|
||||||
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
|
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
|
||||||
XADC_CONF1_ALARM_MASK);
|
XADC_CONF1_ALARM_MASK);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_irq;
|
return ret;
|
||||||
|
|
||||||
/* Set thresholds to min/max */
|
/* Set thresholds to min/max */
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
@@ -1325,60 +1454,17 @@ static int xadc_probe(struct platform_device *pdev)
|
|||||||
ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||||
xadc->threshold[i]);
|
xadc->threshold[i]);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_free_irq;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Go to non-buffered mode */
|
/* Go to non-buffered mode */
|
||||||
xadc_postdisable(indio_dev);
|
xadc_postdisable(indio_dev);
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
return devm_iio_device_register(dev, indio_dev);
|
||||||
if (ret)
|
|
||||||
goto err_free_irq;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, indio_dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_free_irq:
|
|
||||||
free_irq(xadc->irq, indio_dev);
|
|
||||||
cancel_delayed_work_sync(&xadc->zynq_unmask_work);
|
|
||||||
err_clk_disable_unprepare:
|
|
||||||
clk_disable_unprepare(xadc->clk);
|
|
||||||
err_free_samplerate_trigger:
|
|
||||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
|
|
||||||
iio_trigger_free(xadc->samplerate_trigger);
|
|
||||||
err_free_convst_trigger:
|
|
||||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
|
|
||||||
iio_trigger_free(xadc->convst_trigger);
|
|
||||||
err_triggered_buffer_cleanup:
|
|
||||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
|
|
||||||
iio_triggered_buffer_cleanup(indio_dev);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xadc_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
|
||||||
struct xadc *xadc = iio_priv(indio_dev);
|
|
||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
|
||||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
|
||||||
iio_trigger_free(xadc->samplerate_trigger);
|
|
||||||
iio_trigger_free(xadc->convst_trigger);
|
|
||||||
iio_triggered_buffer_cleanup(indio_dev);
|
|
||||||
}
|
|
||||||
free_irq(xadc->irq, indio_dev);
|
|
||||||
cancel_delayed_work_sync(&xadc->zynq_unmask_work);
|
|
||||||
clk_disable_unprepare(xadc->clk);
|
|
||||||
kfree(xadc->data);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver xadc_driver = {
|
static struct platform_driver xadc_driver = {
|
||||||
.probe = xadc_probe,
|
.probe = xadc_probe,
|
||||||
.remove = xadc_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "xadc",
|
.name = "xadc",
|
||||||
.of_match_table = xadc_of_match_table,
|
.of_match_table = xadc_of_match_table,
|
||||||
|
@@ -155,9 +155,6 @@ err_out:
|
|||||||
return ret;
|
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,
|
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||||
enum iio_event_direction dir, enum iio_event_info info,
|
enum iio_event_direction dir, enum iio_event_info info,
|
||||||
@@ -177,7 +174,8 @@ int xadc_read_event_value(struct iio_dev *indio_dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
|
/* MSB aligned */
|
||||||
|
*val >>= 16 - chan->scan_type.realbits;
|
||||||
|
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
}
|
}
|
||||||
@@ -191,7 +189,8 @@ int xadc_write_event_value(struct iio_dev *indio_dev,
|
|||||||
struct xadc *xadc = iio_priv(indio_dev);
|
struct xadc *xadc = iio_priv(indio_dev);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
val <<= XADC_THRESHOLD_VALUE_SHIFT;
|
/* MSB aligned */
|
||||||
|
val <<= 16 - chan->scan_type.realbits;
|
||||||
|
|
||||||
if (val < 0 || val > 0xffff)
|
if (val < 0 || val > 0xffff)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@@ -70,6 +70,11 @@ struct xadc {
|
|||||||
int irq;
|
int irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum xadc_type {
|
||||||
|
XADC_TYPE_S7, /* Series 7 */
|
||||||
|
XADC_TYPE_US, /* UltraScale and UltraScale+ */
|
||||||
|
};
|
||||||
|
|
||||||
struct xadc_ops {
|
struct xadc_ops {
|
||||||
int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val);
|
int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val);
|
||||||
int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val);
|
int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val);
|
||||||
@@ -80,6 +85,7 @@ struct xadc_ops {
|
|||||||
irqreturn_t (*interrupt_handler)(int irq, void *devid);
|
irqreturn_t (*interrupt_handler)(int irq, void *devid);
|
||||||
|
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
enum xadc_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||||
|
@@ -479,7 +479,7 @@ static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
|
|||||||
var4 = (var3 / (calib->res_heat_range + 4));
|
var4 = (var3 / (calib->res_heat_range + 4));
|
||||||
var5 = 131 * calib->res_heat_val + 65536;
|
var5 = 131 * calib->res_heat_val + 65536;
|
||||||
heatr_res_x100 = ((var4 / var5) - 250) * 34;
|
heatr_res_x100 = ((var4 / var5) - 250) * 34;
|
||||||
heatr_res = (heatr_res_x100 + 50) / 100;
|
heatr_res = DIV_ROUND_CLOSEST(heatr_res_x100, 100);
|
||||||
|
|
||||||
return heatr_res;
|
return heatr_res;
|
||||||
}
|
}
|
||||||
|
@@ -282,7 +282,7 @@ static int pms7003_probe(struct serdev_device *serdev)
|
|||||||
state->serdev = serdev;
|
state->serdev = serdev;
|
||||||
indio_dev->info = &pms7003_info;
|
indio_dev->info = &pms7003_info;
|
||||||
indio_dev->name = PMS7003_DRIVER_NAME;
|
indio_dev->name = PMS7003_DRIVER_NAME;
|
||||||
indio_dev->channels = pms7003_channels,
|
indio_dev->channels = pms7003_channels;
|
||||||
indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
|
indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->available_scan_masks = pms7003_scan_masks;
|
indio_dev->available_scan_masks = pms7003_scan_masks;
|
||||||
|
@@ -71,6 +71,8 @@ static struct {
|
|||||||
{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
|
{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
|
||||||
|
|
||||||
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
|
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
|
||||||
|
{HID_USAGE_SENSOR_HINGE, 0, 0, 17453293},
|
||||||
|
{HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
|
||||||
};
|
};
|
||||||
|
|
||||||
static void simple_div(int dividend, int divisor, int *whole,
|
static void simple_div(int dividend, int divisor, int *whole,
|
||||||
|
@@ -488,24 +488,20 @@ int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
|
|||||||
EXPORT_SYMBOL(ms_sensors_ht_read_humidity);
|
EXPORT_SYMBOL(ms_sensors_ht_read_humidity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ms_sensors_tp_crc_valid() - CRC check function for
|
* ms_sensors_tp_crc4() - Calculate PROM CRC for
|
||||||
* Temperature and pressure devices.
|
* Temperature and pressure devices.
|
||||||
* This function is only used when reading PROM coefficients
|
* This function is only used when reading PROM coefficients
|
||||||
*
|
*
|
||||||
* @prom: pointer to PROM coefficients array
|
* @prom: pointer to PROM coefficients array
|
||||||
* @len: length of PROM coefficients array
|
|
||||||
*
|
*
|
||||||
* Return: True if CRC is ok.
|
* Return: CRC.
|
||||||
*/
|
*/
|
||||||
static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
static u8 ms_sensors_tp_crc4(u16 *prom)
|
||||||
{
|
{
|
||||||
unsigned int cnt, n_bit;
|
unsigned int cnt, n_bit;
|
||||||
u16 n_rem = 0x0000, crc_read = prom[0], crc = (*prom & 0xF000) >> 12;
|
u16 n_rem = 0x0000;
|
||||||
|
|
||||||
prom[len - 1] = 0;
|
for (cnt = 0; cnt < MS_SENSORS_TP_PROM_WORDS_NB * 2; cnt++) {
|
||||||
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
|
|
||||||
|
|
||||||
for (cnt = 0; cnt < len * 2; cnt++) {
|
|
||||||
if (cnt % 2 == 1)
|
if (cnt % 2 == 1)
|
||||||
n_rem ^= prom[cnt >> 1] & 0x00FF;
|
n_rem ^= prom[cnt >> 1] & 0x00FF;
|
||||||
else
|
else
|
||||||
@@ -518,10 +514,55 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
|||||||
n_rem <<= 1;
|
n_rem <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n_rem >>= 12;
|
|
||||||
prom[0] = crc_read;
|
|
||||||
|
|
||||||
return n_rem == crc;
|
return n_rem >> 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ms_sensors_tp_crc_valid_112() - CRC check function for
|
||||||
|
* Temperature and pressure devices for 112bit PROM.
|
||||||
|
* This function is only used when reading PROM coefficients
|
||||||
|
*
|
||||||
|
* @prom: pointer to PROM coefficients array
|
||||||
|
*
|
||||||
|
* Return: True if CRC is ok.
|
||||||
|
*/
|
||||||
|
static bool ms_sensors_tp_crc_valid_112(u16 *prom)
|
||||||
|
{
|
||||||
|
u16 w0 = prom[0], crc_read = (w0 & 0xF000) >> 12;
|
||||||
|
u8 crc;
|
||||||
|
|
||||||
|
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
|
||||||
|
prom[MS_SENSORS_TP_PROM_WORDS_NB - 1] = 0;
|
||||||
|
|
||||||
|
crc = ms_sensors_tp_crc4(prom);
|
||||||
|
|
||||||
|
prom[0] = w0;
|
||||||
|
|
||||||
|
return crc == crc_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ms_sensors_tp_crc_valid_128() - CRC check function for
|
||||||
|
* Temperature and pressure devices for 128bit PROM.
|
||||||
|
* This function is only used when reading PROM coefficients
|
||||||
|
*
|
||||||
|
* @prom: pointer to PROM coefficients array
|
||||||
|
*
|
||||||
|
* Return: True if CRC is ok.
|
||||||
|
*/
|
||||||
|
static bool ms_sensors_tp_crc_valid_128(u16 *prom)
|
||||||
|
{
|
||||||
|
u16 w7 = prom[7], crc_read = w7 & 0x000F;
|
||||||
|
u8 crc;
|
||||||
|
|
||||||
|
prom[7] &= 0xFF00; /* Clear the CRC and LSB part */
|
||||||
|
|
||||||
|
crc = ms_sensors_tp_crc4(prom);
|
||||||
|
|
||||||
|
prom[7] = w7;
|
||||||
|
|
||||||
|
return crc == crc_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -536,8 +577,9 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
|||||||
int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
|
int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
bool valid;
|
||||||
|
|
||||||
for (i = 0; i < MS_SENSORS_TP_PROM_WORDS_NB; i++) {
|
for (i = 0; i < dev_data->hw->prom_len; i++) {
|
||||||
ret = ms_sensors_read_prom_word(
|
ret = ms_sensors_read_prom_word(
|
||||||
dev_data->client,
|
dev_data->client,
|
||||||
MS_SENSORS_TP_PROM_READ + (i << 1),
|
MS_SENSORS_TP_PROM_READ + (i << 1),
|
||||||
@@ -547,8 +589,12 @@ int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ms_sensors_tp_crc_valid(dev_data->prom,
|
if (dev_data->hw->prom_len == 8)
|
||||||
MS_SENSORS_TP_PROM_WORDS_NB + 1)) {
|
valid = ms_sensors_tp_crc_valid_128(dev_data->prom);
|
||||||
|
else
|
||||||
|
valid = ms_sensors_tp_crc_valid_112(dev_data->prom);
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
dev_err(&dev_data->client->dev,
|
dev_err(&dev_data->client->dev,
|
||||||
"Calibration coefficients crc check error\n");
|
"Calibration coefficients crc check error\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
#define MS_SENSORS_TP_PROM_WORDS_NB 7
|
#define MS_SENSORS_TP_PROM_WORDS_NB 8
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ms_ht_dev - Humidity/Temperature sensor device structure
|
* struct ms_ht_dev - Humidity/Temperature sensor device structure
|
||||||
@@ -25,6 +25,16 @@ struct ms_ht_dev {
|
|||||||
u8 res_index;
|
u8 res_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ms_hw_data - Temperature/Pressure sensor hardware data
|
||||||
|
* @prom_len: number of words in the PROM
|
||||||
|
* @max_res_index: maximum sensor resolution index
|
||||||
|
*/
|
||||||
|
struct ms_tp_hw_data {
|
||||||
|
u8 prom_len;
|
||||||
|
u8 max_res_index;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ms_tp_dev - Temperature/Pressure sensor device structure
|
* struct ms_tp_dev - Temperature/Pressure sensor device structure
|
||||||
* @client: i2c client
|
* @client: i2c client
|
||||||
@@ -36,7 +46,8 @@ struct ms_ht_dev {
|
|||||||
struct ms_tp_dev {
|
struct ms_tp_dev {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB + 1];
|
const struct ms_tp_hw_data *hw;
|
||||||
|
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB];
|
||||||
u8 res_index;
|
u8 res_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -189,6 +189,16 @@ config AD5764
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad5764.
|
module will be called ad5764.
|
||||||
|
|
||||||
|
config AD5766
|
||||||
|
tristate "Analog Devices AD5766/AD5767 DAC driver"
|
||||||
|
depends on SPI_MASTER
|
||||||
|
help
|
||||||
|
Say yes here to build support for Analog Devices AD5766, AD5767
|
||||||
|
Digital to Analog Converter.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ad5766.
|
||||||
|
|
||||||
config AD5770R
|
config AD5770R
|
||||||
tristate "Analog Devices AD5770R IDAC driver"
|
tristate "Analog Devices AD5770R IDAC driver"
|
||||||
depends on SPI_MASTER
|
depends on SPI_MASTER
|
||||||
|
@@ -19,6 +19,7 @@ obj-$(CONFIG_AD5755) += ad5755.o
|
|||||||
obj-$(CONFIG_AD5755) += ad5758.o
|
obj-$(CONFIG_AD5755) += ad5758.o
|
||||||
obj-$(CONFIG_AD5761) += ad5761.o
|
obj-$(CONFIG_AD5761) += ad5761.o
|
||||||
obj-$(CONFIG_AD5764) += ad5764.o
|
obj-$(CONFIG_AD5764) += ad5764.o
|
||||||
|
obj-$(CONFIG_AD5766) += ad5766.o
|
||||||
obj-$(CONFIG_AD5770R) += ad5770r.o
|
obj-$(CONFIG_AD5770R) += ad5770r.o
|
||||||
obj-$(CONFIG_AD5791) += ad5791.o
|
obj-$(CONFIG_AD5791) += ad5791.o
|
||||||
obj-$(CONFIG_AD5686) += ad5686.o
|
obj-$(CONFIG_AD5686) += ad5686.o
|
||||||
|
643
drivers/iio/dac/ad5766.c
Normal file
643
drivers/iio/dac/ad5766.c
Normal file
@@ -0,0 +1,643 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Analog Devices AD5766, AD5767
|
||||||
|
* Digital to Analog Converters driver
|
||||||
|
* Copyright 2019-2020 Analog Devices Inc.
|
||||||
|
*/
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
#define AD5766_UPPER_WORD_SPI_MASK GENMASK(31, 16)
|
||||||
|
#define AD5766_LOWER_WORD_SPI_MASK GENMASK(15, 0)
|
||||||
|
#define AD5766_DITHER_SOURCE_MASK(ch) GENMASK(((2 * ch) + 1), (2 * ch))
|
||||||
|
#define AD5766_DITHER_SOURCE(ch, source) BIT((ch * 2) + source)
|
||||||
|
#define AD5766_DITHER_SCALE_MASK(x) AD5766_DITHER_SOURCE_MASK(x)
|
||||||
|
#define AD5766_DITHER_SCALE(ch, scale) (scale << (ch * 2))
|
||||||
|
#define AD5766_DITHER_ENABLE_MASK(ch) BIT(ch)
|
||||||
|
#define AD5766_DITHER_ENABLE(ch, state) ((!state) << ch)
|
||||||
|
#define AD5766_DITHER_INVERT_MASK(ch) BIT(ch)
|
||||||
|
#define AD5766_DITHER_INVERT(ch, state) (state << ch)
|
||||||
|
|
||||||
|
#define AD5766_CMD_NOP_MUX_OUT 0x00
|
||||||
|
#define AD5766_CMD_SDO_CNTRL 0x01
|
||||||
|
#define AD5766_CMD_WR_IN_REG(x) (0x10 | ((x) & GENMASK(3, 0)))
|
||||||
|
#define AD5766_CMD_WR_DAC_REG(x) (0x20 | ((x) & GENMASK(3, 0)))
|
||||||
|
#define AD5766_CMD_SW_LDAC 0x30
|
||||||
|
#define AD5766_CMD_SPAN_REG 0x40
|
||||||
|
#define AD5766_CMD_WR_PWR_DITHER 0x51
|
||||||
|
#define AD5766_CMD_WR_DAC_REG_ALL 0x60
|
||||||
|
#define AD5766_CMD_SW_FULL_RESET 0x70
|
||||||
|
#define AD5766_CMD_READBACK_REG(x) (0x80 | ((x) & GENMASK(3, 0)))
|
||||||
|
#define AD5766_CMD_DITHER_SIG_1 0x90
|
||||||
|
#define AD5766_CMD_DITHER_SIG_2 0xA0
|
||||||
|
#define AD5766_CMD_INV_DITHER 0xB0
|
||||||
|
#define AD5766_CMD_DITHER_SCALE_1 0xC0
|
||||||
|
#define AD5766_CMD_DITHER_SCALE_2 0xD0
|
||||||
|
|
||||||
|
#define AD5766_FULL_RESET_CODE 0x1234
|
||||||
|
|
||||||
|
enum ad5766_type {
|
||||||
|
ID_AD5766,
|
||||||
|
ID_AD5767,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ad5766_voltage_range {
|
||||||
|
AD5766_VOLTAGE_RANGE_M20V_0V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M16V_to_0V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M10V_to_0V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M12V_to_14V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M16V_to_10V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M10V_to_6V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M5V_to_5V,
|
||||||
|
AD5766_VOLTAGE_RANGE_M10V_to_10V,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ad5766_chip_info - chip specific information
|
||||||
|
* @num_channels: number of channels
|
||||||
|
* @channels: channel specification
|
||||||
|
*/
|
||||||
|
struct ad5766_chip_info {
|
||||||
|
unsigned int num_channels;
|
||||||
|
const struct iio_chan_spec *channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AD5766_DITHER_ENABLE,
|
||||||
|
AD5766_DITHER_INVERT,
|
||||||
|
AD5766_DITHER_SOURCE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dither signal can also be scaled.
|
||||||
|
* Available dither scale strings corresponding to "dither_scale" field in
|
||||||
|
* "struct ad5766_state".
|
||||||
|
*/
|
||||||
|
static const char * const ad5766_dither_scales[] = {
|
||||||
|
"1",
|
||||||
|
"0.75",
|
||||||
|
"0.5",
|
||||||
|
"0.25",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ad5766_state - driver instance specific data
|
||||||
|
* @spi: SPI device
|
||||||
|
* @lock: Lock used to restrict concurent access to SPI device
|
||||||
|
* @chip_info: Chip model specific constants
|
||||||
|
* @gpio_reset: Reset GPIO, used to reset the device
|
||||||
|
* @crt_range: Current selected output range
|
||||||
|
* @dither_enable: Power enable bit for each channel dither block (for
|
||||||
|
* example, D15 = DAC 15,D8 = DAC 8, and D0 = DAC 0)
|
||||||
|
* 0 - Normal operation, 1 - Power down
|
||||||
|
* @dither_invert: Inverts the dither signal applied to the selected DAC
|
||||||
|
* outputs
|
||||||
|
* @dither_source: Selects between 2 possible sources:
|
||||||
|
* 1: N0, 2: N1
|
||||||
|
* Two bits are used for each channel
|
||||||
|
* @dither_scale: Two bits are used for each of the 16 channels:
|
||||||
|
* 0: 1 SCALING, 1: 0.75 SCALING, 2: 0.5 SCALING,
|
||||||
|
* 3: 0.25 SCALING.
|
||||||
|
* @data: SPI transfer buffers
|
||||||
|
*/
|
||||||
|
struct ad5766_state {
|
||||||
|
struct spi_device *spi;
|
||||||
|
struct mutex lock;
|
||||||
|
const struct ad5766_chip_info *chip_info;
|
||||||
|
struct gpio_desc *gpio_reset;
|
||||||
|
enum ad5766_voltage_range crt_range;
|
||||||
|
u16 dither_enable;
|
||||||
|
u16 dither_invert;
|
||||||
|
u32 dither_source;
|
||||||
|
u32 dither_scale;
|
||||||
|
union {
|
||||||
|
u32 d32;
|
||||||
|
u16 w16[2];
|
||||||
|
u8 b8[4];
|
||||||
|
} data[3] ____cacheline_aligned;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ad5766_span_tbl {
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct ad5766_span_tbl ad5766_span_tbl[] = {
|
||||||
|
[AD5766_VOLTAGE_RANGE_M20V_0V] = {-20, 0},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M16V_to_0V] = {-16, 0},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M10V_to_0V] = {-10, 0},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M12V_to_14V] = {-12, 14},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M16V_to_10V] = {-16, 10},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M10V_to_6V] = {-10, 6},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M5V_to_5V] = {-5, 5},
|
||||||
|
[AD5766_VOLTAGE_RANGE_M10V_to_10V] = {-10, 10},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __ad5766_spi_read(struct ad5766_state *st, u8 dac, int *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct spi_transfer xfers[] = {
|
||||||
|
{
|
||||||
|
.tx_buf = &st->data[0].d32,
|
||||||
|
.bits_per_word = 8,
|
||||||
|
.len = 3,
|
||||||
|
.cs_change = 1,
|
||||||
|
}, {
|
||||||
|
.tx_buf = &st->data[1].d32,
|
||||||
|
.rx_buf = &st->data[2].d32,
|
||||||
|
.bits_per_word = 8,
|
||||||
|
.len = 3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
st->data[0].d32 = AD5766_CMD_READBACK_REG(dac);
|
||||||
|
st->data[1].d32 = AD5766_CMD_NOP_MUX_OUT;
|
||||||
|
|
||||||
|
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*val = st->data[2].w16[1];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __ad5766_spi_write(struct ad5766_state *st, u8 command, u16 data)
|
||||||
|
{
|
||||||
|
st->data[0].b8[0] = command;
|
||||||
|
put_unaligned_be16(data, &st->data[0].b8[1]);
|
||||||
|
|
||||||
|
return spi_write(st->spi, &st->data[0].b8[0], 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_read(struct iio_dev *indio_dev, u8 dac, int *val)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&st->lock);
|
||||||
|
ret = __ad5766_spi_read(st, dac, val);
|
||||||
|
mutex_unlock(&st->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_write(struct iio_dev *indio_dev, u8 dac, u16 data)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&st->lock);
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_WR_DAC_REG(dac), data);
|
||||||
|
mutex_unlock(&st->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_reset(struct ad5766_state *st)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (st->gpio_reset) {
|
||||||
|
gpiod_set_value_cansleep(st->gpio_reset, 1);
|
||||||
|
ndelay(100); /* t_reset >= 100ns */
|
||||||
|
gpiod_set_value_cansleep(st->gpio_reset, 0);
|
||||||
|
} else {
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_SW_FULL_RESET,
|
||||||
|
AD5766_FULL_RESET_CODE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minimum time between a reset and the subsequent successful write is
|
||||||
|
* typically 25 ns
|
||||||
|
*/
|
||||||
|
ndelay(25);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_read_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int *val,
|
||||||
|
int *val2,
|
||||||
|
long m)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (m) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
ret = ad5766_read(indio_dev, chan->address, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_OFFSET:
|
||||||
|
*val = ad5766_span_tbl[st->crt_range].min;
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
*val = ad5766_span_tbl[st->crt_range].max -
|
||||||
|
ad5766_span_tbl[st->crt_range].min;
|
||||||
|
*val2 = st->chip_info->channels[0].scan_type.realbits;
|
||||||
|
|
||||||
|
return IIO_VAL_FRACTIONAL_LOG2;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int val,
|
||||||
|
int val2,
|
||||||
|
long info)
|
||||||
|
{
|
||||||
|
switch (info) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
{
|
||||||
|
const int max_val = GENMASK(chan->scan_type.realbits - 1, 0);
|
||||||
|
|
||||||
|
if (val > max_val || val < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
val <<= chan->scan_type.shift;
|
||||||
|
return ad5766_write(indio_dev, chan->address, val);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info ad5766_info = {
|
||||||
|
.read_raw = ad5766_read_raw,
|
||||||
|
.write_raw = ad5766_write_raw,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ad5766_get_dither_source(struct iio_dev *dev,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(dev);
|
||||||
|
u32 source;
|
||||||
|
|
||||||
|
source = st->dither_source & AD5766_DITHER_SOURCE_MASK(chan->channel);
|
||||||
|
source = source >> (chan->channel * 2);
|
||||||
|
source -= 1;
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_set_dither_source(struct iio_dev *dev,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
unsigned int source)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(dev);
|
||||||
|
uint16_t val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
st->dither_source &= ~AD5766_DITHER_SOURCE_MASK(chan->channel);
|
||||||
|
st->dither_source |= AD5766_DITHER_SOURCE(chan->channel, source);
|
||||||
|
|
||||||
|
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_source);
|
||||||
|
ret = ad5766_write(dev, AD5766_CMD_DITHER_SIG_1, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_source);
|
||||||
|
|
||||||
|
return ad5766_write(dev, AD5766_CMD_DITHER_SIG_2, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_get_dither_scale(struct iio_dev *dev,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(dev);
|
||||||
|
u32 scale;
|
||||||
|
|
||||||
|
scale = st->dither_scale & AD5766_DITHER_SCALE_MASK(chan->channel);
|
||||||
|
|
||||||
|
return (scale >> (chan->channel * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_set_dither_scale(struct iio_dev *dev,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
unsigned int scale)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct ad5766_state *st = iio_priv(dev);
|
||||||
|
uint16_t val;
|
||||||
|
|
||||||
|
st->dither_scale &= ~AD5766_DITHER_SCALE_MASK(chan->channel);
|
||||||
|
st->dither_scale |= AD5766_DITHER_SCALE(chan->channel, scale);
|
||||||
|
|
||||||
|
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_scale);
|
||||||
|
ret = ad5766_write(dev, AD5766_CMD_DITHER_SCALE_1, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_scale);
|
||||||
|
|
||||||
|
return ad5766_write(dev, AD5766_CMD_DITHER_SCALE_2, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_enum ad5766_dither_scale_enum = {
|
||||||
|
.items = ad5766_dither_scales,
|
||||||
|
.num_items = ARRAY_SIZE(ad5766_dither_scales),
|
||||||
|
.set = ad5766_set_dither_scale,
|
||||||
|
.get = ad5766_get_dither_scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t ad5766_read_ext(struct iio_dev *indio_dev,
|
||||||
|
uintptr_t private,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
switch (private) {
|
||||||
|
case AD5766_DITHER_ENABLE:
|
||||||
|
return sprintf(buf, "%u\n",
|
||||||
|
!(st->dither_enable & BIT(chan->channel)));
|
||||||
|
break;
|
||||||
|
case AD5766_DITHER_INVERT:
|
||||||
|
return sprintf(buf, "%u\n",
|
||||||
|
!!(st->dither_invert & BIT(chan->channel)));
|
||||||
|
break;
|
||||||
|
case AD5766_DITHER_SOURCE:
|
||||||
|
return sprintf(buf, "%d\n",
|
||||||
|
ad5766_get_dither_source(indio_dev, chan));
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ad5766_write_ext(struct iio_dev *indio_dev,
|
||||||
|
uintptr_t private,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct ad5766_state *st = iio_priv(indio_dev);
|
||||||
|
bool readin;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtobool(buf, &readin);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (private) {
|
||||||
|
case AD5766_DITHER_ENABLE:
|
||||||
|
st->dither_enable &= ~AD5766_DITHER_ENABLE_MASK(chan->channel);
|
||||||
|
st->dither_enable |= AD5766_DITHER_ENABLE(chan->channel,
|
||||||
|
readin);
|
||||||
|
ret = ad5766_write(indio_dev, AD5766_CMD_WR_PWR_DITHER,
|
||||||
|
st->dither_enable);
|
||||||
|
break;
|
||||||
|
case AD5766_DITHER_INVERT:
|
||||||
|
st->dither_invert &= ~AD5766_DITHER_INVERT_MASK(chan->channel);
|
||||||
|
st->dither_invert |= AD5766_DITHER_INVERT(chan->channel,
|
||||||
|
readin);
|
||||||
|
ret = ad5766_write(indio_dev, AD5766_CMD_INV_DITHER,
|
||||||
|
st->dither_invert);
|
||||||
|
break;
|
||||||
|
case AD5766_DITHER_SOURCE:
|
||||||
|
ret = ad5766_set_dither_source(indio_dev, chan, readin);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret ? ret : len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _AD5766_CHAN_EXT_INFO(_name, _what, _shared) { \
|
||||||
|
.name = _name, \
|
||||||
|
.read = ad5766_read_ext, \
|
||||||
|
.write = ad5766_write_ext, \
|
||||||
|
.private = _what, \
|
||||||
|
.shared = _shared, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IIO_ENUM_AVAILABLE_SHARED(_name, _shared, _e) \
|
||||||
|
{ \
|
||||||
|
.name = (_name "_available"), \
|
||||||
|
.shared = _shared, \
|
||||||
|
.read = iio_enum_available_read, \
|
||||||
|
.private = (uintptr_t)(_e), \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_chan_spec_ext_info ad5766_ext_info[] = {
|
||||||
|
|
||||||
|
_AD5766_CHAN_EXT_INFO("dither_enable", AD5766_DITHER_ENABLE,
|
||||||
|
IIO_SEPARATE),
|
||||||
|
_AD5766_CHAN_EXT_INFO("dither_invert", AD5766_DITHER_INVERT,
|
||||||
|
IIO_SEPARATE),
|
||||||
|
_AD5766_CHAN_EXT_INFO("dither_source", AD5766_DITHER_SOURCE,
|
||||||
|
IIO_SEPARATE),
|
||||||
|
IIO_ENUM("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum),
|
||||||
|
IIO_ENUM_AVAILABLE_SHARED("dither_scale",
|
||||||
|
IIO_SEPARATE,
|
||||||
|
&ad5766_dither_scale_enum),
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AD576x_CHANNEL(_chan, _bits) { \
|
||||||
|
.type = IIO_VOLTAGE, \
|
||||||
|
.indexed = 1, \
|
||||||
|
.output = 1, \
|
||||||
|
.channel = (_chan), \
|
||||||
|
.address = (_chan), \
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||||
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE), \
|
||||||
|
.scan_type = { \
|
||||||
|
.sign = 'u', \
|
||||||
|
.realbits = (_bits), \
|
||||||
|
.storagebits = 16, \
|
||||||
|
.shift = 16 - (_bits), \
|
||||||
|
}, \
|
||||||
|
.ext_info = ad5766_ext_info, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECLARE_AD576x_CHANNELS(_name, _bits) \
|
||||||
|
const struct iio_chan_spec _name[] = { \
|
||||||
|
AD576x_CHANNEL(0, (_bits)), \
|
||||||
|
AD576x_CHANNEL(1, (_bits)), \
|
||||||
|
AD576x_CHANNEL(2, (_bits)), \
|
||||||
|
AD576x_CHANNEL(3, (_bits)), \
|
||||||
|
AD576x_CHANNEL(4, (_bits)), \
|
||||||
|
AD576x_CHANNEL(5, (_bits)), \
|
||||||
|
AD576x_CHANNEL(6, (_bits)), \
|
||||||
|
AD576x_CHANNEL(7, (_bits)), \
|
||||||
|
AD576x_CHANNEL(8, (_bits)), \
|
||||||
|
AD576x_CHANNEL(9, (_bits)), \
|
||||||
|
AD576x_CHANNEL(10, (_bits)), \
|
||||||
|
AD576x_CHANNEL(11, (_bits)), \
|
||||||
|
AD576x_CHANNEL(12, (_bits)), \
|
||||||
|
AD576x_CHANNEL(13, (_bits)), \
|
||||||
|
AD576x_CHANNEL(14, (_bits)), \
|
||||||
|
AD576x_CHANNEL(15, (_bits)), \
|
||||||
|
}
|
||||||
|
|
||||||
|
static DECLARE_AD576x_CHANNELS(ad5766_channels, 16);
|
||||||
|
static DECLARE_AD576x_CHANNELS(ad5767_channels, 12);
|
||||||
|
|
||||||
|
static const struct ad5766_chip_info ad5766_chip_infos[] = {
|
||||||
|
[ID_AD5766] = {
|
||||||
|
.num_channels = ARRAY_SIZE(ad5766_channels),
|
||||||
|
.channels = ad5766_channels,
|
||||||
|
},
|
||||||
|
[ID_AD5767] = {
|
||||||
|
.num_channels = ARRAY_SIZE(ad5767_channels),
|
||||||
|
.channels = ad5767_channels,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ad5766_get_output_range(struct ad5766_state *st)
|
||||||
|
{
|
||||||
|
int i, ret, min, max, tmp[2];
|
||||||
|
|
||||||
|
ret = device_property_read_u32_array(&st->spi->dev,
|
||||||
|
"output-range-voltage",
|
||||||
|
tmp, 2);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
min = tmp[0] / 1000;
|
||||||
|
max = tmp[1] / 1000;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ad5766_span_tbl); i++) {
|
||||||
|
if (ad5766_span_tbl[i].min != min ||
|
||||||
|
ad5766_span_tbl[i].max != max)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
st->crt_range = i;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_default_setup(struct ad5766_state *st)
|
||||||
|
{
|
||||||
|
uint16_t val;
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
/* Always issue a reset before writing to the span register. */
|
||||||
|
ret = ad5766_reset(st);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ad5766_get_output_range(st);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Dither power down */
|
||||||
|
st->dither_enable = GENMASK(15, 0);
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_WR_PWR_DITHER,
|
||||||
|
st->dither_enable);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
st->dither_source = 0;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(ad5766_channels); i++)
|
||||||
|
st->dither_source |= AD5766_DITHER_SOURCE(i, 0);
|
||||||
|
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_source);
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SIG_1, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_source);
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SIG_2, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
st->dither_scale = 0;
|
||||||
|
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_scale);
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SCALE_1, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_scale);
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SCALE_2, val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
st->dither_invert = 0;
|
||||||
|
ret = __ad5766_spi_write(st, AD5766_CMD_INV_DITHER, st->dither_invert);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return __ad5766_spi_write(st, AD5766_CMD_SPAN_REG, st->crt_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ad5766_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
enum ad5766_type type;
|
||||||
|
struct iio_dev *indio_dev;
|
||||||
|
struct ad5766_state *st;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||||
|
if (!indio_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
st = iio_priv(indio_dev);
|
||||||
|
mutex_init(&st->lock);
|
||||||
|
|
||||||
|
st->spi = spi;
|
||||||
|
type = spi_get_device_id(spi)->driver_data;
|
||||||
|
st->chip_info = &ad5766_chip_infos[type];
|
||||||
|
|
||||||
|
indio_dev->channels = st->chip_info->channels;
|
||||||
|
indio_dev->num_channels = st->chip_info->num_channels;
|
||||||
|
indio_dev->info = &ad5766_info;
|
||||||
|
indio_dev->dev.parent = &spi->dev;
|
||||||
|
indio_dev->dev.of_node = spi->dev.of_node;
|
||||||
|
indio_dev->name = spi_get_device_id(spi)->name;
|
||||||
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
|
||||||
|
st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
|
||||||
|
GPIOD_OUT_LOW);
|
||||||
|
if (IS_ERR(st->gpio_reset))
|
||||||
|
return PTR_ERR(st->gpio_reset);
|
||||||
|
|
||||||
|
ret = ad5766_default_setup(st);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id ad5766_dt_match[] = {
|
||||||
|
{ .compatible = "adi,ad5766" },
|
||||||
|
{ .compatible = "adi,ad5767" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, ad5766_dt_match);
|
||||||
|
|
||||||
|
static const struct spi_device_id ad5766_spi_ids[] = {
|
||||||
|
{ "ad5766", ID_AD5766 },
|
||||||
|
{ "ad5767", ID_AD5767 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, ad5766_spi_ids);
|
||||||
|
|
||||||
|
static struct spi_driver ad5766_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "ad5766",
|
||||||
|
.of_match_table = ad5766_dt_match,
|
||||||
|
},
|
||||||
|
.probe = ad5766_probe,
|
||||||
|
.id_table = ad5766_spi_ids,
|
||||||
|
};
|
||||||
|
module_spi_driver(ad5766_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Denis-Gabriel Gheorghescu <denis.gheorghescu@analog.com>");
|
||||||
|
MODULE_DESCRIPTION("Analog Devices AD5766/AD5767 DACs");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@@ -582,8 +582,7 @@ error_disable_reg:
|
|||||||
if (!IS_ERR(st->reg))
|
if (!IS_ERR(st->reg))
|
||||||
regulator_disable(st->reg);
|
regulator_disable(st->reg);
|
||||||
error_disable_clk:
|
error_disable_clk:
|
||||||
if (clk)
|
clk_disable_unprepare(clk);
|
||||||
clk_disable_unprepare(clk);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -599,8 +598,7 @@ static int adf4350_remove(struct spi_device *spi)
|
|||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
|
|
||||||
if (st->clk)
|
clk_disable_unprepare(st->clk);
|
||||||
clk_disable_unprepare(st->clk);
|
|
||||||
|
|
||||||
if (!IS_ERR(reg))
|
if (!IS_ERR(reg))
|
||||||
regulator_disable(reg);
|
regulator_disable(reg);
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/iio/trigger_consumer.h>
|
#include <linux/iio/trigger_consumer.h>
|
||||||
#include <linux/iio/triggered_buffer.h>
|
#include <linux/iio/triggered_buffer.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
#include "bmg160.h"
|
#include "bmg160.h"
|
||||||
|
|
||||||
#define BMG160_IRQ_NAME "bmg160_event"
|
#define BMG160_IRQ_NAME "bmg160_event"
|
||||||
@@ -92,6 +93,7 @@
|
|||||||
|
|
||||||
struct bmg160_data {
|
struct bmg160_data {
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
struct regulator_bulk_data regulators[2];
|
||||||
struct iio_trigger *dready_trig;
|
struct iio_trigger *dready_trig;
|
||||||
struct iio_trigger *motion_trig;
|
struct iio_trigger *motion_trig;
|
||||||
struct iio_mount_matrix orientation;
|
struct iio_mount_matrix orientation;
|
||||||
@@ -1061,6 +1063,13 @@ static const char *bmg160_match_acpi_device(struct device *dev)
|
|||||||
return dev_name(dev);
|
return dev_name(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bmg160_disable_regulators(void *d)
|
||||||
|
{
|
||||||
|
struct bmg160_data *data = d;
|
||||||
|
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||||
|
}
|
||||||
|
|
||||||
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
@@ -1077,6 +1086,22 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||||||
data->irq = irq;
|
data->irq = irq;
|
||||||
data->regmap = regmap;
|
data->regmap = regmap;
|
||||||
|
|
||||||
|
data->regulators[0].supply = "vdd";
|
||||||
|
data->regulators[1].supply = "vddio";
|
||||||
|
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
|
||||||
|
data->regulators);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(dev, ret, "Failed to get regulators\n");
|
||||||
|
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||||
|
data->regulators);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(dev, bmg160_disable_regulators, data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||||
&data->orientation);
|
&data->orientation);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@@ -23,15 +23,20 @@ enum gyro_3d_channel {
|
|||||||
GYRO_3D_CHANNEL_MAX,
|
GYRO_3D_CHANNEL_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CHANNEL_SCAN_INDEX_TIMESTAMP GYRO_3D_CHANNEL_MAX
|
||||||
struct gyro_3d_state {
|
struct gyro_3d_state {
|
||||||
struct hid_sensor_hub_callbacks callbacks;
|
struct hid_sensor_hub_callbacks callbacks;
|
||||||
struct hid_sensor_common common_attributes;
|
struct hid_sensor_common common_attributes;
|
||||||
struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
|
struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
|
||||||
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
|
struct {
|
||||||
|
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
|
||||||
|
u64 timestamp __aligned(8);
|
||||||
|
} scan;
|
||||||
int scale_pre_decml;
|
int scale_pre_decml;
|
||||||
int scale_post_decml;
|
int scale_post_decml;
|
||||||
int scale_precision;
|
int scale_precision;
|
||||||
int value_offset;
|
int value_offset;
|
||||||
|
s64 timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
|
static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
|
||||||
@@ -72,7 +77,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = {
|
|||||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||||
}
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Adjust channel real bits based on report descriptor */
|
/* Adjust channel real bits based on report descriptor */
|
||||||
@@ -178,14 +184,6 @@ static const struct iio_info gyro_3d_info = {
|
|||||||
.write_raw = &gyro_3d_write_raw,
|
.write_raw = &gyro_3d_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 */
|
/* Callback handler to send event after all samples are received and captured */
|
||||||
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||||
unsigned usage_id,
|
unsigned usage_id,
|
||||||
@@ -195,10 +193,15 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||||||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||||
|
|
||||||
dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n");
|
dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n");
|
||||||
if (atomic_read(&gyro_state->common_attributes.data_ready))
|
if (atomic_read(&gyro_state->common_attributes.data_ready)) {
|
||||||
hid_sensor_push_data(indio_dev,
|
if (!gyro_state->timestamp)
|
||||||
gyro_state->gyro_val,
|
gyro_state->timestamp = iio_get_time_ns(indio_dev);
|
||||||
sizeof(gyro_state->gyro_val));
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, &gyro_state->scan,
|
||||||
|
gyro_state->timestamp);
|
||||||
|
|
||||||
|
gyro_state->timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -219,10 +222,15 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||||||
case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
|
case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
|
||||||
case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
|
case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
|
||||||
offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
|
offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
|
||||||
gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
gyro_state->scan.gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||||
*(u32 *)raw_data;
|
*(u32 *)raw_data;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||||
|
gyro_state->timestamp =
|
||||||
|
hid_sensor_convert_timestamp(&gyro_state->common_attributes,
|
||||||
|
*(s64 *)raw_data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,8 @@ config INV_MPU6050_I2C
|
|||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
help
|
help
|
||||||
This driver supports the Invensense MPU6050/9150,
|
This driver supports the Invensense MPU6050/9150,
|
||||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
|
||||||
IAM20680 motion tracking devices over I2C.
|
and IAM20680 motion tracking devices over I2C.
|
||||||
This driver can be built as a module. The module will be called
|
This driver can be built as a module. The module will be called
|
||||||
inv-mpu6050-i2c.
|
inv-mpu6050-i2c.
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ config INV_MPU6050_SPI
|
|||||||
select REGMAP_SPI
|
select REGMAP_SPI
|
||||||
help
|
help
|
||||||
This driver supports the Invensense MPU6000,
|
This driver supports the Invensense MPU6000,
|
||||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
|
||||||
IAM20680 motion tracking devices over SPI.
|
and IAM20680 motion tracking devices over SPI.
|
||||||
This driver can be built as a module. The module will be called
|
This driver can be built as a module. The module will be called
|
||||||
inv-mpu6050-spi.
|
inv-mpu6050-spi.
|
||||||
|
@@ -160,6 +160,14 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
|||||||
.fifo_size = 512,
|
.fifo_size = 512,
|
||||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.whoami = INV_MPU6880_WHOAMI_VALUE,
|
||||||
|
.name = "MPU6880",
|
||||||
|
.reg = ®_set_6500,
|
||||||
|
.config = &chip_config_6500,
|
||||||
|
.fifo_size = 4096,
|
||||||
|
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.whoami = INV_MPU6000_WHOAMI_VALUE,
|
.whoami = INV_MPU6000_WHOAMI_VALUE,
|
||||||
.name = "MPU6000",
|
.name = "MPU6000",
|
||||||
@@ -1323,6 +1331,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
|||||||
case INV_MPU6000:
|
case INV_MPU6000:
|
||||||
case INV_MPU6500:
|
case INV_MPU6500:
|
||||||
case INV_MPU6515:
|
case INV_MPU6515:
|
||||||
|
case INV_MPU6880:
|
||||||
case INV_MPU9250:
|
case INV_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
/* reset signal path (required for spi connection) */
|
/* reset signal path (required for spi connection) */
|
||||||
|
@@ -177,6 +177,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
|||||||
{"mpu6050", INV_MPU6050},
|
{"mpu6050", INV_MPU6050},
|
||||||
{"mpu6500", INV_MPU6500},
|
{"mpu6500", INV_MPU6500},
|
||||||
{"mpu6515", INV_MPU6515},
|
{"mpu6515", INV_MPU6515},
|
||||||
|
{"mpu6880", INV_MPU6880},
|
||||||
{"mpu9150", INV_MPU9150},
|
{"mpu9150", INV_MPU9150},
|
||||||
{"mpu9250", INV_MPU9250},
|
{"mpu9250", INV_MPU9250},
|
||||||
{"mpu9255", INV_MPU9255},
|
{"mpu9255", INV_MPU9255},
|
||||||
@@ -204,6 +205,10 @@ static const struct of_device_id inv_of_match[] = {
|
|||||||
.compatible = "invensense,mpu6515",
|
.compatible = "invensense,mpu6515",
|
||||||
.data = (void *)INV_MPU6515
|
.data = (void *)INV_MPU6515
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "invensense,mpu6880",
|
||||||
|
.data = (void *)INV_MPU6880
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.compatible = "invensense,mpu9150",
|
.compatible = "invensense,mpu9150",
|
||||||
.data = (void *)INV_MPU9150
|
.data = (void *)INV_MPU9150
|
||||||
|
@@ -70,6 +70,7 @@ enum inv_devices {
|
|||||||
INV_MPU6050,
|
INV_MPU6050,
|
||||||
INV_MPU6500,
|
INV_MPU6500,
|
||||||
INV_MPU6515,
|
INV_MPU6515,
|
||||||
|
INV_MPU6880,
|
||||||
INV_MPU6000,
|
INV_MPU6000,
|
||||||
INV_MPU9150,
|
INV_MPU9150,
|
||||||
INV_MPU9250,
|
INV_MPU9250,
|
||||||
@@ -373,6 +374,7 @@ struct inv_mpu6050_state {
|
|||||||
#define INV_MPU6000_WHOAMI_VALUE 0x68
|
#define INV_MPU6000_WHOAMI_VALUE 0x68
|
||||||
#define INV_MPU6050_WHOAMI_VALUE 0x68
|
#define INV_MPU6050_WHOAMI_VALUE 0x68
|
||||||
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
||||||
|
#define INV_MPU6880_WHOAMI_VALUE 0x78
|
||||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||||
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
||||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||||
|
@@ -70,6 +70,7 @@ static const struct spi_device_id inv_mpu_id[] = {
|
|||||||
{"mpu6000", INV_MPU6000},
|
{"mpu6000", INV_MPU6000},
|
||||||
{"mpu6500", INV_MPU6500},
|
{"mpu6500", INV_MPU6500},
|
||||||
{"mpu6515", INV_MPU6515},
|
{"mpu6515", INV_MPU6515},
|
||||||
|
{"mpu6880", INV_MPU6880},
|
||||||
{"mpu9250", INV_MPU9250},
|
{"mpu9250", INV_MPU9250},
|
||||||
{"mpu9255", INV_MPU9255},
|
{"mpu9255", INV_MPU9255},
|
||||||
{"icm20608", INV_ICM20608},
|
{"icm20608", INV_ICM20608},
|
||||||
@@ -96,6 +97,10 @@ static const struct of_device_id inv_of_match[] = {
|
|||||||
.compatible = "invensense,mpu6515",
|
.compatible = "invensense,mpu6515",
|
||||||
.data = (void *)INV_MPU6515
|
.data = (void *)INV_MPU6515
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "invensense,mpu6880",
|
||||||
|
.data = (void *)INV_MPU6880
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.compatible = "invensense,mpu9250",
|
.compatible = "invensense,mpu9250",
|
||||||
.data = (void *)INV_MPU9250
|
.data = (void *)INV_MPU9250
|
||||||
|
@@ -169,6 +169,36 @@ static const char * const iio_chan_info_postfix[] = {
|
|||||||
[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
|
[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iio_sysfs_match_string_with_gaps - matches given string in an array with gaps
|
||||||
|
* @array: array of strings
|
||||||
|
* @n: number of strings in the array
|
||||||
|
* @str: string to match with
|
||||||
|
*
|
||||||
|
* Returns index of @str in the @array or -EINVAL, similar to match_string().
|
||||||
|
* Uses sysfs_streq instead of strcmp for matching.
|
||||||
|
*
|
||||||
|
* This routine will look for a string in an array of strings.
|
||||||
|
* The search will continue until the element is found or the n-th element
|
||||||
|
* is reached, regardless of any NULL elements in the array.
|
||||||
|
*/
|
||||||
|
static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n,
|
||||||
|
const char *str)
|
||||||
|
{
|
||||||
|
const char *item;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
for (index = 0; index < n; index++) {
|
||||||
|
item = array[index];
|
||||||
|
if (!item)
|
||||||
|
continue;
|
||||||
|
if (sysfs_streq(item, str))
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_DEBUG_FS)
|
#if defined(CONFIG_DEBUG_FS)
|
||||||
/*
|
/*
|
||||||
* There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for
|
* There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for
|
||||||
@@ -470,8 +500,11 @@ ssize_t iio_enum_available_read(struct iio_dev *indio_dev,
|
|||||||
if (!e->num_items)
|
if (!e->num_items)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < e->num_items; ++i)
|
for (i = 0; i < e->num_items; ++i) {
|
||||||
|
if (!e->items[i])
|
||||||
|
continue;
|
||||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]);
|
len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
/* replace last space with a newline */
|
/* replace last space with a newline */
|
||||||
buf[len - 1] = '\n';
|
buf[len - 1] = '\n';
|
||||||
@@ -492,7 +525,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
|
|||||||
i = e->get(indio_dev, chan);
|
i = e->get(indio_dev, chan);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return i;
|
return i;
|
||||||
else if (i >= e->num_items)
|
else if (i >= e->num_items || !e->items[i])
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%s\n", e->items[i]);
|
return snprintf(buf, PAGE_SIZE, "%s\n", e->items[i]);
|
||||||
@@ -509,7 +542,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
|
|||||||
if (!e->set)
|
if (!e->set)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = __sysfs_match_string(e->items, e->num_items, buf);
|
ret = iio_sysfs_match_string_with_gaps(e->items, e->num_items, buf);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -1473,11 +1506,14 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|||||||
goto error_clear_attrs;
|
goto error_clear_attrs;
|
||||||
}
|
}
|
||||||
/* Copy across original attributes */
|
/* Copy across original attributes */
|
||||||
if (indio_dev->info->attrs)
|
if (indio_dev->info->attrs) {
|
||||||
memcpy(iio_dev_opaque->chan_attr_group.attrs,
|
memcpy(iio_dev_opaque->chan_attr_group.attrs,
|
||||||
indio_dev->info->attrs->attrs,
|
indio_dev->info->attrs->attrs,
|
||||||
sizeof(iio_dev_opaque->chan_attr_group.attrs[0])
|
sizeof(iio_dev_opaque->chan_attr_group.attrs[0])
|
||||||
*attrcount_orig);
|
*attrcount_orig);
|
||||||
|
iio_dev_opaque->chan_attr_group.is_visible =
|
||||||
|
indio_dev->info->attrs->is_visible;
|
||||||
|
}
|
||||||
attrn = attrcount_orig;
|
attrn = attrcount_orig;
|
||||||
/* Add all elements from the list. */
|
/* Add all elements from the list. */
|
||||||
list_for_each_entry(p, &iio_dev_opaque->channel_attr_list, l)
|
list_for_each_entry(p, &iio_dev_opaque->channel_attr_list, l)
|
||||||
|
@@ -191,8 +191,8 @@ err_free_channel:
|
|||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct iio_channel *chan = NULL;
|
struct iio_channel *chan = NULL;
|
||||||
|
|
||||||
@@ -230,6 +230,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
|||||||
|
|
||||||
return chan;
|
return chan;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name);
|
||||||
|
|
||||||
static struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
static struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||||
{
|
{
|
||||||
@@ -272,12 +273,6 @@ error_free_chans:
|
|||||||
|
|
||||||
#else /* CONFIG_OF */
|
#else /* CONFIG_OF */
|
||||||
|
|
||||||
static inline struct iio_channel *
|
|
||||||
of_iio_channel_get_by_name(struct device_node *np, const char *name)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -393,6 +388,29 @@ struct iio_channel *devm_iio_channel_get(struct device *dev,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(devm_iio_channel_get);
|
EXPORT_SYMBOL_GPL(devm_iio_channel_get);
|
||||||
|
|
||||||
|
struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev,
|
||||||
|
struct device_node *np,
|
||||||
|
const char *channel_name)
|
||||||
|
{
|
||||||
|
struct iio_channel **ptr, *channel;
|
||||||
|
|
||||||
|
ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL);
|
||||||
|
if (!ptr)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
channel = of_iio_channel_get_by_name(np, channel_name);
|
||||||
|
if (IS_ERR(channel)) {
|
||||||
|
devres_free(ptr);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = channel;
|
||||||
|
devres_add(dev, ptr);
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_of_iio_channel_get_by_name);
|
||||||
|
|
||||||
struct iio_channel *iio_channel_get_all(struct device *dev)
|
struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
* TODO: gesture + proximity calib offsets
|
* TODO: gesture + proximity calib offsets
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
@@ -1113,6 +1114,12 @@ static const struct i2c_device_id apds9960_id[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, apds9960_id);
|
MODULE_DEVICE_TABLE(i2c, apds9960_id);
|
||||||
|
|
||||||
|
static const struct acpi_device_id apds9960_acpi_match[] = {
|
||||||
|
{ "MSHW0184" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, apds9960_acpi_match);
|
||||||
|
|
||||||
static const struct of_device_id apds9960_of_match[] = {
|
static const struct of_device_id apds9960_of_match[] = {
|
||||||
{ .compatible = "avago,apds9960" },
|
{ .compatible = "avago,apds9960" },
|
||||||
{ }
|
{ }
|
||||||
@@ -1124,6 +1131,7 @@ static struct i2c_driver apds9960_driver = {
|
|||||||
.name = APDS9960_DRV_NAME,
|
.name = APDS9960_DRV_NAME,
|
||||||
.of_match_table = apds9960_of_match,
|
.of_match_table = apds9960_of_match,
|
||||||
.pm = &apds9960_pm_ops,
|
.pm = &apds9960_pm_ops,
|
||||||
|
.acpi_match_table = apds9960_acpi_match,
|
||||||
},
|
},
|
||||||
.probe = apds9960_probe,
|
.probe = apds9960_probe,
|
||||||
.remove = apds9960_remove,
|
.remove = apds9960_remove,
|
||||||
|
@@ -22,15 +22,21 @@ enum {
|
|||||||
CHANNEL_SCAN_INDEX_MAX
|
CHANNEL_SCAN_INDEX_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
|
||||||
|
|
||||||
struct als_state {
|
struct als_state {
|
||||||
struct hid_sensor_hub_callbacks callbacks;
|
struct hid_sensor_hub_callbacks callbacks;
|
||||||
struct hid_sensor_common common_attributes;
|
struct hid_sensor_common common_attributes;
|
||||||
struct hid_sensor_hub_attribute_info als_illum;
|
struct hid_sensor_hub_attribute_info als_illum;
|
||||||
u32 illum[CHANNEL_SCAN_INDEX_MAX];
|
struct {
|
||||||
|
u32 illum[CHANNEL_SCAN_INDEX_MAX];
|
||||||
|
u64 timestamp __aligned(8);
|
||||||
|
} scan;
|
||||||
int scale_pre_decml;
|
int scale_pre_decml;
|
||||||
int scale_post_decml;
|
int scale_post_decml;
|
||||||
int scale_precision;
|
int scale_precision;
|
||||||
int value_offset;
|
int value_offset;
|
||||||
|
s64 timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Channel definitions */
|
/* Channel definitions */
|
||||||
@@ -54,7 +60,8 @@ static const struct iio_chan_spec als_channels[] = {
|
|||||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
||||||
}
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Adjust channel real bits based on report descriptor */
|
/* Adjust channel real bits based on report descriptor */
|
||||||
@@ -168,14 +175,6 @@ static const struct iio_info als_info = {
|
|||||||
.write_raw = &als_write_raw,
|
.write_raw = &als_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 */
|
/* Callback handler to send event after all samples are received and captured */
|
||||||
static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||||
unsigned usage_id,
|
unsigned usage_id,
|
||||||
@@ -185,10 +184,14 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||||||
struct als_state *als_state = iio_priv(indio_dev);
|
struct als_state *als_state = iio_priv(indio_dev);
|
||||||
|
|
||||||
dev_dbg(&indio_dev->dev, "als_proc_event\n");
|
dev_dbg(&indio_dev->dev, "als_proc_event\n");
|
||||||
if (atomic_read(&als_state->common_attributes.data_ready))
|
if (atomic_read(&als_state->common_attributes.data_ready)) {
|
||||||
hid_sensor_push_data(indio_dev,
|
if (!als_state->timestamp)
|
||||||
&als_state->illum,
|
als_state->timestamp = iio_get_time_ns(indio_dev);
|
||||||
sizeof(als_state->illum));
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, &als_state->scan,
|
||||||
|
als_state->timestamp);
|
||||||
|
als_state->timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -206,10 +209,14 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||||||
|
|
||||||
switch (usage_id) {
|
switch (usage_id) {
|
||||||
case HID_USAGE_SENSOR_LIGHT_ILLUM:
|
case HID_USAGE_SENSOR_LIGHT_ILLUM:
|
||||||
als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
als_state->scan.illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||||
als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||||
|
als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes,
|
||||||
|
*(s64 *)raw_data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -285,7 +285,7 @@ static int tsl2583_get_lux(struct iio_dev *indio_dev)
|
|||||||
lux64 = lux64 * chip->als_settings.als_gain_trim;
|
lux64 = lux64 * chip->als_settings.als_gain_trim;
|
||||||
lux64 >>= 13;
|
lux64 >>= 13;
|
||||||
lux = lux64;
|
lux = lux64;
|
||||||
lux = (lux + 500) / 1000;
|
lux = DIV_ROUND_CLOSEST(lux, 1000);
|
||||||
|
|
||||||
if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
|
if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
|
||||||
return_max:
|
return_max:
|
||||||
@@ -361,12 +361,12 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
|
|||||||
u8 val;
|
u8 val;
|
||||||
|
|
||||||
/* determine als integration register */
|
/* determine als integration register */
|
||||||
als_count = (chip->als_settings.als_time * 100 + 135) / 270;
|
als_count = DIV_ROUND_CLOSEST(chip->als_settings.als_time * 100, 270);
|
||||||
if (!als_count)
|
if (!als_count)
|
||||||
als_count = 1; /* ensure at least one cycle */
|
als_count = 1; /* ensure at least one cycle */
|
||||||
|
|
||||||
/* convert back to time (encompasses overrides) */
|
/* convert back to time (encompasses overrides) */
|
||||||
als_time = (als_count * 27 + 5) / 10;
|
als_time = DIV_ROUND_CLOSEST(als_count * 27, 10);
|
||||||
|
|
||||||
val = 256 - als_count;
|
val = 256 - als_count;
|
||||||
ret = i2c_smbus_write_byte_data(chip->client,
|
ret = i2c_smbus_write_byte_data(chip->client,
|
||||||
@@ -380,7 +380,7 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
|
|||||||
|
|
||||||
/* set chip struct re scaling and saturation */
|
/* set chip struct re scaling and saturation */
|
||||||
chip->als_saturation = als_count * 922; /* 90% of full scale */
|
chip->als_saturation = als_count * 922; /* 90% of full scale */
|
||||||
chip->als_time_scale = (als_time + 25) / 50;
|
chip->als_time_scale = DIV_ROUND_CLOSEST(als_time, 50);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -392,7 +392,7 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2)
|
|||||||
{
|
{
|
||||||
int ret, it_ms;
|
int ret, it_ms;
|
||||||
|
|
||||||
it_ms = (val2 + 500) / 1000; /* round to ms */
|
it_ms = DIV_ROUND_CLOSEST(val2, 1000); /* round to ms */
|
||||||
if (val != 0 || it_ms < 1 || it_ms > 512)
|
if (val != 0 || it_ms < 1 || it_ms > 512)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@@ -205,4 +205,19 @@ config SENSORS_RM3100_SPI
|
|||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called rm3100-spi.
|
will be called rm3100-spi.
|
||||||
|
|
||||||
|
config YAMAHA_YAS530
|
||||||
|
tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)"
|
||||||
|
depends on I2C
|
||||||
|
select REGMAP_I2C
|
||||||
|
select IIO_BUFFER
|
||||||
|
select IIO_TRIGGERED_BUFFER
|
||||||
|
help
|
||||||
|
Say Y here to add support for the Yamaha YAS530 series of
|
||||||
|
3-Axis Magnetometers. Right now YAS530, YAS532 and YAS533 are
|
||||||
|
fully supported.
|
||||||
|
|
||||||
|
This driver can also be compiled as a module.
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called yamaha-yas.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@@ -28,3 +28,5 @@ obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
|
|||||||
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
|
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
|
||||||
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
|
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
|
||||||
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
|
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
#include <linux/iio/trigger_consumer.h>
|
#include <linux/iio/trigger_consumer.h>
|
||||||
#include <linux/iio/triggered_buffer.h>
|
#include <linux/iio/triggered_buffer.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#include "bmc150_magn.h"
|
#include "bmc150_magn.h"
|
||||||
|
|
||||||
@@ -135,6 +136,7 @@ struct bmc150_magn_data {
|
|||||||
*/
|
*/
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
struct regulator_bulk_data regulators[2];
|
||||||
struct iio_mount_matrix orientation;
|
struct iio_mount_matrix orientation;
|
||||||
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
|
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
|
||||||
s32 buffer[6];
|
s32 buffer[6];
|
||||||
@@ -692,12 +694,24 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
|
|||||||
int ret, chip_id;
|
int ret, chip_id;
|
||||||
struct bmc150_magn_preset preset;
|
struct bmc150_magn_preset preset;
|
||||||
|
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||||
|
data->regulators);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(data->dev, "Failed to enable regulators: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 3ms power-on time according to datasheet, let's better
|
||||||
|
* be safe than sorry and set this delay to 5ms.
|
||||||
|
*/
|
||||||
|
msleep(5);
|
||||||
|
|
||||||
ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
|
ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
|
||||||
false);
|
false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(data->dev,
|
dev_err(data->dev,
|
||||||
"Failed to bring up device from suspend mode\n");
|
"Failed to bring up device from suspend mode\n");
|
||||||
return ret;
|
goto err_regulator_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
|
ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
|
||||||
@@ -752,6 +766,8 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
|
|||||||
|
|
||||||
err_poweroff:
|
err_poweroff:
|
||||||
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
||||||
|
err_regulator_disable:
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,6 +883,13 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
|
|||||||
data->irq = irq;
|
data->irq = irq;
|
||||||
data->dev = dev;
|
data->dev = dev;
|
||||||
|
|
||||||
|
data->regulators[0].supply = "vdd";
|
||||||
|
data->regulators[1].supply = "vddio";
|
||||||
|
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
|
||||||
|
data->regulators);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(dev, ret, "failed to get regulators\n");
|
||||||
|
|
||||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||||
&data->orientation);
|
&data->orientation);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -984,6 +1007,7 @@ int bmc150_magn_remove(struct device *dev)
|
|||||||
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(bmc150_magn_remove);
|
EXPORT_SYMBOL(bmc150_magn_remove);
|
||||||
|
@@ -24,6 +24,7 @@ enum magn_3d_channel {
|
|||||||
CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
|
CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
|
||||||
CHANNEL_SCAN_INDEX_NORTH_MAGN,
|
CHANNEL_SCAN_INDEX_NORTH_MAGN,
|
||||||
CHANNEL_SCAN_INDEX_NORTH_TRUE,
|
CHANNEL_SCAN_INDEX_NORTH_TRUE,
|
||||||
|
CHANNEL_SCAN_INDEX_TIMESTAMP,
|
||||||
MAGN_3D_CHANNEL_MAX,
|
MAGN_3D_CHANNEL_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,6 +48,7 @@ struct magn_3d_state {
|
|||||||
|
|
||||||
struct common_attributes magn_flux_attr;
|
struct common_attributes magn_flux_attr;
|
||||||
struct common_attributes rot_attr;
|
struct common_attributes rot_attr;
|
||||||
|
s64 timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
|
static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
|
||||||
@@ -57,6 +59,7 @@ static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
|
|||||||
HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
|
HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
|
||||||
HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
|
HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
|
||||||
HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
|
HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
|
||||||
|
HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Channel definitions */
|
/* Channel definitions */
|
||||||
@@ -124,7 +127,8 @@ static const struct iio_chan_spec magn_3d_channels[] = {
|
|||||||
BIT(IIO_CHAN_INFO_SCALE) |
|
BIT(IIO_CHAN_INFO_SCALE) |
|
||||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
}
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Adjust channel real bits based on report descriptor */
|
/* Adjust channel real bits based on report descriptor */
|
||||||
@@ -273,13 +277,6 @@ static const struct iio_info magn_3d_info = {
|
|||||||
.write_raw = &magn_3d_write_raw,
|
.write_raw = &magn_3d_write_raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Function to push data to buffer */
|
|
||||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data)
|
|
||||||
{
|
|
||||||
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 */
|
/* Callback handler to send event after all samples are received and captured */
|
||||||
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||||
unsigned usage_id,
|
unsigned usage_id,
|
||||||
@@ -289,8 +286,15 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||||||
struct magn_3d_state *magn_state = iio_priv(indio_dev);
|
struct magn_3d_state *magn_state = iio_priv(indio_dev);
|
||||||
|
|
||||||
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
|
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
|
||||||
if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
|
if (atomic_read(&magn_state->magn_flux_attributes.data_ready)) {
|
||||||
hid_sensor_push_data(indio_dev, magn_state->iio_vals);
|
if (!magn_state->timestamp)
|
||||||
|
magn_state->timestamp = iio_get_time_ns(indio_dev);
|
||||||
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||||
|
magn_state->iio_vals,
|
||||||
|
magn_state->timestamp);
|
||||||
|
magn_state->timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -321,6 +325,11 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||||||
offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
|
offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
|
||||||
+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
|
+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
|
||||||
break;
|
break;
|
||||||
|
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||||
|
magn_state->timestamp =
|
||||||
|
hid_sensor_convert_timestamp(&magn_state->magn_flux_attributes,
|
||||||
|
*(s64 *)raw_data);
|
||||||
|
return ret;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@@ -386,9 +395,10 @@ static int magn_3d_parse_report(struct platform_device *pdev,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
|
/* attr_count include timestamp channel, and the iio_vals should be aligned to 8byte */
|
||||||
sizeof(u32),
|
st->iio_vals = devm_kcalloc(&pdev->dev,
|
||||||
GFP_KERNEL);
|
((attr_count + 1) % 2 + (attr_count + 1) / 2) * 2,
|
||||||
|
sizeof(u32), GFP_KERNEL);
|
||||||
if (!st->iio_vals) {
|
if (!st->iio_vals) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"failed to allocate space for iio values array\n");
|
"failed to allocate space for iio values array\n");
|
||||||
@@ -404,11 +414,13 @@ static int magn_3d_parse_report(struct platform_device *pdev,
|
|||||||
(_channels[*chan_count]).scan_index = *chan_count;
|
(_channels[*chan_count]).scan_index = *chan_count;
|
||||||
(_channels[*chan_count]).address = i;
|
(_channels[*chan_count]).address = i;
|
||||||
|
|
||||||
/* Set magn_val_addr to iio value address */
|
if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) {
|
||||||
st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
|
/* Set magn_val_addr to iio value address */
|
||||||
magn_3d_adjust_channel_bit_mask(_channels,
|
st->magn_val_addr[i] = &st->iio_vals[*chan_count];
|
||||||
*chan_count,
|
magn_3d_adjust_channel_bit_mask(_channels,
|
||||||
st->magn[i].size);
|
*chan_count,
|
||||||
|
st->magn[i].size);
|
||||||
|
}
|
||||||
(*chan_count)++;
|
(*chan_count)++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1049
drivers/iio/magnetometer/yamaha-yas530.c
Normal file
1049
drivers/iio/magnetometer/yamaha-yas530.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,15 +24,21 @@ enum incl_3d_channel {
|
|||||||
INCLI_3D_CHANNEL_MAX,
|
INCLI_3D_CHANNEL_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CHANNEL_SCAN_INDEX_TIMESTAMP INCLI_3D_CHANNEL_MAX
|
||||||
|
|
||||||
struct incl_3d_state {
|
struct incl_3d_state {
|
||||||
struct hid_sensor_hub_callbacks callbacks;
|
struct hid_sensor_hub_callbacks callbacks;
|
||||||
struct hid_sensor_common common_attributes;
|
struct hid_sensor_common common_attributes;
|
||||||
struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX];
|
struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX];
|
||||||
u32 incl_val[INCLI_3D_CHANNEL_MAX];
|
struct {
|
||||||
|
u32 incl_val[INCLI_3D_CHANNEL_MAX];
|
||||||
|
u64 timestamp __aligned(8);
|
||||||
|
} scan;
|
||||||
int scale_pre_decml;
|
int scale_pre_decml;
|
||||||
int scale_post_decml;
|
int scale_post_decml;
|
||||||
int scale_precision;
|
int scale_precision;
|
||||||
int value_offset;
|
int value_offset;
|
||||||
|
s64 timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = {
|
static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = {
|
||||||
@@ -73,7 +79,8 @@ static const struct iio_chan_spec incl_3d_channels[] = {
|
|||||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||||
}
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Adjust channel real bits based on report descriptor */
|
/* Adjust channel real bits based on report descriptor */
|
||||||
@@ -178,13 +185,6 @@ static const struct iio_info incl_3d_info = {
|
|||||||
.write_raw = &incl_3d_write_raw,
|
.write_raw = &incl_3d_write_raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Function to push data to buffer */
|
|
||||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
|
||||||
{
|
|
||||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
|
||||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback handler to send event after all samples are received and captured */
|
/* Callback handler to send event after all samples are received and captured */
|
||||||
static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||||
unsigned usage_id,
|
unsigned usage_id,
|
||||||
@@ -194,10 +194,16 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||||||
struct incl_3d_state *incl_state = iio_priv(indio_dev);
|
struct incl_3d_state *incl_state = iio_priv(indio_dev);
|
||||||
|
|
||||||
dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n");
|
dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n");
|
||||||
if (atomic_read(&incl_state->common_attributes.data_ready))
|
if (atomic_read(&incl_state->common_attributes.data_ready)) {
|
||||||
hid_sensor_push_data(indio_dev,
|
if (!incl_state->timestamp)
|
||||||
(u8 *)incl_state->incl_val,
|
incl_state->timestamp = iio_get_time_ns(indio_dev);
|
||||||
sizeof(incl_state->incl_val));
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||||
|
&incl_state->scan,
|
||||||
|
incl_state->timestamp);
|
||||||
|
|
||||||
|
incl_state->timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -214,13 +220,18 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||||||
|
|
||||||
switch (usage_id) {
|
switch (usage_id) {
|
||||||
case HID_USAGE_SENSOR_ORIENT_TILT_X:
|
case HID_USAGE_SENSOR_ORIENT_TILT_X:
|
||||||
incl_state->incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
|
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
|
||||||
break;
|
break;
|
||||||
case HID_USAGE_SENSOR_ORIENT_TILT_Y:
|
case HID_USAGE_SENSOR_ORIENT_TILT_Y:
|
||||||
incl_state->incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
|
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
|
||||||
break;
|
break;
|
||||||
case HID_USAGE_SENSOR_ORIENT_TILT_Z:
|
case HID_USAGE_SENSOR_ORIENT_TILT_Z:
|
||||||
incl_state->incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
|
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
|
||||||
|
break;
|
||||||
|
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||||
|
incl_state->timestamp =
|
||||||
|
hid_sensor_convert_timestamp(&incl_state->common_attributes,
|
||||||
|
*(s64 *)raw_data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@@ -20,11 +20,15 @@ struct dev_rot_state {
|
|||||||
struct hid_sensor_hub_callbacks callbacks;
|
struct hid_sensor_hub_callbacks callbacks;
|
||||||
struct hid_sensor_common common_attributes;
|
struct hid_sensor_common common_attributes;
|
||||||
struct hid_sensor_hub_attribute_info quaternion;
|
struct hid_sensor_hub_attribute_info quaternion;
|
||||||
u32 sampled_vals[4];
|
struct {
|
||||||
|
u32 sampled_vals[4] __aligned(16);
|
||||||
|
u64 timestamp __aligned(8);
|
||||||
|
} scan;
|
||||||
int scale_pre_decml;
|
int scale_pre_decml;
|
||||||
int scale_post_decml;
|
int scale_post_decml;
|
||||||
int scale_precision;
|
int scale_precision;
|
||||||
int value_offset;
|
int value_offset;
|
||||||
|
s64 timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Channel definitions */
|
/* Channel definitions */
|
||||||
@@ -37,8 +41,10 @@ static const struct iio_chan_spec dev_rot_channels[] = {
|
|||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||||
BIT(IIO_CHAN_INFO_SCALE) |
|
BIT(IIO_CHAN_INFO_SCALE) |
|
||||||
BIT(IIO_CHAN_INFO_HYSTERESIS)
|
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||||
}
|
.scan_index = 0
|
||||||
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Adjust channel real bits based on report descriptor */
|
/* Adjust channel real bits based on report descriptor */
|
||||||
@@ -70,7 +76,7 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
|
|||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
if (size >= 4) {
|
if (size >= 4) {
|
||||||
for (i = 0; i < 4; ++i)
|
for (i = 0; i < 4; ++i)
|
||||||
vals[i] = rot_state->sampled_vals[i];
|
vals[i] = rot_state->scan.sampled_vals[i];
|
||||||
ret_type = IIO_VAL_INT_MULTIPLE;
|
ret_type = IIO_VAL_INT_MULTIPLE;
|
||||||
*val_len = 4;
|
*val_len = 4;
|
||||||
} else
|
} else
|
||||||
@@ -132,15 +138,6 @@ static const struct iio_info dev_rot_info = {
|
|||||||
.write_raw = &dev_rot_write_raw,
|
.write_raw = &dev_rot_write_raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Function to push data to buffer */
|
|
||||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
|
||||||
{
|
|
||||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n");
|
|
||||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
|
||||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback handler to send event after all samples are received and captured */
|
/* Callback handler to send event after all samples are received and captured */
|
||||||
static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
|
static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||||
unsigned usage_id,
|
unsigned usage_id,
|
||||||
@@ -150,10 +147,15 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||||||
struct dev_rot_state *rot_state = iio_priv(indio_dev);
|
struct dev_rot_state *rot_state = iio_priv(indio_dev);
|
||||||
|
|
||||||
dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
|
dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
|
||||||
if (atomic_read(&rot_state->common_attributes.data_ready))
|
if (atomic_read(&rot_state->common_attributes.data_ready)) {
|
||||||
hid_sensor_push_data(indio_dev,
|
if (!rot_state->timestamp)
|
||||||
(u8 *)rot_state->sampled_vals,
|
rot_state->timestamp = iio_get_time_ns(indio_dev);
|
||||||
sizeof(rot_state->sampled_vals));
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
|
||||||
|
rot_state->timestamp);
|
||||||
|
|
||||||
|
rot_state->timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -168,10 +170,14 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||||||
struct dev_rot_state *rot_state = iio_priv(indio_dev);
|
struct dev_rot_state *rot_state = iio_priv(indio_dev);
|
||||||
|
|
||||||
if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
|
if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
|
||||||
memcpy(rot_state->sampled_vals, raw_data,
|
memcpy(&rot_state->scan.sampled_vals, raw_data,
|
||||||
sizeof(rot_state->sampled_vals));
|
sizeof(rot_state->scan.sampled_vals));
|
||||||
|
|
||||||
dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
|
dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
|
||||||
sizeof(rot_state->sampled_vals));
|
sizeof(rot_state->scan.sampled_vals));
|
||||||
|
} else if (usage_id == HID_USAGE_SENSOR_TIME_TIMESTAMP) {
|
||||||
|
rot_state->timestamp = hid_sensor_convert_timestamp(&rot_state->common_attributes,
|
||||||
|
*(s64 *)raw_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -16,4 +16,20 @@ config IQS624_POS
|
|||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called iqs624-pos.
|
will be called iqs624-pos.
|
||||||
|
|
||||||
|
config HID_SENSOR_CUSTOM_INTEL_HINGE
|
||||||
|
depends on HID_SENSOR_HUB
|
||||||
|
select IIO_BUFFER
|
||||||
|
select IIO_TRIGGERED_BUFFER
|
||||||
|
select HID_SENSOR_IIO_COMMON
|
||||||
|
select HID_SENSOR_IIO_TRIGGER
|
||||||
|
tristate "HID Hinge"
|
||||||
|
help
|
||||||
|
This sensor present three angles, hinge angel, screen angles
|
||||||
|
and keyboard angle respect to horizon (ground).
|
||||||
|
Say yes here to build support for the HID custom
|
||||||
|
intel hinge sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called hid-sensor-custom-hinge.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@@ -4,4 +4,5 @@
|
|||||||
|
|
||||||
# When adding new entries keep the list in alphabetical order
|
# When adding new entries keep the list in alphabetical order
|
||||||
|
|
||||||
|
obj-$(CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE) += hid-sensor-custom-intel-hinge.o
|
||||||
obj-$(CONFIG_IQS624_POS) += iqs624-pos.o
|
obj-$(CONFIG_IQS624_POS) += iqs624-pos.o
|
||||||
|
385
drivers/iio/position/hid-sensor-custom-intel-hinge.c
Normal file
385
drivers/iio/position/hid-sensor-custom-intel-hinge.c
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* HID Sensors Driver
|
||||||
|
* Copyright (c) 2020, Intel Corporation.
|
||||||
|
*/
|
||||||
|
#include <linux/hid-sensor-hub.h>
|
||||||
|
#include <linux/iio/buffer.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||||
|
|
||||||
|
enum hinge_channel {
|
||||||
|
CHANNEL_SCAN_INDEX_HINGE_ANGLE,
|
||||||
|
CHANNEL_SCAN_INDEX_SCREEN_ANGLE,
|
||||||
|
CHANNEL_SCAN_INDEX_KEYBOARD_ANGLE,
|
||||||
|
CHANNEL_SCAN_INDEX_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
|
||||||
|
|
||||||
|
static const u32 hinge_addresses[CHANNEL_SCAN_INDEX_MAX] = {
|
||||||
|
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1),
|
||||||
|
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(2),
|
||||||
|
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(3)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const hinge_labels[CHANNEL_SCAN_INDEX_MAX] = { "hinge",
|
||||||
|
"screen",
|
||||||
|
"keyboard" };
|
||||||
|
|
||||||
|
struct hinge_state {
|
||||||
|
struct iio_dev *indio_dev;
|
||||||
|
struct hid_sensor_hub_attribute_info hinge[CHANNEL_SCAN_INDEX_MAX];
|
||||||
|
struct hid_sensor_hub_callbacks callbacks;
|
||||||
|
struct hid_sensor_common common_attributes;
|
||||||
|
const char *labels[CHANNEL_SCAN_INDEX_MAX];
|
||||||
|
struct {
|
||||||
|
u32 hinge_val[3];
|
||||||
|
u64 timestamp __aligned(8);
|
||||||
|
} scan;
|
||||||
|
|
||||||
|
int scale_pre_decml;
|
||||||
|
int scale_post_decml;
|
||||||
|
int scale_precision;
|
||||||
|
int value_offset;
|
||||||
|
u64 timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Channel definitions */
|
||||||
|
static const struct iio_chan_spec hinge_channels[] = {
|
||||||
|
{
|
||||||
|
.type = IIO_ANGL,
|
||||||
|
.indexed = 1,
|
||||||
|
.channel = 0,
|
||||||
|
.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_HINGE_ANGLE,
|
||||||
|
.scan_type = {
|
||||||
|
.sign = 's',
|
||||||
|
.storagebits = 32,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
.type = IIO_ANGL,
|
||||||
|
.indexed = 1,
|
||||||
|
.channel = 1,
|
||||||
|
.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_SCREEN_ANGLE,
|
||||||
|
.scan_type = {
|
||||||
|
.sign = 's',
|
||||||
|
.storagebits = 32,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
.type = IIO_ANGL,
|
||||||
|
.indexed = 1,
|
||||||
|
.channel = 2,
|
||||||
|
.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_KEYBOARD_ANGLE,
|
||||||
|
.scan_type = {
|
||||||
|
.sign = 's',
|
||||||
|
.storagebits = 32,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Adjust channel real bits based on report descriptor */
|
||||||
|
static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels,
|
||||||
|
int channel, int size)
|
||||||
|
{
|
||||||
|
channels[channel].scan_type.realbits = size * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Channel read_raw handler */
|
||||||
|
static int hinge_read_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||||
|
long mask)
|
||||||
|
{
|
||||||
|
struct hinge_state *st = iio_priv(indio_dev);
|
||||||
|
struct hid_sensor_hub_device *hsdev;
|
||||||
|
int report_id;
|
||||||
|
s32 min;
|
||||||
|
|
||||||
|
hsdev = st->common_attributes.hsdev;
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
hid_sensor_power_state(&st->common_attributes, true);
|
||||||
|
report_id = st->hinge[chan->scan_index].report_id;
|
||||||
|
min = st->hinge[chan->scan_index].logical_minimum;
|
||||||
|
if (report_id < 0) {
|
||||||
|
hid_sensor_power_state(&st->common_attributes, false);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = sensor_hub_input_attr_get_raw_value(st->common_attributes.hsdev,
|
||||||
|
hsdev->usage,
|
||||||
|
hinge_addresses[chan->scan_index],
|
||||||
|
report_id,
|
||||||
|
SENSOR_HUB_SYNC, min < 0);
|
||||||
|
|
||||||
|
hid_sensor_power_state(&st->common_attributes, false);
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
*val = st->scale_pre_decml;
|
||||||
|
*val2 = st->scale_post_decml;
|
||||||
|
return st->scale_precision;
|
||||||
|
case IIO_CHAN_INFO_OFFSET:
|
||||||
|
*val = st->value_offset;
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
return hid_sensor_read_samp_freq_value(&st->common_attributes,
|
||||||
|
val, val2);
|
||||||
|
case IIO_CHAN_INFO_HYSTERESIS:
|
||||||
|
return hid_sensor_read_raw_hyst_value(&st->common_attributes,
|
||||||
|
val, val2);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Channel write_raw handler */
|
||||||
|
static int hinge_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, int val, int val2,
|
||||||
|
long mask)
|
||||||
|
{
|
||||||
|
struct hinge_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
return hid_sensor_write_samp_freq_value(&st->common_attributes,
|
||||||
|
val, val2);
|
||||||
|
case IIO_CHAN_INFO_HYSTERESIS:
|
||||||
|
return hid_sensor_write_raw_hyst_value(&st->common_attributes,
|
||||||
|
val, val2);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hinge_read_label(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan, char *label)
|
||||||
|
{
|
||||||
|
struct hinge_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return sprintf(label, "%s\n", st->labels[chan->channel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info hinge_info = {
|
||||||
|
.read_raw = hinge_read_raw,
|
||||||
|
.write_raw = hinge_write_raw,
|
||||||
|
.read_label = hinge_read_label,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback handler to send event after all samples are received
|
||||||
|
* and captured.
|
||||||
|
*/
|
||||||
|
static int hinge_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||||
|
unsigned int usage_id, void *priv)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||||
|
struct hinge_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
if (atomic_read(&st->common_attributes.data_ready)) {
|
||||||
|
if (!st->timestamp)
|
||||||
|
st->timestamp = iio_get_time_ns(indio_dev);
|
||||||
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, &st->scan,
|
||||||
|
st->timestamp);
|
||||||
|
|
||||||
|
st->timestamp = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Capture samples in local storage */
|
||||||
|
static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||||
|
unsigned int usage_id, size_t raw_len,
|
||||||
|
char *raw_data, void *priv)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||||
|
struct hinge_state *st = iio_priv(indio_dev);
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
switch (usage_id) {
|
||||||
|
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1):
|
||||||
|
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(2):
|
||||||
|
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(3):
|
||||||
|
offset = usage_id - HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1);
|
||||||
|
st->scan.hinge_val[offset] = *(u32 *)raw_data;
|
||||||
|
return 0;
|
||||||
|
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||||
|
st->timestamp = hid_sensor_convert_timestamp(&st->common_attributes,
|
||||||
|
*(int64_t *)raw_data);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse report which is specific to an usage id */
|
||||||
|
static int hinge_parse_report(struct platform_device *pdev,
|
||||||
|
struct hid_sensor_hub_device *hsdev,
|
||||||
|
struct iio_chan_spec *channels,
|
||||||
|
unsigned int usage_id, struct hinge_state *st)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; ++i) {
|
||||||
|
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||||
|
HID_INPUT_REPORT,
|
||||||
|
usage_id,
|
||||||
|
hinge_addresses[i],
|
||||||
|
&st->hinge[i]);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
hinge_adjust_channel_realbits(channels, i, st->hinge[i].size);
|
||||||
|
}
|
||||||
|
|
||||||
|
st->scale_precision = hid_sensor_format_scale(HID_USAGE_SENSOR_HINGE,
|
||||||
|
&st->hinge[CHANNEL_SCAN_INDEX_HINGE_ANGLE],
|
||||||
|
&st->scale_pre_decml, &st->scale_post_decml);
|
||||||
|
|
||||||
|
/* 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_FIELD_CUSTOM_VALUE(1),
|
||||||
|
&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_hinge_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct hinge_state *st;
|
||||||
|
struct iio_dev *indio_dev;
|
||||||
|
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
|
||||||
|
if (!indio_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, indio_dev);
|
||||||
|
|
||||||
|
st = iio_priv(indio_dev);
|
||||||
|
st->common_attributes.hsdev = hsdev;
|
||||||
|
st->common_attributes.pdev = pdev;
|
||||||
|
st->indio_dev = indio_dev;
|
||||||
|
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; i++)
|
||||||
|
st->labels[i] = hinge_labels[i];
|
||||||
|
|
||||||
|
ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
|
||||||
|
&st->common_attributes);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
indio_dev->num_channels = ARRAY_SIZE(hinge_channels);
|
||||||
|
indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels,
|
||||||
|
sizeof(hinge_channels), GFP_KERNEL);
|
||||||
|
if (!indio_dev->channels)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = hinge_parse_report(pdev, hsdev,
|
||||||
|
(struct iio_chan_spec *)indio_dev->channels,
|
||||||
|
hsdev->usage, st);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
indio_dev->dev.parent = &pdev->dev;
|
||||||
|
indio_dev->info = &hinge_info;
|
||||||
|
indio_dev->name = "hinge";
|
||||||
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
|
||||||
|
atomic_set(&st->common_attributes.data_ready, 0);
|
||||||
|
ret = hid_sensor_setup_trigger(indio_dev, indio_dev->name,
|
||||||
|
&st->common_attributes);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->callbacks.send_event = hinge_proc_event;
|
||||||
|
st->callbacks.capture_sample = hinge_capture_sample;
|
||||||
|
st->callbacks.pdev = pdev;
|
||||||
|
ret = sensor_hub_register_callback(hsdev, hsdev->usage, &st->callbacks);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "callback reg failed\n");
|
||||||
|
goto error_remove_trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = iio_device_register(indio_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "device register failed\n");
|
||||||
|
goto error_remove_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
error_remove_callback:
|
||||||
|
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||||
|
error_remove_trigger:
|
||||||
|
hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function to deinitialize the processing for usage id */
|
||||||
|
static int hid_hinge_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 hinge_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
iio_device_unregister(indio_dev);
|
||||||
|
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||||
|
hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct platform_device_id hid_hinge_ids[] = {
|
||||||
|
{
|
||||||
|
/* Format: HID-SENSOR-INT-usage_id_in_hex_lowercase */
|
||||||
|
.name = "HID-SENSOR-INT-020b",
|
||||||
|
},
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(platform, hid_hinge_ids);
|
||||||
|
|
||||||
|
static struct platform_driver hid_hinge_platform_driver = {
|
||||||
|
.id_table = hid_hinge_ids,
|
||||||
|
.driver = {
|
||||||
|
.name = KBUILD_MODNAME,
|
||||||
|
.pm = &hid_sensor_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = hid_hinge_probe,
|
||||||
|
.remove = hid_hinge_remove,
|
||||||
|
};
|
||||||
|
module_platform_driver(hid_hinge_platform_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("HID Sensor INTEL Hinge");
|
||||||
|
MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
@@ -30,9 +30,25 @@
|
|||||||
|
|
||||||
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
||||||
|
|
||||||
|
struct ms_tp_data {
|
||||||
|
const char *name;
|
||||||
|
const struct ms_tp_hw_data *hw;
|
||||||
|
};
|
||||||
|
|
||||||
static const int ms5637_samp_freq[6] = { 960, 480, 240, 120, 60, 30 };
|
static const int ms5637_samp_freq[6] = { 960, 480, 240, 120, 60, 30 };
|
||||||
/* String copy of the above const for readability purpose */
|
|
||||||
static const char ms5637_show_samp_freq[] = "960 480 240 120 60 30";
|
static ssize_t ms5637_show_samp_freq(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||||
|
struct ms_tp_dev *dev_data = iio_priv(indio_dev);
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for (i = 0; i <= dev_data->hw->max_res_index; i++)
|
||||||
|
len += sysfs_emit_at(buf, len, "%u ", ms5637_samp_freq[i]);
|
||||||
|
sysfs_emit_at(buf, len - 1, "\n");
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static int ms5637_read_raw(struct iio_dev *indio_dev,
|
static int ms5637_read_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *channel, int *val,
|
struct iio_chan_spec const *channel, int *val,
|
||||||
@@ -109,10 +125,10 @@ static const struct iio_chan_spec ms5637_channels[] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
|
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
|
||||||
|
|
||||||
static struct attribute *ms5637_attributes[] = {
|
static struct attribute *ms5637_attributes[] = {
|
||||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,6 +145,7 @@ static const struct iio_info ms5637_info = {
|
|||||||
static int ms5637_probe(struct i2c_client *client,
|
static int ms5637_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
const struct ms_tp_data *data;
|
||||||
struct ms_tp_dev *dev_data;
|
struct ms_tp_dev *dev_data;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -142,17 +159,25 @@ static int ms5637_probe(struct i2c_client *client,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id)
|
||||||
|
data = (const struct ms_tp_data *)id->driver_data;
|
||||||
|
else
|
||||||
|
data = device_get_match_data(&client->dev);
|
||||||
|
if (!data)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
dev_data = iio_priv(indio_dev);
|
dev_data = iio_priv(indio_dev);
|
||||||
dev_data->client = client;
|
dev_data->client = client;
|
||||||
dev_data->res_index = 5;
|
dev_data->res_index = data->hw->max_res_index;
|
||||||
|
dev_data->hw = data->hw;
|
||||||
mutex_init(&dev_data->lock);
|
mutex_init(&dev_data->lock);
|
||||||
|
|
||||||
indio_dev->info = &ms5637_info;
|
indio_dev->info = &ms5637_info;
|
||||||
indio_dev->name = id->name;
|
indio_dev->name = data->name;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->channels = ms5637_channels;
|
indio_dev->channels = ms5637_channels;
|
||||||
indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
|
indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
|
||||||
@@ -170,20 +195,44 @@ static int ms5637_probe(struct i2c_client *client,
|
|||||||
return devm_iio_device_register(&client->dev, indio_dev);
|
return devm_iio_device_register(&client->dev, indio_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct ms_tp_hw_data ms5637_hw_data = {
|
||||||
|
.prom_len = 7,
|
||||||
|
.max_res_index = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct ms_tp_hw_data ms5803_hw_data = {
|
||||||
|
.prom_len = 8,
|
||||||
|
.max_res_index = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct ms_tp_data ms5637_data = { .name = "ms5637", .hw = &ms5637_hw_data };
|
||||||
|
|
||||||
|
static const struct ms_tp_data ms5803_data = { .name = "ms5803", .hw = &ms5803_hw_data };
|
||||||
|
|
||||||
|
static const struct ms_tp_data ms5805_data = { .name = "ms5805", .hw = &ms5637_hw_data };
|
||||||
|
|
||||||
|
static const struct ms_tp_data ms5837_data = { .name = "ms5837", .hw = &ms5637_hw_data };
|
||||||
|
|
||||||
|
static const struct ms_tp_data ms8607_data = {
|
||||||
|
.name = "ms8607-temppressure",
|
||||||
|
.hw = &ms5637_hw_data,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct i2c_device_id ms5637_id[] = {
|
static const struct i2c_device_id ms5637_id[] = {
|
||||||
{"ms5637", 0},
|
{"ms5637", (kernel_ulong_t)&ms5637_data },
|
||||||
{"ms5805", 0},
|
{"ms5805", (kernel_ulong_t)&ms5805_data },
|
||||||
{"ms5837", 0},
|
{"ms5837", (kernel_ulong_t)&ms5837_data },
|
||||||
{"ms8607-temppressure", 0},
|
{"ms8607-temppressure", (kernel_ulong_t)&ms8607_data },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, ms5637_id);
|
MODULE_DEVICE_TABLE(i2c, ms5637_id);
|
||||||
|
|
||||||
static const struct of_device_id ms5637_of_match[] = {
|
static const struct of_device_id ms5637_of_match[] = {
|
||||||
{ .compatible = "meas,ms5637", },
|
{ .compatible = "meas,ms5637", .data = &ms5637_data },
|
||||||
{ .compatible = "meas,ms5805", },
|
{ .compatible = "meas,ms5803", .data = &ms5803_data },
|
||||||
{ .compatible = "meas,ms5837", },
|
{ .compatible = "meas,ms5805", .data = &ms5805_data },
|
||||||
{ .compatible = "meas,ms8607-temppressure", },
|
{ .compatible = "meas,ms5837", .data = &ms5837_data },
|
||||||
|
{ .compatible = "meas,ms8607-temppressure", .data = &ms8607_data },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, ms5637_of_match);
|
MODULE_DEVICE_TABLE(of, ms5637_of_match);
|
||||||
|
@@ -933,7 +933,7 @@ static int __init ashmem_init(void)
|
|||||||
|
|
||||||
ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
|
ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
|
||||||
sizeof(struct ashmem_range),
|
sizeof(struct ashmem_range),
|
||||||
0, 0, NULL);
|
0, SLAB_RECLAIM_ACCOUNT, NULL);
|
||||||
if (!ashmem_range_cachep) {
|
if (!ashmem_range_cachep) {
|
||||||
pr_err("failed to create slab cache\n");
|
pr_err("failed to create slab cache\n");
|
||||||
goto out_free1;
|
goto out_free1;
|
||||||
|
@@ -3,7 +3,10 @@ config STAGING_BOARD
|
|||||||
bool "Staging Board Support"
|
bool "Staging Board Support"
|
||||||
depends on OF_ADDRESS && OF_IRQ && CLKDEV_LOOKUP
|
depends on OF_ADDRESS && OF_IRQ && CLKDEV_LOOKUP
|
||||||
help
|
help
|
||||||
Select to enable per-board staging support code.
|
Staging board base is to support continuous upstream
|
||||||
|
in-tree development and integration of platform devices.
|
||||||
If in doubt, say N here.
|
|
||||||
|
|
||||||
|
Helps developers integrate devices as platform devices for
|
||||||
|
device drivers that only provide platform device bindings.
|
||||||
|
This in turn allows for incremental development of both
|
||||||
|
hardware feature support and DT binding work in parallel.
|
||||||
|
@@ -2,7 +2,8 @@ TODO:
|
|||||||
- support for fractional multiplier
|
- support for fractional multiplier
|
||||||
- support for fractional divider (output 0 only)
|
- support for fractional divider (output 0 only)
|
||||||
- support for set_rate() operations (may benefit from Stephen Boyd's
|
- support for set_rate() operations (may benefit from Stephen Boyd's
|
||||||
refactoring of the clk primitives: https://lkml.org/lkml/2014/9/5/766)
|
refactoring of the clk primitives:
|
||||||
|
https://lore.kernel.org/lkml/1409957256-23729-1-git-send-email-sboyd@codeaurora.org)
|
||||||
- review arithmetic
|
- review arithmetic
|
||||||
- overflow after multiplication?
|
- overflow after multiplication?
|
||||||
- maximize accuracy before divisions
|
- maximize accuracy before divisions
|
||||||
|
@@ -939,8 +939,8 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
|
|||||||
/* fill devinfo structure */
|
/* fill devinfo structure */
|
||||||
devinfo.version_code = COMEDI_VERSION_CODE;
|
devinfo.version_code = COMEDI_VERSION_CODE;
|
||||||
devinfo.n_subdevs = dev->n_subdevices;
|
devinfo.n_subdevs = dev->n_subdevices;
|
||||||
strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
|
strscpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
|
||||||
strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
|
strscpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
|
||||||
|
|
||||||
s = comedi_file_read_subdevice(file);
|
s = comedi_file_read_subdevice(file);
|
||||||
if (s)
|
if (s)
|
||||||
|
@@ -19,14 +19,15 @@
|
|||||||
* PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
|
* PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
|
||||||
* PCI-7434 (adl_pci7434)
|
* PCI-7434 (adl_pci7434)
|
||||||
* Author: H Hartley Sweeten <hsweeten@visionengravers.com>
|
* Author: H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||||
* Updated: Thu, 02 Aug 2012 14:27:46 -0700
|
* Updated: Fri, 20 Nov 2020 14:49:36 +0000
|
||||||
* Status: untested
|
* Status: works (tested on PCI-7230)
|
||||||
*
|
*
|
||||||
* One or two subdevices are setup by this driver depending on
|
* One or two subdevices are setup by this driver depending on
|
||||||
* the number of digital inputs and/or outputs provided by the
|
* the number of digital inputs and/or outputs provided by the
|
||||||
* board. Each subdevice has a maximum of 32 channels.
|
* board. Each subdevice has a maximum of 32 channels.
|
||||||
*
|
*
|
||||||
* PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
|
* PCI-7230 - 4 subdevices: 0 - 16 input, 1 - 16 output,
|
||||||
|
* 2 - IRQ_IDI0, 3 - IRQ_IDI1
|
||||||
* PCI-7233 - 1 subdevice: 0 - 32 input
|
* PCI-7233 - 1 subdevice: 0 - 32 input
|
||||||
* PCI-7234 - 1 subdevice: 0 - 32 output
|
* PCI-7234 - 1 subdevice: 0 - 32 output
|
||||||
* PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
|
* PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
|
||||||
@@ -37,8 +38,9 @@
|
|||||||
* interrupt signals on digital input channels 0 and 1. The PCI-7233
|
* interrupt signals on digital input channels 0 and 1. The PCI-7233
|
||||||
* has dual-interrupt sources for change-of-state (COS) on any 16
|
* has dual-interrupt sources for change-of-state (COS) on any 16
|
||||||
* digital input channels of LSB and for COS on any 16 digital input
|
* digital input channels of LSB and for COS on any 16 digital input
|
||||||
* lines of MSB. Interrupts are not currently supported by this
|
* lines of MSB.
|
||||||
* driver.
|
*
|
||||||
|
* Currently, this driver only supports interrupts for PCI-7230.
|
||||||
*
|
*
|
||||||
* Configuration Options: not applicable, uses comedi PCI auto config
|
* Configuration Options: not applicable, uses comedi PCI auto config
|
||||||
*/
|
*/
|
||||||
@@ -47,13 +49,22 @@
|
|||||||
|
|
||||||
#include "../comedi_pci.h"
|
#include "../comedi_pci.h"
|
||||||
|
|
||||||
|
#include "plx9052.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register I/O map (32-bit access only)
|
* Register I/O map (32-bit access only)
|
||||||
*/
|
*/
|
||||||
#define PCI7X3X_DIO_REG 0x00
|
#define PCI7X3X_DIO_REG 0x0000 /* in the DigIO Port area */
|
||||||
#define PCI743X_DIO_REG 0x04
|
#define PCI743X_DIO_REG 0x0004
|
||||||
|
|
||||||
enum apci1516_boardid {
|
#define ADL_PT_CLRIRQ 0x0040 /* in the DigIO Port area */
|
||||||
|
|
||||||
|
#define LINTI1_EN_ACT_IDI0 (PLX9052_INTCSR_LI1ENAB | PLX9052_INTCSR_LI1STAT)
|
||||||
|
#define LINTI2_EN_ACT_IDI1 (PLX9052_INTCSR_LI2ENAB | PLX9052_INTCSR_LI2STAT)
|
||||||
|
#define EN_PCI_LINT2H_LINT1H \
|
||||||
|
(PLX9052_INTCSR_PCIENAB | PLX9052_INTCSR_LI2POL | PLX9052_INTCSR_LI1POL)
|
||||||
|
|
||||||
|
enum adl_pci7x3x_boardid {
|
||||||
BOARD_PCI7230,
|
BOARD_PCI7230,
|
||||||
BOARD_PCI7233,
|
BOARD_PCI7233,
|
||||||
BOARD_PCI7234,
|
BOARD_PCI7234,
|
||||||
@@ -67,14 +78,16 @@ struct adl_pci7x3x_boardinfo {
|
|||||||
int nsubdevs;
|
int nsubdevs;
|
||||||
int di_nchan;
|
int di_nchan;
|
||||||
int do_nchan;
|
int do_nchan;
|
||||||
|
int irq_nchan;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
|
static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
|
||||||
[BOARD_PCI7230] = {
|
[BOARD_PCI7230] = {
|
||||||
.name = "adl_pci7230",
|
.name = "adl_pci7230",
|
||||||
.nsubdevs = 2,
|
.nsubdevs = 4, /* IDI, IDO, IRQ_IDI0, IRQ_IDI1 */
|
||||||
.di_nchan = 16,
|
.di_nchan = 16,
|
||||||
.do_nchan = 16,
|
.do_nchan = 16,
|
||||||
|
.irq_nchan = 2,
|
||||||
},
|
},
|
||||||
[BOARD_PCI7233] = {
|
[BOARD_PCI7233] = {
|
||||||
.name = "adl_pci7233",
|
.name = "adl_pci7233",
|
||||||
@@ -104,6 +117,178 @@ static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct adl_pci7x3x_dev_private_data {
|
||||||
|
unsigned long lcr_io_base;
|
||||||
|
unsigned int int_ctrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct adl_pci7x3x_sd_private_data {
|
||||||
|
spinlock_t subd_slock; /* spin-lock for cmd_running */
|
||||||
|
unsigned long port_offset;
|
||||||
|
short int cmd_running;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void process_irq(struct comedi_device *dev, unsigned int subdev,
|
||||||
|
unsigned short intcsr)
|
||||||
|
{
|
||||||
|
struct comedi_subdevice *s = &dev->subdevices[subdev];
|
||||||
|
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
|
||||||
|
unsigned long reg = sd_priv->port_offset;
|
||||||
|
struct comedi_async *async_p = s->async;
|
||||||
|
|
||||||
|
if (async_p) {
|
||||||
|
unsigned short val = inw(dev->iobase + reg);
|
||||||
|
|
||||||
|
spin_lock(&sd_priv->subd_slock);
|
||||||
|
if (sd_priv->cmd_running)
|
||||||
|
comedi_buf_write_samples(s, &val, 1);
|
||||||
|
spin_unlock(&sd_priv->subd_slock);
|
||||||
|
comedi_handle_events(dev, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t adl_pci7x3x_interrupt(int irq, void *p_device)
|
||||||
|
{
|
||||||
|
struct comedi_device *dev = p_device;
|
||||||
|
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
|
||||||
|
unsigned long cpu_flags;
|
||||||
|
unsigned int intcsr;
|
||||||
|
bool li1stat, li2stat;
|
||||||
|
|
||||||
|
if (!dev->attached) {
|
||||||
|
/* Ignore interrupt before device fully attached. */
|
||||||
|
/* Might not even have allocated subdevices yet! */
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we are source of interrupt */
|
||||||
|
spin_lock_irqsave(&dev->spinlock, cpu_flags);
|
||||||
|
intcsr = inl(dev_private->lcr_io_base + PLX9052_INTCSR);
|
||||||
|
li1stat = (intcsr & LINTI1_EN_ACT_IDI0) == LINTI1_EN_ACT_IDI0;
|
||||||
|
li2stat = (intcsr & LINTI2_EN_ACT_IDI1) == LINTI2_EN_ACT_IDI1;
|
||||||
|
if (li1stat || li2stat) {
|
||||||
|
/* clear all current interrupt flags */
|
||||||
|
/* Fixme: Reset all 2 Int Flags */
|
||||||
|
outb(0x00, dev->iobase + ADL_PT_CLRIRQ);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
|
||||||
|
/* SubDev 2, 3 = Isolated DigIn , on "SCSI2" jack!*/
|
||||||
|
|
||||||
|
if (li1stat) /* 0x0005 LINTi1 is Enabled && IDI0 is 1 */
|
||||||
|
process_irq(dev, 2, intcsr);
|
||||||
|
|
||||||
|
if (li2stat) /* 0x0028 LINTi2 is Enabled && IDI1 is 1 */
|
||||||
|
process_irq(dev, 3, intcsr);
|
||||||
|
|
||||||
|
return IRQ_RETVAL(li1stat || li2stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adl_pci7x3x_asy_cmdtest(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s,
|
||||||
|
struct comedi_cmd *cmd)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Step 1 : check if triggers are trivially valid */
|
||||||
|
|
||||||
|
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Step 2a : make sure trigger sources are unique */
|
||||||
|
/* Step 2b : and mutually compatible */
|
||||||
|
|
||||||
|
/* Step 3: check if arguments are trivially valid */
|
||||||
|
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||||
|
cmd->chanlist_len);
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
/* Step 4: fix up any arguments */
|
||||||
|
|
||||||
|
/* Step 5: check channel list if it exists */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adl_pci7x3x_asy_cmd(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s)
|
||||||
|
{
|
||||||
|
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
|
||||||
|
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
|
||||||
|
unsigned long cpu_flags;
|
||||||
|
unsigned int int_enab;
|
||||||
|
|
||||||
|
if (s->index == 2) {
|
||||||
|
/* enable LINTi1 == IDI sdi[0] Ch 0 IRQ ActHigh */
|
||||||
|
int_enab = PLX9052_INTCSR_LI1ENAB;
|
||||||
|
} else {
|
||||||
|
/* enable LINTi2 == IDI sdi[0] Ch 1 IRQ ActHigh */
|
||||||
|
int_enab = PLX9052_INTCSR_LI2ENAB;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->spinlock, cpu_flags);
|
||||||
|
dev_private->int_ctrl |= int_enab;
|
||||||
|
outl(dev_private->int_ctrl, dev_private->lcr_io_base + PLX9052_INTCSR);
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
sd_priv->cmd_running = 1;
|
||||||
|
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adl_pci7x3x_asy_cancel(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s)
|
||||||
|
{
|
||||||
|
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
|
||||||
|
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
|
||||||
|
unsigned long cpu_flags;
|
||||||
|
unsigned int int_enab;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
sd_priv->cmd_running = 0;
|
||||||
|
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
/* disable Interrupts */
|
||||||
|
if (s->index == 2)
|
||||||
|
int_enab = PLX9052_INTCSR_LI1ENAB;
|
||||||
|
else
|
||||||
|
int_enab = PLX9052_INTCSR_LI2ENAB;
|
||||||
|
spin_lock_irqsave(&dev->spinlock, cpu_flags);
|
||||||
|
dev_private->int_ctrl &= ~int_enab;
|
||||||
|
outl(dev_private->int_ctrl, dev_private->lcr_io_base + PLX9052_INTCSR);
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* same as _di_insn_bits because the IRQ-pins are the DI-ports */
|
||||||
|
static int adl_pci7x3x_dirq_insn_bits(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s,
|
||||||
|
struct comedi_insn *insn,
|
||||||
|
unsigned int *data)
|
||||||
|
{
|
||||||
|
struct adl_pci7x3x_sd_private_data *sd_priv = s->private;
|
||||||
|
unsigned long reg = (unsigned long)sd_priv->port_offset;
|
||||||
|
|
||||||
|
data[1] = inl(dev->iobase + reg);
|
||||||
|
|
||||||
|
return insn->n;
|
||||||
|
}
|
||||||
|
|
||||||
static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
|
static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
|
||||||
struct comedi_subdevice *s,
|
struct comedi_subdevice *s,
|
||||||
struct comedi_insn *insn,
|
struct comedi_insn *insn,
|
||||||
@@ -143,15 +328,28 @@ static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
|
|||||||
return insn->n;
|
return insn->n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int adl_pci7x3x_reset(struct comedi_device *dev)
|
||||||
|
{
|
||||||
|
struct adl_pci7x3x_dev_private_data *dev_private = dev->private;
|
||||||
|
|
||||||
|
/* disable Interrupts */
|
||||||
|
dev_private->int_ctrl = 0x00; /* Disable PCI + LINTi2 + LINTi1 */
|
||||||
|
outl(dev_private->int_ctrl, dev_private->lcr_io_base + PLX9052_INTCSR);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
|
static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
|
||||||
unsigned long context)
|
unsigned long context)
|
||||||
{
|
{
|
||||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||||
const struct adl_pci7x3x_boardinfo *board = NULL;
|
const struct adl_pci7x3x_boardinfo *board = NULL;
|
||||||
struct comedi_subdevice *s;
|
struct comedi_subdevice *s;
|
||||||
|
struct adl_pci7x3x_dev_private_data *dev_private;
|
||||||
int subdev;
|
int subdev;
|
||||||
int nchan;
|
int nchan;
|
||||||
int ret;
|
int ret;
|
||||||
|
int ic;
|
||||||
|
|
||||||
if (context < ARRAY_SIZE(adl_pci7x3x_boards))
|
if (context < ARRAY_SIZE(adl_pci7x3x_boards))
|
||||||
board = &adl_pci7x3x_boards[context];
|
board = &adl_pci7x3x_boards[context];
|
||||||
@@ -160,10 +358,34 @@ static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
|
|||||||
dev->board_ptr = board;
|
dev->board_ptr = board;
|
||||||
dev->board_name = board->name;
|
dev->board_name = board->name;
|
||||||
|
|
||||||
|
dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
|
||||||
|
if (!dev_private)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = comedi_pci_enable(dev);
|
ret = comedi_pci_enable(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
dev->iobase = pci_resource_start(pcidev, 2);
|
dev->iobase = pci_resource_start(pcidev, 2);
|
||||||
|
dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
|
||||||
|
|
||||||
|
adl_pci7x3x_reset(dev);
|
||||||
|
|
||||||
|
if (board->irq_nchan) {
|
||||||
|
/* discard all evtl. old IRQs */
|
||||||
|
outb(0x00, dev->iobase + ADL_PT_CLRIRQ);
|
||||||
|
|
||||||
|
if (pcidev->irq) {
|
||||||
|
ret = request_irq(pcidev->irq, adl_pci7x3x_interrupt,
|
||||||
|
IRQF_SHARED, dev->board_name, dev);
|
||||||
|
if (ret == 0) {
|
||||||
|
dev->irq = pcidev->irq;
|
||||||
|
/* 0x52 PCI + IDI Ch 1 Ch 0 IRQ Off ActHigh */
|
||||||
|
dev_private->int_ctrl = EN_PCI_LINT2H_LINT1H;
|
||||||
|
outl(dev_private->int_ctrl,
|
||||||
|
dev_private->lcr_io_base + PLX9052_INTCSR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
|
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -237,14 +459,56 @@ static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (ic = 0; ic < board->irq_nchan; ++ic) {
|
||||||
|
struct adl_pci7x3x_sd_private_data *sd_priv;
|
||||||
|
|
||||||
|
nchan = 1;
|
||||||
|
|
||||||
|
s = &dev->subdevices[subdev];
|
||||||
|
/* Isolated digital inputs 0 or 1 */
|
||||||
|
s->type = COMEDI_SUBD_DI;
|
||||||
|
s->subdev_flags = SDF_READABLE;
|
||||||
|
s->n_chan = nchan;
|
||||||
|
s->maxdata = 1;
|
||||||
|
s->insn_bits = adl_pci7x3x_dirq_insn_bits;
|
||||||
|
s->range_table = &range_digital;
|
||||||
|
|
||||||
|
sd_priv = comedi_alloc_spriv(s, sizeof(*sd_priv));
|
||||||
|
if (!sd_priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(&sd_priv->subd_slock);
|
||||||
|
sd_priv->port_offset = PCI7X3X_DIO_REG;
|
||||||
|
sd_priv->cmd_running = 0;
|
||||||
|
|
||||||
|
if (dev->irq) {
|
||||||
|
dev->read_subdev = s;
|
||||||
|
s->type = COMEDI_SUBD_DI;
|
||||||
|
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
|
||||||
|
s->len_chanlist = 1;
|
||||||
|
s->do_cmdtest = adl_pci7x3x_asy_cmdtest;
|
||||||
|
s->do_cmd = adl_pci7x3x_asy_cmd;
|
||||||
|
s->cancel = adl_pci7x3x_asy_cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
subdev++;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void adl_pci7x3x_detach(struct comedi_device *dev)
|
||||||
|
{
|
||||||
|
if (dev->iobase)
|
||||||
|
adl_pci7x3x_reset(dev);
|
||||||
|
comedi_pci_detach(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static struct comedi_driver adl_pci7x3x_driver = {
|
static struct comedi_driver adl_pci7x3x_driver = {
|
||||||
.driver_name = "adl_pci7x3x",
|
.driver_name = "adl_pci7x3x",
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.auto_attach = adl_pci7x3x_auto_attach,
|
.auto_attach = adl_pci7x3x_auto_attach,
|
||||||
.detach = comedi_pci_detach,
|
.detach = adl_pci7x3x_detach,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
|
static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
|
||||||
|
@@ -34,9 +34,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* PCI-1730, PCI-1733, PCI-1736 interrupt control registers */
|
/* PCI-1730, PCI-1733, PCI-1736 interrupt control registers */
|
||||||
#define PCI173X_INT_EN_REG 0x08 /* R/W: enable/disable */
|
#define PCI173X_INT_EN_REG 0x0008 /* R/W: enable/disable */
|
||||||
#define PCI173X_INT_RF_REG 0x0c /* R/W: falling/rising edge */
|
#define PCI173X_INT_RF_REG 0x000c /* R/W: falling/rising edge */
|
||||||
#define PCI173X_INT_CLR_REG 0x10 /* R/W: clear */
|
#define PCI173X_INT_FLAG_REG 0x0010 /* R: status */
|
||||||
|
#define PCI173X_INT_CLR_REG 0x0010 /* W: clear */
|
||||||
|
|
||||||
|
#define PCI173X_INT_IDI0 0x01 /* IDI0 edge occurred */
|
||||||
|
#define PCI173X_INT_IDI1 0x02 /* IDI1 edge occurred */
|
||||||
|
#define PCI173X_INT_DI0 0x04 /* DI0 edge occurred */
|
||||||
|
#define PCI173X_INT_DI1 0x08 /* DI1 edge occurred */
|
||||||
|
|
||||||
/* PCI-1739U, PCI-1750, PCI1751 interrupt control registers */
|
/* PCI-1739U, PCI-1750, PCI1751 interrupt control registers */
|
||||||
#define PCI1750_INT_REG 0x20 /* R/W: status/control */
|
#define PCI1750_INT_REG 0x20 /* R/W: status/control */
|
||||||
@@ -63,6 +69,7 @@
|
|||||||
#define PCI_DIO_MAX_DI_SUBDEVS 2 /* 2 x 8/16/32 input channels max */
|
#define PCI_DIO_MAX_DI_SUBDEVS 2 /* 2 x 8/16/32 input channels max */
|
||||||
#define PCI_DIO_MAX_DO_SUBDEVS 2 /* 2 x 8/16/32 output channels max */
|
#define PCI_DIO_MAX_DO_SUBDEVS 2 /* 2 x 8/16/32 output channels max */
|
||||||
#define PCI_DIO_MAX_DIO_SUBDEVG 2 /* 2 x any number of 8255 devices max */
|
#define PCI_DIO_MAX_DIO_SUBDEVG 2 /* 2 x any number of 8255 devices max */
|
||||||
|
#define PCI_DIO_MAX_IRQ_SUBDEVS 4 /* 4 x 1 input IRQ channels max */
|
||||||
|
|
||||||
enum pci_dio_boardid {
|
enum pci_dio_boardid {
|
||||||
TYPE_PCI1730,
|
TYPE_PCI1730,
|
||||||
@@ -84,7 +91,12 @@ enum pci_dio_boardid {
|
|||||||
|
|
||||||
struct diosubd_data {
|
struct diosubd_data {
|
||||||
int chans; /* num of chans or 8255 devices */
|
int chans; /* num of chans or 8255 devices */
|
||||||
unsigned long addr; /* PCI address ofset */
|
unsigned long addr; /* PCI address offset */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dio_irq_subd_data {
|
||||||
|
unsigned short int_en; /* interrupt enable/status bit */
|
||||||
|
unsigned long addr; /* PCI address offset */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dio_boardtype {
|
struct dio_boardtype {
|
||||||
@@ -93,6 +105,7 @@ struct dio_boardtype {
|
|||||||
struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
|
struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
|
||||||
struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
|
struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
|
||||||
struct diosubd_data sdio[PCI_DIO_MAX_DIO_SUBDEVG];
|
struct diosubd_data sdio[PCI_DIO_MAX_DIO_SUBDEVG];
|
||||||
|
struct dio_irq_subd_data sdirq[PCI_DIO_MAX_IRQ_SUBDEVS];
|
||||||
unsigned long id_reg;
|
unsigned long id_reg;
|
||||||
unsigned long timer_regbase;
|
unsigned long timer_regbase;
|
||||||
unsigned int is_16bit:1;
|
unsigned int is_16bit:1;
|
||||||
@@ -101,12 +114,17 @@ struct dio_boardtype {
|
|||||||
static const struct dio_boardtype boardtypes[] = {
|
static const struct dio_boardtype boardtypes[] = {
|
||||||
[TYPE_PCI1730] = {
|
[TYPE_PCI1730] = {
|
||||||
.name = "pci1730",
|
.name = "pci1730",
|
||||||
.nsubdevs = 5,
|
/* DI, IDI, DO, IDO, ID, IRQ_DI0, IRQ_DI1, IRQ_IDI0, IRQ_IDI1 */
|
||||||
|
.nsubdevs = 9,
|
||||||
.sdi[0] = { 16, 0x02, }, /* DI 0-15 */
|
.sdi[0] = { 16, 0x02, }, /* DI 0-15 */
|
||||||
.sdi[1] = { 16, 0x00, }, /* ISO DI 0-15 */
|
.sdi[1] = { 16, 0x00, }, /* ISO DI 0-15 */
|
||||||
.sdo[0] = { 16, 0x02, }, /* DO 0-15 */
|
.sdo[0] = { 16, 0x02, }, /* DO 0-15 */
|
||||||
.sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
|
.sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
|
||||||
.id_reg = 0x04,
|
.id_reg = 0x04,
|
||||||
|
.sdirq[0] = { PCI173X_INT_DI0, 0x02, }, /* DI 0 */
|
||||||
|
.sdirq[1] = { PCI173X_INT_DI1, 0x02, }, /* DI 1 */
|
||||||
|
.sdirq[2] = { PCI173X_INT_IDI0, 0x00, }, /* ISO DI 0 */
|
||||||
|
.sdirq[3] = { PCI173X_INT_IDI1, 0x00, }, /* ISO DI 1 */
|
||||||
},
|
},
|
||||||
[TYPE_PCI1733] = {
|
[TYPE_PCI1733] = {
|
||||||
.name = "pci1733",
|
.name = "pci1733",
|
||||||
@@ -205,6 +223,188 @@ static const struct dio_boardtype boardtypes[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pci_dio_dev_private_data {
|
||||||
|
int boardtype;
|
||||||
|
int irq_subd;
|
||||||
|
unsigned short int_ctrl;
|
||||||
|
unsigned short int_rf;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pci_dio_sd_private_data {
|
||||||
|
spinlock_t subd_slock; /* spin-lock for cmd_running */
|
||||||
|
unsigned long port_offset;
|
||||||
|
short int cmd_running;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void process_irq(struct comedi_device *dev, unsigned int subdev,
|
||||||
|
unsigned char irqflags)
|
||||||
|
{
|
||||||
|
struct comedi_subdevice *s = &dev->subdevices[subdev];
|
||||||
|
struct pci_dio_sd_private_data *sd_priv = s->private;
|
||||||
|
unsigned long reg = sd_priv->port_offset;
|
||||||
|
struct comedi_async *async_p = s->async;
|
||||||
|
|
||||||
|
if (async_p) {
|
||||||
|
unsigned short val = inw(dev->iobase + reg);
|
||||||
|
|
||||||
|
spin_lock(&sd_priv->subd_slock);
|
||||||
|
if (sd_priv->cmd_running)
|
||||||
|
comedi_buf_write_samples(s, &val, 1);
|
||||||
|
spin_unlock(&sd_priv->subd_slock);
|
||||||
|
comedi_handle_events(dev, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t pci_dio_interrupt(int irq, void *p_device)
|
||||||
|
{
|
||||||
|
struct comedi_device *dev = p_device;
|
||||||
|
struct pci_dio_dev_private_data *dev_private = dev->private;
|
||||||
|
const struct dio_boardtype *board = dev->board_ptr;
|
||||||
|
unsigned long cpu_flags;
|
||||||
|
unsigned char irqflags;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!dev->attached) {
|
||||||
|
/* Ignore interrupt before device fully attached. */
|
||||||
|
/* Might not even have allocated subdevices yet! */
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we are source of interrupt */
|
||||||
|
spin_lock_irqsave(&dev->spinlock, cpu_flags);
|
||||||
|
irqflags = inb(dev->iobase + PCI173X_INT_FLAG_REG);
|
||||||
|
if (!(irqflags & 0x0F)) {
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear all current interrupt flags */
|
||||||
|
outb(irqflags, dev->iobase + PCI173X_INT_CLR_REG);
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
|
||||||
|
/* check irq subdevice triggers */
|
||||||
|
for (i = 0; i < PCI_DIO_MAX_IRQ_SUBDEVS; i++) {
|
||||||
|
if (irqflags & board->sdirq[i].int_en)
|
||||||
|
process_irq(dev, dev_private->irq_subd + i, irqflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pci_dio_asy_cmdtest(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s,
|
||||||
|
struct comedi_cmd *cmd)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Step 1 : check if triggers are trivially valid */
|
||||||
|
|
||||||
|
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
|
||||||
|
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Step 2a : make sure trigger sources are unique */
|
||||||
|
/* Step 2b : and mutually compatible */
|
||||||
|
|
||||||
|
/* Step 3: check if arguments are trivially valid */
|
||||||
|
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
|
||||||
|
/*
|
||||||
|
* For scan_begin_arg, the trigger number must be 0 and the only
|
||||||
|
* allowed flags are CR_EDGE and CR_INVERT. CR_EDGE is ignored,
|
||||||
|
* CR_INVERT sets the trigger to falling edge.
|
||||||
|
*/
|
||||||
|
if (cmd->scan_begin_arg & ~(CR_EDGE | CR_INVERT)) {
|
||||||
|
cmd->scan_begin_arg &= (CR_EDGE | CR_INVERT);
|
||||||
|
err |= -EINVAL;
|
||||||
|
}
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
|
||||||
|
cmd->chanlist_len);
|
||||||
|
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
/* Step 4: fix up any arguments */
|
||||||
|
|
||||||
|
/* Step 5: check channel list if it exists */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pci_dio_asy_cmd(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s)
|
||||||
|
{
|
||||||
|
struct pci_dio_dev_private_data *dev_private = dev->private;
|
||||||
|
struct pci_dio_sd_private_data *sd_priv = s->private;
|
||||||
|
const struct dio_boardtype *board = dev->board_ptr;
|
||||||
|
struct comedi_cmd *cmd = &s->async->cmd;
|
||||||
|
unsigned long cpu_flags;
|
||||||
|
unsigned short int_en;
|
||||||
|
|
||||||
|
int_en = board->sdirq[s->index - dev_private->irq_subd].int_en;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->spinlock, cpu_flags);
|
||||||
|
if (cmd->scan_begin_arg & CR_INVERT)
|
||||||
|
dev_private->int_rf |= int_en; /* falling edge */
|
||||||
|
else
|
||||||
|
dev_private->int_rf &= ~int_en; /* rising edge */
|
||||||
|
outb(dev_private->int_rf, dev->iobase + PCI173X_INT_RF_REG);
|
||||||
|
dev_private->int_ctrl |= int_en; /* enable interrupt source */
|
||||||
|
outb(dev_private->int_ctrl, dev->iobase + PCI173X_INT_EN_REG);
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
sd_priv->cmd_running = 1;
|
||||||
|
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pci_dio_asy_cancel(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s)
|
||||||
|
{
|
||||||
|
struct pci_dio_dev_private_data *dev_private = dev->private;
|
||||||
|
struct pci_dio_sd_private_data *sd_priv = s->private;
|
||||||
|
const struct dio_boardtype *board = dev->board_ptr;
|
||||||
|
unsigned long cpu_flags;
|
||||||
|
unsigned short int_en;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
sd_priv->cmd_running = 0;
|
||||||
|
spin_unlock_irqrestore(&sd_priv->subd_slock, cpu_flags);
|
||||||
|
|
||||||
|
int_en = board->sdirq[s->index - dev_private->irq_subd].int_en;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev->spinlock, cpu_flags);
|
||||||
|
dev_private->int_ctrl &= ~int_en;
|
||||||
|
outb(dev_private->int_ctrl, dev->iobase + PCI173X_INT_EN_REG);
|
||||||
|
spin_unlock_irqrestore(&dev->spinlock, cpu_flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* same as _insn_bits_di_ because the IRQ-pins are the DI-ports */
|
||||||
|
static int pci_dio_insn_bits_dirq_b(struct comedi_device *dev,
|
||||||
|
struct comedi_subdevice *s,
|
||||||
|
struct comedi_insn *insn,
|
||||||
|
unsigned int *data)
|
||||||
|
{
|
||||||
|
struct pci_dio_sd_private_data *sd_priv = s->private;
|
||||||
|
unsigned long reg = (unsigned long)sd_priv->port_offset;
|
||||||
|
unsigned long iobase = dev->iobase + reg;
|
||||||
|
|
||||||
|
data[1] = inb(iobase);
|
||||||
|
|
||||||
|
return insn->n;
|
||||||
|
}
|
||||||
|
|
||||||
static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
|
static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
|
||||||
struct comedi_subdevice *s,
|
struct comedi_subdevice *s,
|
||||||
struct comedi_insn *insn,
|
struct comedi_insn *insn,
|
||||||
@@ -283,6 +483,7 @@ static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
|
|||||||
|
|
||||||
static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
|
static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
|
||||||
{
|
{
|
||||||
|
struct pci_dio_dev_private_data *dev_private = dev->private;
|
||||||
/* disable channel freeze function on the PCI-1752/1756 boards */
|
/* disable channel freeze function on the PCI-1752/1756 boards */
|
||||||
if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
|
if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
|
||||||
outw(0, dev->iobase + PCI1752_CFC_REG);
|
outw(0, dev->iobase + PCI1752_CFC_REG);
|
||||||
@@ -292,9 +493,13 @@ static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
|
|||||||
case TYPE_PCI1730:
|
case TYPE_PCI1730:
|
||||||
case TYPE_PCI1733:
|
case TYPE_PCI1733:
|
||||||
case TYPE_PCI1736:
|
case TYPE_PCI1736:
|
||||||
outb(0, dev->iobase + PCI173X_INT_EN_REG);
|
dev_private->int_ctrl = 0x00;
|
||||||
|
outb(dev_private->int_ctrl, dev->iobase + PCI173X_INT_EN_REG);
|
||||||
|
/* Reset all 4 Int Flags */
|
||||||
outb(0x0f, dev->iobase + PCI173X_INT_CLR_REG);
|
outb(0x0f, dev->iobase + PCI173X_INT_CLR_REG);
|
||||||
outb(0, dev->iobase + PCI173X_INT_RF_REG);
|
/* Rising Edge => IRQ . On all 4 Pins */
|
||||||
|
dev_private->int_rf = 0x00;
|
||||||
|
outb(dev_private->int_rf, dev->iobase + PCI173X_INT_RF_REG);
|
||||||
break;
|
break;
|
||||||
case TYPE_PCI1739:
|
case TYPE_PCI1739:
|
||||||
case TYPE_PCI1750:
|
case TYPE_PCI1750:
|
||||||
@@ -346,8 +551,8 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||||||
{
|
{
|
||||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||||
const struct dio_boardtype *board = NULL;
|
const struct dio_boardtype *board = NULL;
|
||||||
const struct diosubd_data *d;
|
|
||||||
struct comedi_subdevice *s;
|
struct comedi_subdevice *s;
|
||||||
|
struct pci_dio_dev_private_data *dev_private;
|
||||||
int ret, subdev, i, j;
|
int ret, subdev, i, j;
|
||||||
|
|
||||||
if (context < ARRAY_SIZE(boardtypes))
|
if (context < ARRAY_SIZE(boardtypes))
|
||||||
@@ -357,6 +562,10 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||||||
dev->board_ptr = board;
|
dev->board_ptr = board;
|
||||||
dev->board_name = board->name;
|
dev->board_name = board->name;
|
||||||
|
|
||||||
|
dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
|
||||||
|
if (!dev_private)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = comedi_pci_enable(dev);
|
ret = comedi_pci_enable(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -365,15 +574,25 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||||||
else
|
else
|
||||||
dev->iobase = pci_resource_start(pcidev, 2);
|
dev->iobase = pci_resource_start(pcidev, 2);
|
||||||
|
|
||||||
|
dev_private->boardtype = context;
|
||||||
pci_dio_reset(dev, context);
|
pci_dio_reset(dev, context);
|
||||||
|
|
||||||
|
/* request IRQ if device has irq subdevices */
|
||||||
|
if (board->sdirq[0].int_en && pcidev->irq) {
|
||||||
|
ret = request_irq(pcidev->irq, pci_dio_interrupt, IRQF_SHARED,
|
||||||
|
dev->board_name, dev);
|
||||||
|
if (ret == 0)
|
||||||
|
dev->irq = pcidev->irq;
|
||||||
|
}
|
||||||
|
|
||||||
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
|
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
subdev = 0;
|
subdev = 0;
|
||||||
for (i = 0; i < PCI_DIO_MAX_DI_SUBDEVS; i++) {
|
for (i = 0; i < PCI_DIO_MAX_DI_SUBDEVS; i++) {
|
||||||
d = &board->sdi[i];
|
const struct diosubd_data *d = &board->sdi[i];
|
||||||
|
|
||||||
if (d->chans) {
|
if (d->chans) {
|
||||||
s = &dev->subdevices[subdev++];
|
s = &dev->subdevices[subdev++];
|
||||||
s->type = COMEDI_SUBD_DI;
|
s->type = COMEDI_SUBD_DI;
|
||||||
@@ -385,11 +604,13 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||||||
? pci_dio_insn_bits_di_w
|
? pci_dio_insn_bits_di_w
|
||||||
: pci_dio_insn_bits_di_b;
|
: pci_dio_insn_bits_di_b;
|
||||||
s->private = (void *)d->addr;
|
s->private = (void *)d->addr;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < PCI_DIO_MAX_DO_SUBDEVS; i++) {
|
for (i = 0; i < PCI_DIO_MAX_DO_SUBDEVS; i++) {
|
||||||
d = &board->sdo[i];
|
const struct diosubd_data *d = &board->sdo[i];
|
||||||
|
|
||||||
if (d->chans) {
|
if (d->chans) {
|
||||||
s = &dev->subdevices[subdev++];
|
s = &dev->subdevices[subdev++];
|
||||||
s->type = COMEDI_SUBD_DO;
|
s->type = COMEDI_SUBD_DO;
|
||||||
@@ -420,7 +641,8 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < PCI_DIO_MAX_DIO_SUBDEVG; i++) {
|
for (i = 0; i < PCI_DIO_MAX_DIO_SUBDEVG; i++) {
|
||||||
d = &board->sdio[i];
|
const struct diosubd_data *d = &board->sdio[i];
|
||||||
|
|
||||||
for (j = 0; j < d->chans; j++) {
|
for (j = 0; j < d->chans; j++) {
|
||||||
s = &dev->subdevices[subdev++];
|
s = &dev->subdevices[subdev++];
|
||||||
ret = subdev_8255_init(dev, s, NULL,
|
ret = subdev_8255_init(dev, s, NULL,
|
||||||
@@ -454,14 +676,57 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||||||
comedi_8254_subdevice_init(s, dev->pacer);
|
comedi_8254_subdevice_init(s, dev->pacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev_private->irq_subd = subdev; /* first interrupt subdevice index */
|
||||||
|
for (i = 0; i < PCI_DIO_MAX_IRQ_SUBDEVS; ++i) {
|
||||||
|
struct pci_dio_sd_private_data *sd_priv = NULL;
|
||||||
|
const struct dio_irq_subd_data *d = &board->sdirq[i];
|
||||||
|
|
||||||
|
if (d->int_en) {
|
||||||
|
s = &dev->subdevices[subdev++];
|
||||||
|
s->type = COMEDI_SUBD_DI;
|
||||||
|
s->subdev_flags = SDF_READABLE;
|
||||||
|
s->n_chan = 1;
|
||||||
|
s->maxdata = 1;
|
||||||
|
s->range_table = &range_digital;
|
||||||
|
s->insn_bits = pci_dio_insn_bits_dirq_b;
|
||||||
|
sd_priv = comedi_alloc_spriv(s, sizeof(*sd_priv));
|
||||||
|
if (!sd_priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(&sd_priv->subd_slock);
|
||||||
|
sd_priv->port_offset = d->addr;
|
||||||
|
sd_priv->cmd_running = 0;
|
||||||
|
|
||||||
|
if (dev->irq) {
|
||||||
|
dev->read_subdev = s;
|
||||||
|
s->type = COMEDI_SUBD_DI;
|
||||||
|
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
|
||||||
|
s->len_chanlist = 1;
|
||||||
|
s->do_cmdtest = pci_dio_asy_cmdtest;
|
||||||
|
s->do_cmd = pci_dio_asy_cmd;
|
||||||
|
s->cancel = pci_dio_asy_cancel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pci_dio_detach(struct comedi_device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dio_dev_private_data *dev_private = dev->private;
|
||||||
|
int boardtype = dev_private->boardtype;
|
||||||
|
|
||||||
|
if (dev->iobase)
|
||||||
|
pci_dio_reset(dev, boardtype);
|
||||||
|
comedi_pci_detach(dev);
|
||||||
|
}
|
||||||
|
|
||||||
static struct comedi_driver adv_pci_dio_driver = {
|
static struct comedi_driver adv_pci_dio_driver = {
|
||||||
.driver_name = "adv_pci_dio",
|
.driver_name = "adv_pci_dio",
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.auto_attach = pci_dio_auto_attach,
|
.auto_attach = pci_dio_auto_attach,
|
||||||
.detach = comedi_pci_detach,
|
.detach = pci_dio_detach,
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
|
static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
|
||||||
|
@@ -34,6 +34,9 @@
|
|||||||
#define DRIVER_DESC "EMXX UDC driver"
|
#define DRIVER_DESC "EMXX UDC driver"
|
||||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||||
|
|
||||||
|
static struct gpio_desc *vbus_gpio;
|
||||||
|
static int vbus_irq;
|
||||||
|
|
||||||
static const char driver_name[] = "emxx_udc";
|
static const char driver_name[] = "emxx_udc";
|
||||||
static const char driver_desc[] = DRIVER_DESC;
|
static const char driver_desc[] = DRIVER_DESC;
|
||||||
|
|
||||||
|
@@ -20,8 +20,6 @@
|
|||||||
/* below hacked up for staging integration */
|
/* below hacked up for staging integration */
|
||||||
#define GPIO_VBUS 0 /* GPIO_P153 on KZM9D */
|
#define GPIO_VBUS 0 /* GPIO_P153 on KZM9D */
|
||||||
#define INT_VBUS 0 /* IRQ for GPIO_P153 */
|
#define INT_VBUS 0 /* IRQ for GPIO_P153 */
|
||||||
struct gpio_desc *vbus_gpio;
|
|
||||||
int vbus_irq;
|
|
||||||
|
|
||||||
/*------------ Board dependence(Wait) */
|
/*------------ Board dependence(Wait) */
|
||||||
|
|
||||||
|
@@ -7,9 +7,13 @@
|
|||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <video/mipi_display.h>
|
#include <video/mipi_display.h>
|
||||||
|
|
||||||
#include "fbtft.h"
|
#include "fbtft.h"
|
||||||
@@ -66,6 +70,62 @@ enum st7789v_command {
|
|||||||
#define MADCTL_MX BIT(6) /* bitmask for column address order */
|
#define MADCTL_MX BIT(6) /* bitmask for column address order */
|
||||||
#define MADCTL_MY BIT(7) /* bitmask for page address order */
|
#define MADCTL_MY BIT(7) /* bitmask for page address order */
|
||||||
|
|
||||||
|
/* 60Hz for 16.6ms, configured as 2*16.6ms */
|
||||||
|
#define PANEL_TE_TIMEOUT_MS 33
|
||||||
|
|
||||||
|
static struct completion panel_te; /* completion for panel TE line */
|
||||||
|
static int irq_te; /* Linux IRQ for LCD TE line */
|
||||||
|
|
||||||
|
static irqreturn_t panel_te_handler(int irq, void *data)
|
||||||
|
{
|
||||||
|
complete(&panel_te);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init_tearing_effect_line() - init tearing effect line.
|
||||||
|
* @par: FBTFT parameter object.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
static int init_tearing_effect_line(struct fbtft_par *par)
|
||||||
|
{
|
||||||
|
struct device *dev = par->info->device;
|
||||||
|
struct gpio_desc *te;
|
||||||
|
int rc, irq;
|
||||||
|
|
||||||
|
te = gpiod_get_optional(dev, "te", GPIOD_IN);
|
||||||
|
if (IS_ERR(te))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(te), "Failed to request te GPIO\n");
|
||||||
|
|
||||||
|
/* if te is NULL, indicating no configuration, directly return success */
|
||||||
|
if (!te) {
|
||||||
|
irq_te = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq = gpiod_to_irq(te);
|
||||||
|
|
||||||
|
/* GPIO is locked as an IRQ, we may drop the reference */
|
||||||
|
gpiod_put(te);
|
||||||
|
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
irq_te = irq;
|
||||||
|
init_completion(&panel_te);
|
||||||
|
|
||||||
|
/* The effective state is high and lasts no more than 1000 microseconds */
|
||||||
|
rc = devm_request_irq(dev, irq_te, panel_te_handler,
|
||||||
|
IRQF_TRIGGER_RISING, "TE_GPIO", par);
|
||||||
|
if (rc)
|
||||||
|
return dev_err_probe(dev, rc, "TE IRQ request failed.\n");
|
||||||
|
|
||||||
|
disable_irq_nosync(irq_te);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init_display() - initialize the display controller
|
* init_display() - initialize the display controller
|
||||||
*
|
*
|
||||||
@@ -82,6 +142,12 @@ enum st7789v_command {
|
|||||||
*/
|
*/
|
||||||
static int init_display(struct fbtft_par *par)
|
static int init_display(struct fbtft_par *par)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = init_tearing_effect_line(par);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
/* turn off sleep mode */
|
/* turn off sleep mode */
|
||||||
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
|
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
|
||||||
mdelay(120);
|
mdelay(120);
|
||||||
@@ -137,6 +203,10 @@ static int init_display(struct fbtft_par *par)
|
|||||||
*/
|
*/
|
||||||
write_reg(par, PWCTRL1, 0xA4, 0xA1);
|
write_reg(par, PWCTRL1, 0xA4, 0xA1);
|
||||||
|
|
||||||
|
/* TE line output is off by default when powering on */
|
||||||
|
if (irq_te)
|
||||||
|
write_reg(par, MIPI_DCS_SET_TEAR_ON, 0x00);
|
||||||
|
|
||||||
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
|
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
|
||||||
|
|
||||||
if (HSD20_IPS)
|
if (HSD20_IPS)
|
||||||
@@ -145,6 +215,50 @@ static int init_display(struct fbtft_par *par)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* write_vmem() - write data to display.
|
||||||
|
* @par: FBTFT parameter object.
|
||||||
|
* @offset: offset from screen_buffer.
|
||||||
|
* @len: the length of data to be writte.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, or a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
|
||||||
|
{
|
||||||
|
struct device *dev = par->info->device;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (irq_te) {
|
||||||
|
enable_irq(irq_te);
|
||||||
|
reinit_completion(&panel_te);
|
||||||
|
ret = wait_for_completion_timeout(&panel_te,
|
||||||
|
msecs_to_jiffies(PANEL_TE_TIMEOUT_MS));
|
||||||
|
if (ret == 0)
|
||||||
|
dev_err(dev, "wait panel TE timeout\n");
|
||||||
|
|
||||||
|
disable_irq(irq_te);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (par->pdata->display.buswidth) {
|
||||||
|
case 8:
|
||||||
|
ret = fbtft_write_vmem16_bus8(par, offset, len);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
ret = fbtft_write_vmem16_bus9(par, offset, len);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
ret = fbtft_write_vmem16_bus16(par, offset, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(dev, "Unsupported buswidth %d\n",
|
||||||
|
par->pdata->display.buswidth);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set_var() - apply LCD properties like rotation and BGR mode
|
* set_var() - apply LCD properties like rotation and BGR mode
|
||||||
*
|
*
|
||||||
@@ -259,6 +373,7 @@ static struct fbtft_display display = {
|
|||||||
.gamma = HSD20_IPS_GAMMA,
|
.gamma = HSD20_IPS_GAMMA,
|
||||||
.fbtftops = {
|
.fbtftops = {
|
||||||
.init_display = init_display,
|
.init_display = init_display,
|
||||||
|
.write_vmem = write_vmem,
|
||||||
.set_var = set_var,
|
.set_var = set_var,
|
||||||
.set_gamma = set_gamma,
|
.set_gamma = set_gamma,
|
||||||
.blank = blank,
|
.blank = blank,
|
||||||
|
@@ -185,7 +185,7 @@ static struct attribute *controller_attributes[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute_group controller_attribute_group = {
|
static const struct attribute_group controller_attribute_group = {
|
||||||
.attrs = controller_attributes,
|
.attrs = controller_attributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ static int can_power_is_enabled(struct regulator_dev *rdev)
|
|||||||
return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
|
return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct regulator_ops can_power_ops = {
|
static const struct regulator_ops can_power_ops = {
|
||||||
.is_enabled = can_power_is_enabled,
|
.is_enabled = can_power_is_enabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -38,19 +38,19 @@ static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
|
|||||||
u16 version_major, version_minor;
|
u16 version_major, version_minor;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
|
strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
|
||||||
|
|
||||||
err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
|
err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
|
||||||
&version_major,
|
&version_major,
|
||||||
&version_minor);
|
&version_minor);
|
||||||
if (err)
|
if (err)
|
||||||
strlcpy(drvinfo->fw_version, "N/A",
|
strscpy(drvinfo->fw_version, "N/A",
|
||||||
sizeof(drvinfo->fw_version));
|
sizeof(drvinfo->fw_version));
|
||||||
else
|
else
|
||||||
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
|
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
|
||||||
"%u.%u", version_major, version_minor);
|
"%u.%u", version_major, version_minor);
|
||||||
|
|
||||||
strlcpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
|
strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
|
||||||
sizeof(drvinfo->bus_info));
|
sizeof(drvinfo->bus_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2189,6 +2189,7 @@ static int fwserial_create(struct fw_unit *unit)
|
|||||||
err = fw_core_add_address_handler(&port->rx_handler,
|
err = fw_core_add_address_handler(&port->rx_handler,
|
||||||
&fw_high_memory_region);
|
&fw_high_memory_region);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
tty_port_destroy(&port->port);
|
||||||
kfree(port);
|
kfree(port);
|
||||||
goto free_ports;
|
goto free_ports;
|
||||||
}
|
}
|
||||||
@@ -2271,6 +2272,7 @@ unregister_ttys:
|
|||||||
|
|
||||||
free_ports:
|
free_ports:
|
||||||
for (--i; i >= 0; --i) {
|
for (--i; i >= 0; --i) {
|
||||||
|
fw_core_remove_address_handler(&serial->ports[i]->rx_handler);
|
||||||
tty_port_destroy(&serial->ports[i]->port);
|
tty_port_destroy(&serial->ports[i]->port);
|
||||||
kfree(serial->ports[i]);
|
kfree(serial->ports[i]);
|
||||||
}
|
}
|
||||||
|
@@ -40,10 +40,11 @@ static int gasket_set_event_fd(struct gasket_dev *gasket_dev,
|
|||||||
|
|
||||||
/* Read the size of the page table. */
|
/* Read the size of the page table. */
|
||||||
static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
|
static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
|
||||||
struct gasket_page_table_ioctl __user *argp)
|
struct gasket_page_table_ioctl __user *argp)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct gasket_page_table_ioctl ibuf;
|
struct gasket_page_table_ioctl ibuf;
|
||||||
|
struct gasket_page_table *table;
|
||||||
|
|
||||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -51,8 +52,8 @@ static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
|
|||||||
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
ibuf.size = gasket_page_table_num_entries(
|
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||||
gasket_dev->page_table[ibuf.page_table_index]);
|
ibuf.size = gasket_page_table_num_entries(table);
|
||||||
|
|
||||||
trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
|
trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
|
||||||
ibuf.host_address,
|
ibuf.host_address,
|
||||||
@@ -66,10 +67,11 @@ static int gasket_read_page_table_size(struct gasket_dev *gasket_dev,
|
|||||||
|
|
||||||
/* Read the size of the simple page table. */
|
/* Read the size of the simple page table. */
|
||||||
static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
|
static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
|
||||||
struct gasket_page_table_ioctl __user *argp)
|
struct gasket_page_table_ioctl __user *argp)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct gasket_page_table_ioctl ibuf;
|
struct gasket_page_table_ioctl ibuf;
|
||||||
|
struct gasket_page_table *table;
|
||||||
|
|
||||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -77,8 +79,8 @@ static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
|
|||||||
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
ibuf.size =
|
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||||
gasket_page_table_num_simple_entries(gasket_dev->page_table[ibuf.page_table_index]);
|
ibuf.size = gasket_page_table_num_simple_entries(table);
|
||||||
|
|
||||||
trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
|
trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
|
||||||
ibuf.host_address,
|
ibuf.host_address,
|
||||||
@@ -92,11 +94,12 @@ static int gasket_read_simple_page_table_size(struct gasket_dev *gasket_dev,
|
|||||||
|
|
||||||
/* Set the boundary between the simple and extended page tables. */
|
/* Set the boundary between the simple and extended page tables. */
|
||||||
static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
|
static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
|
||||||
struct gasket_page_table_ioctl __user *argp)
|
struct gasket_page_table_ioctl __user *argp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct gasket_page_table_ioctl ibuf;
|
struct gasket_page_table_ioctl ibuf;
|
||||||
uint max_page_table_size;
|
uint max_page_table_size;
|
||||||
|
struct gasket_page_table *table;
|
||||||
|
|
||||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -107,8 +110,8 @@ static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
|
|||||||
|
|
||||||
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
max_page_table_size = gasket_page_table_max_size(
|
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||||
gasket_dev->page_table[ibuf.page_table_index]);
|
max_page_table_size = gasket_page_table_max_size(table);
|
||||||
|
|
||||||
if (ibuf.size > max_page_table_size) {
|
if (ibuf.size > max_page_table_size) {
|
||||||
dev_dbg(gasket_dev->dev,
|
dev_dbg(gasket_dev->dev,
|
||||||
@@ -119,8 +122,7 @@ static int gasket_partition_page_table(struct gasket_dev *gasket_dev,
|
|||||||
|
|
||||||
mutex_lock(&gasket_dev->mutex);
|
mutex_lock(&gasket_dev->mutex);
|
||||||
|
|
||||||
ret = gasket_page_table_partition(
|
ret = gasket_page_table_partition(table, ibuf.size);
|
||||||
gasket_dev->page_table[ibuf.page_table_index], ibuf.size);
|
|
||||||
mutex_unlock(&gasket_dev->mutex);
|
mutex_unlock(&gasket_dev->mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -131,6 +133,7 @@ static int gasket_map_buffers(struct gasket_dev *gasket_dev,
|
|||||||
struct gasket_page_table_ioctl __user *argp)
|
struct gasket_page_table_ioctl __user *argp)
|
||||||
{
|
{
|
||||||
struct gasket_page_table_ioctl ibuf;
|
struct gasket_page_table_ioctl ibuf;
|
||||||
|
struct gasket_page_table *table;
|
||||||
|
|
||||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -142,13 +145,12 @@ static int gasket_map_buffers(struct gasket_dev *gasket_dev,
|
|||||||
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[ibuf.page_table_index],
|
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||||
ibuf.host_address,
|
if (gasket_page_table_are_addrs_bad(table, ibuf.host_address,
|
||||||
ibuf.device_address, ibuf.size))
|
ibuf.device_address, ibuf.size))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return gasket_page_table_map(gasket_dev->page_table[ibuf.page_table_index],
|
return gasket_page_table_map(table, ibuf.host_address, ibuf.device_address,
|
||||||
ibuf.host_address, ibuf.device_address,
|
|
||||||
ibuf.size / PAGE_SIZE);
|
ibuf.size / PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +159,7 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
|
|||||||
struct gasket_page_table_ioctl __user *argp)
|
struct gasket_page_table_ioctl __user *argp)
|
||||||
{
|
{
|
||||||
struct gasket_page_table_ioctl ibuf;
|
struct gasket_page_table_ioctl ibuf;
|
||||||
|
struct gasket_page_table *table;
|
||||||
|
|
||||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -168,12 +171,11 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
|
|||||||
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
if (ibuf.page_table_index >= gasket_dev->num_page_tables)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (gasket_page_table_is_dev_addr_bad(gasket_dev->page_table[ibuf.page_table_index],
|
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||||
ibuf.device_address, ibuf.size))
|
if (gasket_page_table_is_dev_addr_bad(table, ibuf.device_address, ibuf.size))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
gasket_page_table_unmap(gasket_dev->page_table[ibuf.page_table_index],
|
gasket_page_table_unmap(table, ibuf.device_address, ibuf.size / PAGE_SIZE);
|
||||||
ibuf.device_address, ibuf.size / PAGE_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -183,7 +185,7 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
|
|||||||
* corresponding memory.
|
* corresponding memory.
|
||||||
*/
|
*/
|
||||||
static int gasket_config_coherent_allocator(struct gasket_dev *gasket_dev,
|
static int gasket_config_coherent_allocator(struct gasket_dev *gasket_dev,
|
||||||
struct gasket_coherent_alloc_config_ioctl __user *argp)
|
struct gasket_coherent_alloc_config_ioctl __user *argp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct gasket_coherent_alloc_config_ioctl ibuf;
|
struct gasket_coherent_alloc_config_ioctl ibuf;
|
||||||
|
@@ -56,20 +56,24 @@ static int gdm_usb_recv(void *priv_dev,
|
|||||||
|
|
||||||
static int request_mac_address(struct lte_udev *udev)
|
static int request_mac_address(struct lte_udev *udev)
|
||||||
{
|
{
|
||||||
u8 buf[16] = {0,};
|
struct hci_packet *hci;
|
||||||
struct hci_packet *hci = (struct hci_packet *)buf;
|
|
||||||
struct usb_device *usbdev = udev->usbdev;
|
struct usb_device *usbdev = udev->usbdev;
|
||||||
int actual;
|
int actual;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
|
hci = kmalloc(struct_size(hci, data, 1), GFP_KERNEL);
|
||||||
|
if (!hci)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
hci->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_GET_INFORMATION);
|
hci->cmd_evt = gdm_cpu_to_dev16(udev->gdm_ed, LTE_GET_INFORMATION);
|
||||||
hci->len = gdm_cpu_to_dev16(udev->gdm_ed, 1);
|
hci->len = gdm_cpu_to_dev16(udev->gdm_ed, 1);
|
||||||
hci->data[0] = MAC_ADDRESS;
|
hci->data[0] = MAC_ADDRESS;
|
||||||
|
|
||||||
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), buf, 5,
|
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), hci, 5,
|
||||||
&actual, 1000);
|
&actual, 1000);
|
||||||
|
|
||||||
udev->request_mac_addr = 1;
|
udev->request_mac_addr = 1;
|
||||||
|
kfree(hci);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -166,7 +166,7 @@ static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
|
|||||||
snprintf(id.name, sizeof(id.name), "%s %s", prefix,
|
snprintf(id.name, sizeof(id.name), "%s %s", prefix,
|
||||||
control->name);
|
control->name);
|
||||||
else
|
else
|
||||||
strlcpy(id.name, control->name, sizeof(id.name));
|
strscpy(id.name, control->name, sizeof(id.name));
|
||||||
id.numid = 0;
|
id.numid = 0;
|
||||||
id.iface = control->iface;
|
id.iface = control->iface;
|
||||||
id.device = control->device;
|
id.device = control->device;
|
||||||
|
@@ -18,8 +18,8 @@ static ssize_t manager_sysfs_add_store(struct kobject *kobj,
|
|||||||
struct gb_audio_manager_module_descriptor desc = { {0} };
|
struct gb_audio_manager_module_descriptor desc = { {0} };
|
||||||
|
|
||||||
int num = sscanf(buf,
|
int num = sscanf(buf,
|
||||||
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s "
|
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF
|
||||||
"vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X",
|
"s vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X",
|
||||||
desc.name, &desc.vid, &desc.pid, &desc.intf_id,
|
desc.name, &desc.vid, &desc.pid, &desc.intf_id,
|
||||||
&desc.ip_devices, &desc.op_devices);
|
&desc.ip_devices, &desc.op_devices);
|
||||||
|
|
||||||
|
@@ -342,7 +342,7 @@ static int gb_audio_probe(struct gb_bundle *bundle,
|
|||||||
/* inform above layer for uevent */
|
/* inform above layer for uevent */
|
||||||
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
|
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
|
||||||
/* prepare for the audio manager */
|
/* prepare for the audio manager */
|
||||||
strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
|
strscpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
|
||||||
desc.vid = 2; /* todo */
|
desc.vid = 2; /* todo */
|
||||||
desc.pid = 3; /* todo */
|
desc.pid = 3; /* todo */
|
||||||
desc.intf_id = gbmodule->dev_id;
|
desc.intf_id = gbmodule->dev_id;
|
||||||
|
@@ -200,7 +200,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
name = gbaudio_map_controlid(module, data->ctl_id,
|
name = gbaudio_map_controlid(module, data->ctl_id,
|
||||||
uinfo->value.enumerated.item);
|
uinfo->value.enumerated.item);
|
||||||
strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE);
|
strscpy(uinfo->value.enumerated.name, name, NAME_SIZE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n",
|
dev_err(comp->dev, "Invalid type: %d for %s:kcontrol\n",
|
||||||
@@ -1047,7 +1047,7 @@ static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Prefix dev_id to widget control_name */
|
/* Prefix dev_id to widget control_name */
|
||||||
strlcpy(temp_name, w->name, NAME_SIZE);
|
strscpy(temp_name, w->name, NAME_SIZE);
|
||||||
snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name);
|
snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name);
|
||||||
|
|
||||||
switch (w->type) {
|
switch (w->type) {
|
||||||
@@ -1169,7 +1169,7 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module,
|
|||||||
}
|
}
|
||||||
control->id = curr->id;
|
control->id = curr->id;
|
||||||
/* Prefix dev_id to widget_name */
|
/* Prefix dev_id to widget_name */
|
||||||
strlcpy(temp_name, curr->name, NAME_SIZE);
|
strscpy(temp_name, curr->name, NAME_SIZE);
|
||||||
snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id,
|
snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id,
|
||||||
temp_name);
|
temp_name);
|
||||||
control->name = curr->name;
|
control->name = curr->name;
|
||||||
|
@@ -221,8 +221,8 @@ static void gb_hid_init_reports(struct gb_hid *ghid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int __gb_hid_get_raw_report(struct hid_device *hid,
|
static int __gb_hid_get_raw_report(struct hid_device *hid,
|
||||||
unsigned char report_number, __u8 *buf, size_t count,
|
unsigned char report_number, __u8 *buf, size_t count,
|
||||||
unsigned char report_type)
|
unsigned char report_type)
|
||||||
{
|
{
|
||||||
struct gb_hid *ghid = hid->driver_data;
|
struct gb_hid *ghid = hid->driver_data;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -254,7 +254,7 @@ static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
|
|||||||
|
|
||||||
ret = gb_hid_set_report(ghid, report_type, report_id, buf, len);
|
ret = gb_hid_set_report(ghid, report_type, report_id, buf, len);
|
||||||
if (report_id && ret >= 0)
|
if (report_id && ret >= 0)
|
||||||
ret++; /* add report_id to the number of transfered bytes */
|
ret++; /* add report_id to the number of transferred bytes */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -290,8 +290,7 @@ static int channel_attr_groups_set(struct gb_channel *channel,
|
|||||||
channel->attrs = kcalloc(size + 1, sizeof(*channel->attrs), GFP_KERNEL);
|
channel->attrs = kcalloc(size + 1, sizeof(*channel->attrs), GFP_KERNEL);
|
||||||
if (!channel->attrs)
|
if (!channel->attrs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
channel->attr_group = kcalloc(1, sizeof(*channel->attr_group),
|
channel->attr_group = kzalloc(sizeof(*channel->attr_group), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!channel->attr_group)
|
if (!channel->attr_group)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
channel->attr_groups = kcalloc(2, sizeof(*channel->attr_groups),
|
channel->attr_groups = kcalloc(2, sizeof(*channel->attr_groups),
|
||||||
|
@@ -449,7 +449,7 @@ static int __gb_power_supply_set_name(char *init_name, char *name, size_t len)
|
|||||||
|
|
||||||
if (!strlen(init_name))
|
if (!strlen(init_name))
|
||||||
init_name = "gb_power_supply";
|
init_name = "gb_power_supply";
|
||||||
strlcpy(name, init_name, len);
|
strscpy(name, init_name, len);
|
||||||
|
|
||||||
while ((ret < len) && (psy = power_supply_get_by_name(name))) {
|
while ((ret < len) && (psy = power_supply_get_by_name(name))) {
|
||||||
power_supply_put(psy);
|
power_supply_put(psy);
|
||||||
|
@@ -455,10 +455,10 @@ static int gb_spi_setup_device(struct gb_spilib *spi, u8 cs)
|
|||||||
dev_type = response.device_type;
|
dev_type = response.device_type;
|
||||||
|
|
||||||
if (dev_type == GB_SPI_SPI_DEV)
|
if (dev_type == GB_SPI_SPI_DEV)
|
||||||
strlcpy(spi_board.modalias, "spidev",
|
strscpy(spi_board.modalias, "spidev",
|
||||||
sizeof(spi_board.modalias));
|
sizeof(spi_board.modalias));
|
||||||
else if (dev_type == GB_SPI_SPI_NOR)
|
else if (dev_type == GB_SPI_SPI_NOR)
|
||||||
strlcpy(spi_board.modalias, "spi-nor",
|
strscpy(spi_board.modalias, "spi-nor",
|
||||||
sizeof(spi_board.modalias));
|
sizeof(spi_board.modalias));
|
||||||
else if (dev_type == GB_SPI_SPI_MODALIAS)
|
else if (dev_type == GB_SPI_SPI_MODALIAS)
|
||||||
memcpy(spi_board.modalias, response.name,
|
memcpy(spi_board.modalias, response.name,
|
||||||
|
@@ -29,6 +29,7 @@ config MFD_HI6421_SPMI
|
|||||||
depends on OF
|
depends on OF
|
||||||
depends on SPMI
|
depends on SPMI
|
||||||
select MFD_CORE
|
select MFD_CORE
|
||||||
|
select REGMAP_SPMI
|
||||||
help
|
help
|
||||||
Add support for HiSilicon Hi6421v600 SPMI PMIC. Hi6421 includes
|
Add support for HiSilicon Hi6421v600 SPMI PMIC. Hi6421 includes
|
||||||
multi-functions, such as regulators, RTC, codec, Coulomb counter,
|
multi-functions, such as regulators, RTC, codec, Coulomb counter,
|
||||||
@@ -44,6 +45,7 @@ config REGULATOR_HI6421V600
|
|||||||
tristate "HiSilicon Hi6421v600 PMIC voltage regulator support"
|
tristate "HiSilicon Hi6421v600 PMIC voltage regulator support"
|
||||||
depends on MFD_HI6421_SPMI && OF
|
depends on MFD_HI6421_SPMI && OF
|
||||||
depends on REGULATOR
|
depends on REGULATOR
|
||||||
|
select REGMAP
|
||||||
help
|
help
|
||||||
This driver provides support for the voltage regulators on
|
This driver provides support for the voltage regulators on
|
||||||
HiSilicon Hi6421v600 PMU / Codec IC.
|
HiSilicon Hi6421v600 PMU / Codec IC.
|
||||||
|
@@ -4,149 +4,105 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2013 Linaro Ltd.
|
* Copyright (c) 2013 Linaro Ltd.
|
||||||
* Copyright (c) 2011 Hisilicon.
|
* Copyright (c) 2011 Hisilicon.
|
||||||
*
|
* Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/mfd/hi6421-spmi-pmic.h>
|
#include <linux/mfd/hi6421-spmi-pmic.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
|
||||||
/* 8-bit register offset in PMIC */
|
enum hi6421_spmi_pmic_irq_list {
|
||||||
#define HISI_MASK_STATE 0xff
|
OTMP = 0,
|
||||||
|
VBUS_CONNECT,
|
||||||
|
VBUS_DISCONNECT,
|
||||||
|
ALARMON_R,
|
||||||
|
HOLD_6S,
|
||||||
|
HOLD_1S,
|
||||||
|
POWERKEY_UP,
|
||||||
|
POWERKEY_DOWN,
|
||||||
|
OCP_SCP_R,
|
||||||
|
COUL_R,
|
||||||
|
SIM0_HPD_R,
|
||||||
|
SIM0_HPD_F,
|
||||||
|
SIM1_HPD_R,
|
||||||
|
SIM1_HPD_F,
|
||||||
|
PMIC_IRQ_LIST_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
#define HISI_IRQ_ARRAY 2
|
#define HISI_IRQ_ARRAY 2
|
||||||
#define HISI_IRQ_NUM (HISI_IRQ_ARRAY * 8)
|
#define HISI_IRQ_NUM (HISI_IRQ_ARRAY * 8)
|
||||||
|
|
||||||
|
#define HISI_IRQ_KEY_NUM 0
|
||||||
|
|
||||||
|
#define HISI_BITS 8
|
||||||
|
#define HISI_IRQ_KEY_VALUE (BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP))
|
||||||
|
#define HISI_MASK GENMASK(HISI_BITS - 1, 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The IRQs are mapped as:
|
||||||
|
*
|
||||||
|
* ====================== ============= ============ =====
|
||||||
|
* IRQ MASK REGISTER IRQ REGISTER BIT
|
||||||
|
* ====================== ============= ============ =====
|
||||||
|
* OTMP 0x0202 0x212 bit 0
|
||||||
|
* VBUS_CONNECT 0x0202 0x212 bit 1
|
||||||
|
* VBUS_DISCONNECT 0x0202 0x212 bit 2
|
||||||
|
* ALARMON_R 0x0202 0x212 bit 3
|
||||||
|
* HOLD_6S 0x0202 0x212 bit 4
|
||||||
|
* HOLD_1S 0x0202 0x212 bit 5
|
||||||
|
* POWERKEY_UP 0x0202 0x212 bit 6
|
||||||
|
* POWERKEY_DOWN 0x0202 0x212 bit 7
|
||||||
|
*
|
||||||
|
* OCP_SCP_R 0x0203 0x213 bit 0
|
||||||
|
* COUL_R 0x0203 0x213 bit 1
|
||||||
|
* SIM0_HPD_R 0x0203 0x213 bit 2
|
||||||
|
* SIM0_HPD_F 0x0203 0x213 bit 3
|
||||||
|
* SIM1_HPD_R 0x0203 0x213 bit 4
|
||||||
|
* SIM1_HPD_F 0x0203 0x213 bit 5
|
||||||
|
* ====================== ============= ============ =====
|
||||||
|
*/
|
||||||
#define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202
|
#define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202
|
||||||
#define SOC_PMIC_IRQ0_ADDR 0x0212
|
#define SOC_PMIC_IRQ0_ADDR 0x0212
|
||||||
|
|
||||||
#define HISI_IRQ_KEY_NUM 0
|
#define IRQ_MASK_REGISTER(irq_data) (SOC_PMIC_IRQ_MASK_0_ADDR + \
|
||||||
#define HISI_IRQ_KEY_VALUE 0xc0
|
(irqd_to_hwirq(irq_data) >> 3))
|
||||||
#define HISI_IRQ_KEY_DOWN 7
|
#define IRQ_MASK_BIT(irq_data) BIT(irqd_to_hwirq(irq_data) & 0x07)
|
||||||
#define HISI_IRQ_KEY_UP 6
|
|
||||||
|
|
||||||
#define HISI_MASK_FIELD 0xFF
|
|
||||||
#define HISI_BITS 8
|
|
||||||
|
|
||||||
/*define the first group interrupt register number*/
|
|
||||||
#define HISI_PMIC_FIRST_GROUP_INT_NUM 2
|
|
||||||
|
|
||||||
static const struct mfd_cell hi6421v600_devs[] = {
|
static const struct mfd_cell hi6421v600_devs[] = {
|
||||||
{ .name = "hi6421v600-regulator", },
|
{ .name = "hi6421v600-regulator", },
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static irqreturn_t hi6421_spmi_irq_handler(int irq, void *priv)
|
||||||
* The PMIC register is only 8-bit.
|
|
||||||
* Hisilicon SoC use hardware to map PMIC register into SoC mapping.
|
|
||||||
* At here, we are accessing SoC register with 32-bit.
|
|
||||||
*/
|
|
||||||
int hi6421_spmi_pmic_read(struct hi6421_spmi_pmic *pmic, int reg)
|
|
||||||
{
|
{
|
||||||
struct spmi_device *pdev;
|
struct hi6421_spmi_pmic *ddata = (struct hi6421_spmi_pmic *)priv;
|
||||||
u8 read_value = 0;
|
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
pdev = to_spmi_device(pmic->dev);
|
|
||||||
if (!pdev) {
|
|
||||||
pr_err("%s: pdev get failed!\n", __func__);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = spmi_ext_register_readl(pdev, reg, &read_value, 1);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("%s: spmi_ext_register_readl failed!\n", __func__);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return read_value;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(hi6421_spmi_pmic_read);
|
|
||||||
|
|
||||||
int hi6421_spmi_pmic_write(struct hi6421_spmi_pmic *pmic, int reg, u32 val)
|
|
||||||
{
|
|
||||||
struct spmi_device *pdev;
|
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
pdev = to_spmi_device(pmic->dev);
|
|
||||||
if (!pdev) {
|
|
||||||
pr_err("%s: pdev get failed!\n", __func__);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = spmi_ext_register_writel(pdev, reg, (unsigned char *)&val, 1);
|
|
||||||
if (ret)
|
|
||||||
pr_err("%s: spmi_ext_register_writel failed!\n", __func__);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(hi6421_spmi_pmic_write);
|
|
||||||
|
|
||||||
int hi6421_spmi_pmic_rmw(struct hi6421_spmi_pmic *pmic, int reg,
|
|
||||||
u32 mask, u32 bits)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
u32 data;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&pmic->lock, flags);
|
|
||||||
data = hi6421_spmi_pmic_read(pmic, reg) & ~mask;
|
|
||||||
data |= mask & bits;
|
|
||||||
ret = hi6421_spmi_pmic_write(pmic, reg, data);
|
|
||||||
spin_unlock_irqrestore(&pmic->lock, flags);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(hi6421_spmi_pmic_rmw);
|
|
||||||
|
|
||||||
static irqreturn_t hi6421_spmi_irq_handler(int irq, void *data)
|
|
||||||
{
|
|
||||||
struct hi6421_spmi_pmic *pmic = (struct hi6421_spmi_pmic *)data;
|
|
||||||
unsigned long pending;
|
unsigned long pending;
|
||||||
|
unsigned int in;
|
||||||
int i, offset;
|
int i, offset;
|
||||||
|
|
||||||
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
|
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
|
||||||
pending = hi6421_spmi_pmic_read(pmic, (i + SOC_PMIC_IRQ0_ADDR));
|
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);
|
||||||
pending &= HISI_MASK_FIELD;
|
pending = HISI_MASK & in;
|
||||||
if (pending != 0)
|
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, pending);
|
||||||
pr_debug("pending[%d]=0x%lx\n\r", i, pending);
|
|
||||||
|
|
||||||
hi6421_spmi_pmic_write(pmic, (i + SOC_PMIC_IRQ0_ADDR), pending);
|
if (i == HISI_IRQ_KEY_NUM &&
|
||||||
|
(pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE) {
|
||||||
/* solve powerkey order */
|
generic_handle_irq(ddata->irqs[POWERKEY_DOWN]);
|
||||||
if ((i == HISI_IRQ_KEY_NUM) &&
|
generic_handle_irq(ddata->irqs[POWERKEY_UP]);
|
||||||
((pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE)) {
|
|
||||||
generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_DOWN]);
|
|
||||||
generic_handle_irq(pmic->irqs[HISI_IRQ_KEY_UP]);
|
|
||||||
pending &= (~HISI_IRQ_KEY_VALUE);
|
pending &= (~HISI_IRQ_KEY_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pending) {
|
if (!pending)
|
||||||
for_each_set_bit(offset, &pending, HISI_BITS)
|
continue;
|
||||||
generic_handle_irq(pmic->irqs[offset + i * HISI_BITS]);
|
|
||||||
}
|
for_each_set_bit(offset, &pending, HISI_BITS)
|
||||||
|
generic_handle_irq(ddata->irqs[offset + i * HISI_BITS]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
@@ -154,34 +110,38 @@ static irqreturn_t hi6421_spmi_irq_handler(int irq, void *data)
|
|||||||
|
|
||||||
static void hi6421_spmi_irq_mask(struct irq_data *d)
|
static void hi6421_spmi_irq_mask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct hi6421_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
|
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
|
||||||
u32 data, offset;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned int data;
|
||||||
|
u32 offset;
|
||||||
|
|
||||||
offset = (irqd_to_hwirq(d) >> 3);
|
offset = IRQ_MASK_REGISTER(d);
|
||||||
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&pmic->lock, flags);
|
spin_lock_irqsave(&ddata->lock, flags);
|
||||||
data = hi6421_spmi_pmic_read(pmic, offset);
|
|
||||||
data |= (1 << (irqd_to_hwirq(d) & 0x07));
|
regmap_read(ddata->regmap, offset, &data);
|
||||||
hi6421_spmi_pmic_write(pmic, offset, data);
|
data |= IRQ_MASK_BIT(d);
|
||||||
spin_unlock_irqrestore(&pmic->lock, flags);
|
regmap_write(ddata->regmap, offset, data);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&ddata->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hi6421_spmi_irq_unmask(struct irq_data *d)
|
static void hi6421_spmi_irq_unmask(struct irq_data *d)
|
||||||
{
|
{
|
||||||
struct hi6421_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
|
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
|
||||||
u32 data, offset;
|
u32 data, offset;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
offset = (irqd_to_hwirq(d) >> 3);
|
offset = (irqd_to_hwirq(d) >> 3);
|
||||||
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
|
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
|
||||||
|
|
||||||
spin_lock_irqsave(&pmic->lock, flags);
|
spin_lock_irqsave(&ddata->lock, flags);
|
||||||
data = hi6421_spmi_pmic_read(pmic, offset);
|
|
||||||
|
regmap_read(ddata->regmap, offset, &data);
|
||||||
data &= ~(1 << (irqd_to_hwirq(d) & 0x07));
|
data &= ~(1 << (irqd_to_hwirq(d) & 0x07));
|
||||||
hi6421_spmi_pmic_write(pmic, offset, data);
|
regmap_write(ddata->regmap, offset, data);
|
||||||
spin_unlock_irqrestore(&pmic->lock, flags);
|
|
||||||
|
spin_unlock_irqrestore(&ddata->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct irq_chip hi6421_spmi_pmu_irqchip = {
|
static struct irq_chip hi6421_spmi_pmu_irqchip = {
|
||||||
@@ -195,11 +155,11 @@ static struct irq_chip hi6421_spmi_pmu_irqchip = {
|
|||||||
static int hi6421_spmi_irq_map(struct irq_domain *d, unsigned int virq,
|
static int hi6421_spmi_irq_map(struct irq_domain *d, unsigned int virq,
|
||||||
irq_hw_number_t hw)
|
irq_hw_number_t hw)
|
||||||
{
|
{
|
||||||
struct hi6421_spmi_pmic *pmic = d->host_data;
|
struct hi6421_spmi_pmic *ddata = d->host_data;
|
||||||
|
|
||||||
irq_set_chip_and_handler_name(virq, &hi6421_spmi_pmu_irqchip,
|
irq_set_chip_and_handler_name(virq, &hi6421_spmi_pmu_irqchip,
|
||||||
handle_simple_irq, "hisi");
|
handle_simple_irq, "hisi");
|
||||||
irq_set_chip_data(virq, pmic);
|
irq_set_chip_data(virq, ddata);
|
||||||
irq_set_irq_type(virq, IRQ_TYPE_NONE);
|
irq_set_irq_type(virq, IRQ_TYPE_NONE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -210,118 +170,111 @@ static const struct irq_domain_ops hi6421_spmi_domain_ops = {
|
|||||||
.xlate = irq_domain_xlate_twocell,
|
.xlate = irq_domain_xlate_twocell,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void hi6421_spmi_pmic_irq_prc(struct hi6421_spmi_pmic *pmic)
|
static void hi6421_spmi_pmic_irq_init(struct hi6421_spmi_pmic *ddata)
|
||||||
{
|
{
|
||||||
int i, pending;
|
int i;
|
||||||
|
unsigned int pending;
|
||||||
|
|
||||||
for (i = 0 ; i < HISI_IRQ_ARRAY; i++)
|
for (i = 0; i < HISI_IRQ_ARRAY; i++)
|
||||||
hi6421_spmi_pmic_write(pmic, SOC_PMIC_IRQ_MASK_0_ADDR + i,
|
regmap_write(ddata->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
|
||||||
HISI_MASK_STATE);
|
HISI_MASK);
|
||||||
|
|
||||||
for (i = 0 ; i < HISI_IRQ_ARRAY; i++) {
|
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
|
||||||
pending = hi6421_spmi_pmic_read(pmic, SOC_PMIC_IRQ0_ADDR + i);
|
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
|
||||||
|
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i,
|
||||||
pr_debug("PMU IRQ address value:irq[0x%x] = 0x%x\n",
|
HISI_MASK);
|
||||||
SOC_PMIC_IRQ0_ADDR + i, pending);
|
|
||||||
hi6421_spmi_pmic_write(pmic, SOC_PMIC_IRQ0_ADDR + i,
|
|
||||||
HISI_MASK_STATE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config regmap_config = {
|
||||||
|
.reg_bits = 16,
|
||||||
|
.val_bits = HISI_BITS,
|
||||||
|
.max_register = 0xffff,
|
||||||
|
.fast_io = true
|
||||||
|
};
|
||||||
|
|
||||||
static int hi6421_spmi_pmic_probe(struct spmi_device *pdev)
|
static int hi6421_spmi_pmic_probe(struct spmi_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct hi6421_spmi_pmic *pmic;
|
struct hi6421_spmi_pmic *ddata;
|
||||||
unsigned int virq;
|
unsigned int virq;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
|
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||||
if (!pmic)
|
if (!ddata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_init(&pmic->lock);
|
ddata->regmap = devm_regmap_init_spmi_ext(pdev, ®map_config);
|
||||||
|
if (IS_ERR(ddata->regmap))
|
||||||
|
return PTR_ERR(ddata->regmap);
|
||||||
|
|
||||||
pmic->dev = dev;
|
spin_lock_init(&ddata->lock);
|
||||||
|
|
||||||
pmic->gpio = of_get_gpio(np, 0);
|
ddata->dev = dev;
|
||||||
if (pmic->gpio < 0)
|
|
||||||
return pmic->gpio;
|
|
||||||
|
|
||||||
if (!gpio_is_valid(pmic->gpio))
|
ddata->gpio = of_get_gpio(np, 0);
|
||||||
|
if (ddata->gpio < 0)
|
||||||
|
return ddata->gpio;
|
||||||
|
|
||||||
|
if (!gpio_is_valid(ddata->gpio))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = devm_gpio_request_one(dev, pmic->gpio, GPIOF_IN, "pmic");
|
ret = devm_gpio_request_one(dev, ddata->gpio, GPIOF_IN, "pmic");
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
|
dev_err(dev, "Failed to request gpio%d\n", ddata->gpio);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pmic->irq = gpio_to_irq(pmic->gpio);
|
ddata->irq = gpio_to_irq(ddata->gpio);
|
||||||
|
|
||||||
hi6421_spmi_pmic_irq_prc(pmic);
|
hi6421_spmi_pmic_irq_init(ddata);
|
||||||
|
|
||||||
pmic->irqs = devm_kzalloc(dev, HISI_IRQ_NUM * sizeof(int), GFP_KERNEL);
|
ddata->irqs = devm_kzalloc(dev, HISI_IRQ_NUM * sizeof(int), GFP_KERNEL);
|
||||||
if (!pmic->irqs) {
|
if (!ddata->irqs)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto irq_malloc;
|
|
||||||
}
|
|
||||||
|
|
||||||
pmic->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0,
|
ddata->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0,
|
||||||
&hi6421_spmi_domain_ops, pmic);
|
&hi6421_spmi_domain_ops, ddata);
|
||||||
if (!pmic->domain) {
|
if (!ddata->domain) {
|
||||||
dev_err(dev, "failed irq domain add simple!\n");
|
dev_err(dev, "Failed to create IRQ domain\n");
|
||||||
ret = -ENODEV;
|
return -ENODEV;
|
||||||
goto irq_malloc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < HISI_IRQ_NUM; i++) {
|
for (i = 0; i < HISI_IRQ_NUM; i++) {
|
||||||
virq = irq_create_mapping(pmic->domain, i);
|
virq = irq_create_mapping(ddata->domain, i);
|
||||||
if (!virq) {
|
if (!virq) {
|
||||||
dev_err(dev, "Failed mapping hwirq\n");
|
dev_err(dev, "Failed to map H/W IRQ\n");
|
||||||
ret = -ENOSPC;
|
return -ENOSPC;
|
||||||
goto irq_malloc;
|
|
||||||
}
|
}
|
||||||
pmic->irqs[i] = virq;
|
ddata->irqs[i] = virq;
|
||||||
dev_dbg(dev, "%s: pmic->irqs[%d] = %d\n",
|
|
||||||
__func__, i, pmic->irqs[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = request_threaded_irq(pmic->irq, hi6421_spmi_irq_handler, NULL,
|
ret = request_threaded_irq(ddata->irq, hi6421_spmi_irq_handler, NULL,
|
||||||
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
|
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
|
||||||
"pmic", pmic);
|
"pmic", ddata);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "could not claim pmic IRQ: error %d\n", ret);
|
dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
|
||||||
goto irq_malloc;
|
ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, pmic);
|
dev_set_drvdata(&pdev->dev, ddata);
|
||||||
|
|
||||||
/*
|
|
||||||
* The logic below will rely that the pmic is already stored at
|
|
||||||
* drvdata.
|
|
||||||
*/
|
|
||||||
dev_dbg(&pdev->dev, "SPMI-PMIC: adding children for %pOF\n",
|
|
||||||
pdev->dev.of_node);
|
|
||||||
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
||||||
hi6421v600_devs, ARRAY_SIZE(hi6421v600_devs),
|
hi6421v600_devs, ARRAY_SIZE(hi6421v600_devs),
|
||||||
NULL, 0, NULL);
|
NULL, 0, NULL);
|
||||||
if (!ret)
|
if (ret < 0)
|
||||||
return 0;
|
dev_err(dev, "Failed to add child devices: %d\n", ret);
|
||||||
|
|
||||||
dev_err(dev, "Failed to add child devices: %d\n", ret);
|
|
||||||
|
|
||||||
irq_malloc:
|
|
||||||
free_irq(pmic->irq, pmic);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hi6421_spmi_pmic_remove(struct spmi_device *pdev)
|
static void hi6421_spmi_pmic_remove(struct spmi_device *pdev)
|
||||||
{
|
{
|
||||||
struct hi6421_spmi_pmic *pmic = dev_get_drvdata(&pdev->dev);
|
struct hi6421_spmi_pmic *ddata = dev_get_drvdata(&pdev->dev);
|
||||||
|
|
||||||
free_irq(pmic->irq, pmic);
|
free_irq(ddata->irq, ddata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id pmic_spmi_id_table[] = {
|
static const struct of_device_id pmic_spmi_id_table[] = {
|
||||||
|
@@ -1,183 +1,148 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
//
|
||||||
* Device driver for regulators in Hisi IC
|
// Device driver for regulators in Hisi IC
|
||||||
*
|
//
|
||||||
* Copyright (c) 2013 Linaro Ltd.
|
// Copyright (c) 2013 Linaro Ltd.
|
||||||
* Copyright (c) 2011 Hisilicon.
|
// Copyright (c) 2011 Hisilicon.
|
||||||
*
|
// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
|
||||||
* Guodong Xu <guodong.xu@linaro.org>
|
//
|
||||||
*
|
// Guodong Xu <guodong.xu@linaro.org>
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/mfd/hi6421-spmi-pmic.h>
|
#include <linux/mfd/hi6421-spmi-pmic.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/machine.h>
|
|
||||||
#include <linux/regulator/of_regulator.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
#include <linux/time.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
|
|
||||||
#define rdev_dbg(rdev, fmt, arg...) \
|
struct hi6421_spmi_reg_info {
|
||||||
pr_debug("%s: %s: " fmt, (rdev)->desc->name, __func__, ##arg)
|
struct regulator_desc desc;
|
||||||
|
|
||||||
struct hi6421v600_regulator {
|
|
||||||
struct regulator_desc rdesc;
|
|
||||||
struct hi6421_spmi_pmic *pmic;
|
struct hi6421_spmi_pmic *pmic;
|
||||||
u32 eco_mode_mask, eco_uA;
|
u8 eco_mode_mask;
|
||||||
|
u32 eco_uA;
|
||||||
|
|
||||||
|
/* Serialize regulator enable logic */
|
||||||
|
struct mutex enable_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_MUTEX(enable_mutex);
|
static const unsigned int ldo3_voltages[] = {
|
||||||
|
1500000, 1550000, 1600000, 1650000,
|
||||||
|
1700000, 1725000, 1750000, 1775000,
|
||||||
|
1800000, 1825000, 1850000, 1875000,
|
||||||
|
1900000, 1925000, 1950000, 2000000
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
static const unsigned int ldo4_voltages[] = {
|
||||||
* helper function to ensure when it returns it is at least 'delay_us'
|
1725000, 1750000, 1775000, 1800000,
|
||||||
* microseconds after 'since'.
|
1825000, 1850000, 1875000, 1900000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ldo9_voltages[] = {
|
||||||
|
1750000, 1800000, 1825000, 2800000,
|
||||||
|
2850000, 2950000, 3000000, 3300000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ldo15_voltages[] = {
|
||||||
|
1800000, 1850000, 2400000, 2600000,
|
||||||
|
2700000, 2850000, 2950000, 3000000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ldo17_voltages[] = {
|
||||||
|
2500000, 2600000, 2700000, 2800000,
|
||||||
|
3000000, 3100000, 3200000, 3300000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ldo34_voltages[] = {
|
||||||
|
2600000, 2700000, 2800000, 2900000,
|
||||||
|
3000000, 3100000, 3200000, 3300000
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HI6421V600_LDO() - specify a LDO power line
|
||||||
|
* @_id: LDO id name string
|
||||||
|
* @vtable: voltage table
|
||||||
|
* @ereg: enable register
|
||||||
|
* @emask: enable mask
|
||||||
|
* @vreg: voltage select register
|
||||||
|
* @odelay: off/on delay time in uS
|
||||||
|
* @etime: enable time in uS
|
||||||
|
* @ecomask: eco mode mask
|
||||||
|
* @ecoamp: eco mode load uppler limit in uA
|
||||||
*/
|
*/
|
||||||
|
#define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \
|
||||||
static int hi6421_spmi_regulator_is_enabled(struct regulator_dev *rdev)
|
odelay, etime, ecomask, ecoamp) \
|
||||||
{
|
[HI6421V600_##_id] = { \
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
.desc = { \
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
.name = #_id, \
|
||||||
u32 reg_val;
|
.of_match = of_match_ptr(#_id), \
|
||||||
|
.regulators_node = of_match_ptr("regulators"), \
|
||||||
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->enable_reg);
|
.ops = &hi6421_spmi_ldo_rops, \
|
||||||
|
.type = REGULATOR_VOLTAGE, \
|
||||||
rdev_dbg(rdev,
|
.id = HI6421V600_##_id, \
|
||||||
"enable_reg=0x%x, val= 0x%x, enable_state=%d\n",
|
.owner = THIS_MODULE, \
|
||||||
rdev->desc->enable_reg,
|
.volt_table = vtable, \
|
||||||
reg_val, (reg_val & rdev->desc->enable_mask));
|
.n_voltages = ARRAY_SIZE(vtable), \
|
||||||
|
.vsel_mask = (1 << (ARRAY_SIZE(vtable) - 1)) - 1, \
|
||||||
return ((reg_val & rdev->desc->enable_mask) != 0);
|
.vsel_reg = vreg, \
|
||||||
}
|
.enable_reg = ereg, \
|
||||||
|
.enable_mask = emask, \
|
||||||
|
.enable_time = etime, \
|
||||||
|
.ramp_delay = etime, \
|
||||||
|
.off_on_delay = odelay, \
|
||||||
|
}, \
|
||||||
|
.eco_mode_mask = ecomask, \
|
||||||
|
.eco_uA = ecoamp, \
|
||||||
|
}
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_enable(struct regulator_dev *rdev)
|
static int hi6421_spmi_regulator_enable(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* cannot enable more than one regulator at one time */
|
/* cannot enable more than one regulator at one time */
|
||||||
mutex_lock(&enable_mutex);
|
mutex_lock(&sreg->enable_mutex);
|
||||||
usleep_range(HISI_REGS_ENA_PROTECT_TIME,
|
|
||||||
HISI_REGS_ENA_PROTECT_TIME + 1000);
|
|
||||||
|
|
||||||
/* set enable register */
|
ret = regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
||||||
rdev_dbg(rdev,
|
rdev->desc->enable_mask,
|
||||||
"off_on_delay=%d us, enable_reg=0x%x, enable_mask=0x%x\n",
|
rdev->desc->enable_mask);
|
||||||
rdev->desc->off_on_delay, rdev->desc->enable_reg,
|
|
||||||
rdev->desc->enable_mask);
|
|
||||||
|
|
||||||
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
|
/* Avoid powering up multiple devices at the same time */
|
||||||
rdev->desc->enable_mask,
|
usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60);
|
||||||
rdev->desc->enable_mask);
|
|
||||||
|
|
||||||
mutex_unlock(&enable_mutex);
|
mutex_unlock(&sreg->enable_mutex);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_disable(struct regulator_dev *rdev)
|
static int hi6421_spmi_regulator_disable(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
||||||
|
|
||||||
/* set enable register to 0 */
|
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
||||||
rdev_dbg(rdev, "enable_reg=0x%x, enable_mask=0x%x\n",
|
rdev->desc->enable_mask, 0);
|
||||||
rdev->desc->enable_reg, rdev->desc->enable_mask);
|
|
||||||
|
|
||||||
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
|
|
||||||
rdev->desc->enable_mask, 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_get_voltage_sel(struct regulator_dev *rdev)
|
|
||||||
{
|
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
|
||||||
u32 reg_val, selector;
|
|
||||||
|
|
||||||
/* get voltage selector */
|
|
||||||
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->vsel_reg);
|
|
||||||
|
|
||||||
selector = (reg_val & rdev->desc->vsel_mask) >> (ffs(rdev->desc->vsel_mask) - 1);
|
|
||||||
|
|
||||||
rdev_dbg(rdev,
|
|
||||||
"vsel_reg=0x%x, value=0x%x, entry=0x%x, voltage=%d mV\n",
|
|
||||||
rdev->desc->vsel_reg, reg_val, selector,
|
|
||||||
rdev->desc->ops->list_voltage(rdev, selector) / 1000);
|
|
||||||
|
|
||||||
return selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_set_voltage_sel(struct regulator_dev *rdev,
|
|
||||||
unsigned int selector)
|
|
||||||
{
|
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
|
||||||
u32 reg_val;
|
|
||||||
|
|
||||||
if (unlikely(selector >= rdev->desc->n_voltages))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
reg_val = selector << (ffs(rdev->desc->vsel_mask) - 1);
|
|
||||||
|
|
||||||
/* set voltage selector */
|
|
||||||
rdev_dbg(rdev,
|
|
||||||
"vsel_reg=0x%x, mask=0x%x, value=0x%x, voltage=%d mV\n",
|
|
||||||
rdev->desc->vsel_reg, rdev->desc->vsel_mask, reg_val,
|
|
||||||
rdev->desc->ops->list_voltage(rdev, selector) / 1000);
|
|
||||||
|
|
||||||
hi6421_spmi_pmic_rmw(pmic, rdev->desc->vsel_reg,
|
|
||||||
rdev->desc->vsel_mask, reg_val);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev)
|
static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
||||||
unsigned int mode;
|
|
||||||
u32 reg_val;
|
u32 reg_val;
|
||||||
|
|
||||||
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->enable_reg);
|
regmap_read(pmic->regmap, rdev->desc->enable_reg, ®_val);
|
||||||
|
|
||||||
if (reg_val & sreg->eco_mode_mask)
|
if (reg_val & sreg->eco_mode_mask)
|
||||||
mode = REGULATOR_MODE_IDLE;
|
return REGULATOR_MODE_IDLE;
|
||||||
else
|
|
||||||
mode = REGULATOR_MODE_NORMAL;
|
|
||||||
|
|
||||||
rdev_dbg(rdev,
|
return REGULATOR_MODE_NORMAL;
|
||||||
"enable_reg=0x%x, eco_mode_mask=0x%x, reg_val=0x%x, %s mode\n",
|
|
||||||
rdev->desc->enable_reg, sreg->eco_mode_mask, reg_val,
|
|
||||||
mode == REGULATOR_MODE_IDLE ? "idle" : "normal");
|
|
||||||
|
|
||||||
return mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
|
static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
|
||||||
unsigned int mode)
|
unsigned int mode)
|
||||||
{
|
{
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
||||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
@@ -192,14 +157,8 @@ static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set mode */
|
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
||||||
rdev_dbg(rdev, "enable_reg=0x%x, eco_mode_mask=0x%x, value=0x%x\n",
|
sreg->eco_mode_mask, val);
|
||||||
rdev->desc->enable_reg, sreg->eco_mode_mask, val);
|
|
||||||
|
|
||||||
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
|
|
||||||
sreg->eco_mode_mask, val);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
@@ -207,199 +166,84 @@ hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev,
|
|||||||
int input_uV, int output_uV,
|
int input_uV, int output_uV,
|
||||||
int load_uA)
|
int load_uA)
|
||||||
{
|
{
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
||||||
|
|
||||||
if (load_uA || ((unsigned int)load_uA > sreg->eco_uA))
|
if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA))
|
||||||
return REGULATOR_MODE_NORMAL;
|
return REGULATOR_MODE_NORMAL;
|
||||||
|
|
||||||
return REGULATOR_MODE_IDLE;
|
return REGULATOR_MODE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi6421_spmi_dt_parse(struct platform_device *pdev,
|
|
||||||
struct hi6421v600_regulator *sreg,
|
|
||||||
struct regulator_desc *rdesc)
|
|
||||||
{
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
unsigned int *v_table;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = of_property_read_u32(np, "reg", &rdesc->enable_reg);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing reg property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = of_property_read_u32(np, "vsel-reg", &rdesc->vsel_reg);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing vsel-reg property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = of_property_read_u32(np, "enable-mask", &rdesc->enable_mask);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing enable-mask property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Not all regulators work on idle mode
|
|
||||||
*/
|
|
||||||
ret = of_property_read_u32(np, "idle-mode-mask", &sreg->eco_mode_mask);
|
|
||||||
if (ret) {
|
|
||||||
dev_dbg(dev, "LDO doesn't support economy mode.\n");
|
|
||||||
sreg->eco_mode_mask = 0;
|
|
||||||
sreg->eco_uA = 0;
|
|
||||||
} else {
|
|
||||||
ret = of_property_read_u32(np, "eco-microamp", &sreg->eco_uA);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing eco-microamp property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse .off-on-delay */
|
|
||||||
ret = of_property_read_u32(np, "off-on-delay-us",
|
|
||||||
&rdesc->off_on_delay);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing off-on-delay-us property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse .enable_time */
|
|
||||||
ret = of_property_read_u32(np, "startup-delay-us",
|
|
||||||
&rdesc->enable_time);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing startup-delay-us property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: are there a better value for this? */
|
|
||||||
rdesc->ramp_delay = rdesc->enable_time;
|
|
||||||
|
|
||||||
/* parse volt_table */
|
|
||||||
|
|
||||||
rdesc->n_voltages = of_property_count_u32_elems(np, "voltage-table");
|
|
||||||
|
|
||||||
v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (unlikely(!v_table))
|
|
||||||
return -ENOMEM;
|
|
||||||
rdesc->volt_table = v_table;
|
|
||||||
|
|
||||||
ret = of_property_read_u32_array(np, "voltage-table",
|
|
||||||
v_table, rdesc->n_voltages);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "missing voltage-table property\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Instead of explicitly requiring a mask for the voltage selector,
|
|
||||||
* as they all start from bit zero (at least on the known LDOs),
|
|
||||||
* just use the number of voltages at the voltage table, getting the
|
|
||||||
* minimal mask that would pick everything.
|
|
||||||
*/
|
|
||||||
rdesc->vsel_mask = (1 << (fls(rdesc->n_voltages) - 1)) - 1;
|
|
||||||
|
|
||||||
dev_dbg(dev, "voltage selector settings: reg: 0x%x, mask: 0x%x\n",
|
|
||||||
rdesc->vsel_reg, rdesc->vsel_mask);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct regulator_ops hi6421_spmi_ldo_rops = {
|
static const struct regulator_ops hi6421_spmi_ldo_rops = {
|
||||||
.is_enabled = hi6421_spmi_regulator_is_enabled,
|
.is_enabled = regulator_is_enabled_regmap,
|
||||||
.enable = hi6421_spmi_regulator_enable,
|
.enable = hi6421_spmi_regulator_enable,
|
||||||
.disable = hi6421_spmi_regulator_disable,
|
.disable = hi6421_spmi_regulator_disable,
|
||||||
.list_voltage = regulator_list_voltage_table,
|
.list_voltage = regulator_list_voltage_table,
|
||||||
.map_voltage = regulator_map_voltage_iterate,
|
.map_voltage = regulator_map_voltage_iterate,
|
||||||
.get_voltage_sel = hi6421_spmi_regulator_get_voltage_sel,
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||||
.set_voltage_sel = hi6421_spmi_regulator_set_voltage_sel,
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||||
.get_mode = hi6421_spmi_regulator_get_mode,
|
.get_mode = hi6421_spmi_regulator_get_mode,
|
||||||
.set_mode = hi6421_spmi_regulator_set_mode,
|
.set_mode = hi6421_spmi_regulator_set_mode,
|
||||||
.get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode,
|
.get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_probe_ldo(struct platform_device *pdev,
|
/* HI6421v600 regulators with known registers */
|
||||||
struct device_node *np,
|
enum hi6421_spmi_regulator_id {
|
||||||
struct hi6421_spmi_pmic *pmic)
|
HI6421V600_LDO3,
|
||||||
{
|
HI6421V600_LDO4,
|
||||||
struct regulation_constraints *constraint;
|
HI6421V600_LDO9,
|
||||||
struct regulator_init_data *initdata;
|
HI6421V600_LDO15,
|
||||||
struct regulator_config config = { };
|
HI6421V600_LDO16,
|
||||||
struct hi6421v600_regulator *sreg;
|
HI6421V600_LDO17,
|
||||||
struct device *dev = &pdev->dev;
|
HI6421V600_LDO33,
|
||||||
struct regulator_desc *rdesc;
|
HI6421V600_LDO34,
|
||||||
struct regulator_dev *rdev;
|
};
|
||||||
const char *supplyname;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
initdata = of_get_regulator_init_data(dev, np, NULL);
|
static struct hi6421_spmi_reg_info regulator_info[] = {
|
||||||
if (!initdata) {
|
HI6421V600_LDO(LDO3, ldo3_voltages,
|
||||||
dev_err(dev, "failed to get regulator data\n");
|
0x16, 0x01, 0x51,
|
||||||
return -EINVAL;
|
20000, 120,
|
||||||
}
|
0, 0),
|
||||||
|
HI6421V600_LDO(LDO4, ldo4_voltages,
|
||||||
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
|
0x17, 0x01, 0x52,
|
||||||
if (!sreg)
|
20000, 120,
|
||||||
return -ENOMEM;
|
0x10, 10000),
|
||||||
|
HI6421V600_LDO(LDO9, ldo9_voltages,
|
||||||
sreg->pmic = pmic;
|
0x1c, 0x01, 0x57,
|
||||||
rdesc = &sreg->rdesc;
|
20000, 360,
|
||||||
|
0x10, 10000),
|
||||||
rdesc->name = initdata->constraints.name;
|
HI6421V600_LDO(LDO15, ldo15_voltages,
|
||||||
rdesc->ops = &hi6421_spmi_ldo_rops;
|
0x21, 0x01, 0x5c,
|
||||||
rdesc->type = REGULATOR_VOLTAGE;
|
20000, 360,
|
||||||
rdesc->min_uV = initdata->constraints.min_uV;
|
0x10, 10000),
|
||||||
|
HI6421V600_LDO(LDO16, ldo15_voltages,
|
||||||
supplyname = of_get_property(np, "supply_name", NULL);
|
0x22, 0x01, 0x5d,
|
||||||
if (supplyname)
|
20000, 360,
|
||||||
initdata->supply_regulator = supplyname;
|
0x10, 10000),
|
||||||
|
HI6421V600_LDO(LDO17, ldo17_voltages,
|
||||||
/* parse device tree data for regulator specific */
|
0x23, 0x01, 0x5e,
|
||||||
ret = hi6421_spmi_dt_parse(pdev, sreg, rdesc);
|
20000, 120,
|
||||||
if (ret)
|
0x10, 10000),
|
||||||
return ret;
|
HI6421V600_LDO(LDO33, ldo17_voltages,
|
||||||
|
0x32, 0x01, 0x6d,
|
||||||
/* hisi regulator supports two modes */
|
20000, 120,
|
||||||
constraint = &initdata->constraints;
|
0, 0),
|
||||||
|
HI6421V600_LDO(LDO34, ldo34_voltages,
|
||||||
constraint->valid_modes_mask = REGULATOR_MODE_NORMAL;
|
0x33, 0x01, 0x6e,
|
||||||
if (sreg->eco_mode_mask) {
|
20000, 120,
|
||||||
constraint->valid_modes_mask |= REGULATOR_MODE_IDLE;
|
0, 0),
|
||||||
constraint->valid_ops_mask |= REGULATOR_CHANGE_MODE;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
config.dev = &pdev->dev;
|
|
||||||
config.init_data = initdata;
|
|
||||||
config.driver_data = sreg;
|
|
||||||
config.of_node = pdev->dev.of_node;
|
|
||||||
|
|
||||||
/* register regulator */
|
|
||||||
rdev = regulator_register(rdesc, &config);
|
|
||||||
if (IS_ERR(rdev)) {
|
|
||||||
dev_err(dev, "failed to register %s\n",
|
|
||||||
rdesc->name);
|
|
||||||
return PTR_ERR(rdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
rdev_dbg(rdev, "valid_modes_mask: 0x%x, valid_ops_mask: 0x%x\n",
|
|
||||||
constraint->valid_modes_mask, constraint->valid_ops_mask);
|
|
||||||
|
|
||||||
dev_set_drvdata(dev, rdev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
|
static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *pmic_dev = pdev->dev.parent;
|
struct device *pmic_dev = pdev->dev.parent;
|
||||||
struct device_node *np = pmic_dev->of_node;
|
struct regulator_config config = { };
|
||||||
struct device_node *regulators, *child;
|
struct hi6421_spmi_reg_info *sreg;
|
||||||
struct platform_device *new_pdev;
|
struct hi6421_spmi_reg_info *info;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
struct hi6421_spmi_pmic *pmic;
|
struct hi6421_spmi_pmic *pmic;
|
||||||
int ret;
|
struct regulator_dev *rdev;
|
||||||
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This driver is meant to be called by hi6421-spmi-core,
|
* This driver is meant to be called by hi6421-spmi-core,
|
||||||
@@ -410,69 +254,46 @@ static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
|
|||||||
if (WARN_ON(!pmic))
|
if (WARN_ON(!pmic))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
regulators = of_get_child_by_name(np, "regulators");
|
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
|
||||||
if (!regulators) {
|
if (!sreg)
|
||||||
dev_err(&pdev->dev, "regulator node not found\n");
|
return -ENOMEM;
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
sreg->pmic = pmic;
|
||||||
* Parse all LDO regulator nodes
|
mutex_init(&sreg->enable_mutex);
|
||||||
*/
|
|
||||||
for_each_child_of_node(regulators, child) {
|
|
||||||
dev_dbg(&pdev->dev, "adding child %pOF\n", child);
|
|
||||||
|
|
||||||
new_pdev = platform_device_alloc(child->name, -1);
|
for (i = 0; i < ARRAY_SIZE(regulator_info); i++) {
|
||||||
new_pdev->dev.parent = pmic_dev;
|
info = ®ulator_info[i];
|
||||||
new_pdev->dev.of_node = of_node_get(child);
|
|
||||||
|
|
||||||
ret = platform_device_add(new_pdev);
|
config.dev = pdev->dev.parent;
|
||||||
if (ret < 0) {
|
config.driver_data = sreg;
|
||||||
platform_device_put(new_pdev);
|
config.regmap = pmic->regmap;
|
||||||
continue;
|
|
||||||
|
rdev = devm_regulator_register(dev, &info->desc, &config);
|
||||||
|
if (IS_ERR(rdev)) {
|
||||||
|
dev_err(dev, "failed to register %s\n",
|
||||||
|
info->desc.name);
|
||||||
|
return PTR_ERR(rdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = hi6421_spmi_regulator_probe_ldo(new_pdev, child, pmic);
|
|
||||||
if (ret < 0)
|
|
||||||
platform_device_put(new_pdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
of_node_put(regulators);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi6421_spmi_regulator_remove(struct platform_device *pdev)
|
static const struct platform_device_id hi6421_spmi_regulator_table[] = {
|
||||||
{
|
|
||||||
struct regulator_dev *rdev = dev_get_drvdata(&pdev->dev);
|
|
||||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
|
||||||
|
|
||||||
regulator_unregister(rdev);
|
|
||||||
|
|
||||||
if (rdev->desc->volt_table)
|
|
||||||
devm_kfree(&pdev->dev, (unsigned int *)rdev->desc->volt_table);
|
|
||||||
|
|
||||||
kfree(sreg);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct platform_device_id hi6421v600_regulator_table[] = {
|
|
||||||
{ .name = "hi6421v600-regulator" },
|
{ .name = "hi6421v600-regulator" },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(platform, hi6421v600_regulator_table);
|
MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table);
|
||||||
|
|
||||||
static struct platform_driver hi6421v600_regulator_driver = {
|
static struct platform_driver hi6421_spmi_regulator_driver = {
|
||||||
.id_table = hi6421v600_regulator_table,
|
.id_table = hi6421_spmi_regulator_table,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "hi6421v600-regulator",
|
.name = "hi6421v600-regulator",
|
||||||
},
|
},
|
||||||
.probe = hi6421_spmi_regulator_probe,
|
.probe = hi6421_spmi_regulator_probe,
|
||||||
.remove = hi6421_spmi_regulator_remove,
|
|
||||||
};
|
};
|
||||||
module_platform_driver(hi6421v600_regulator_driver);
|
module_platform_driver(hi6421_spmi_regulator_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Hi6421v600 regulator driver");
|
MODULE_DESCRIPTION("Hi6421v600 SPMI regulator driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
||||||
|
@@ -55,52 +55,13 @@ properties:
|
|||||||
|
|
||||||
$ref: "/schemas/regulator/regulator.yaml#"
|
$ref: "/schemas/regulator/regulator.yaml#"
|
||||||
|
|
||||||
properties:
|
|
||||||
reg:
|
|
||||||
description: Enable register.
|
|
||||||
|
|
||||||
'#address-cells':
|
|
||||||
const: 1
|
|
||||||
|
|
||||||
'#size-cells':
|
|
||||||
const: 0
|
|
||||||
|
|
||||||
vsel-reg:
|
|
||||||
description: Voltage selector register.
|
|
||||||
|
|
||||||
enable-mask:
|
|
||||||
description: Bitmask used to enable the regulator.
|
|
||||||
|
|
||||||
voltage-table:
|
|
||||||
description: Table with the selector items for the voltage regulator.
|
|
||||||
minItems: 2
|
|
||||||
maxItems: 16
|
|
||||||
|
|
||||||
off-on-delay-us:
|
|
||||||
description: Time required for changing state to enabled in microseconds.
|
|
||||||
|
|
||||||
startup-delay-us:
|
|
||||||
description: Startup time in microseconds.
|
|
||||||
|
|
||||||
idle-mode-mask:
|
|
||||||
description: Bitmask used to put the regulator on idle mode.
|
|
||||||
|
|
||||||
eco-microamp:
|
|
||||||
description: Maximum current while on idle mode.
|
|
||||||
|
|
||||||
required:
|
|
||||||
- reg
|
|
||||||
- vsel-reg
|
|
||||||
- enable-mask
|
|
||||||
- voltage-table
|
|
||||||
- off-on-delay-us
|
|
||||||
- startup-delay-us
|
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
- regulators
|
- regulators
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
- |
|
- |
|
||||||
/* pmic properties */
|
/* pmic properties */
|
||||||
@@ -117,43 +78,58 @@ examples:
|
|||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
||||||
ldo3: ldo3@16 {
|
ldo3: LDO3 {
|
||||||
reg = <0x16>;
|
|
||||||
vsel-reg = <0x51>;
|
|
||||||
|
|
||||||
regulator-name = "ldo3";
|
regulator-name = "ldo3";
|
||||||
regulator-min-microvolt = <1500000>;
|
regulator-min-microvolt = <1500000>;
|
||||||
regulator-max-microvolt = <2000000>;
|
regulator-max-microvolt = <2000000>;
|
||||||
regulator-boot-on;
|
regulator-boot-on;
|
||||||
|
|
||||||
enable-mask = <0x01>;
|
|
||||||
|
|
||||||
voltage-table = <1500000>, <1550000>, <1600000>, <1650000>,
|
|
||||||
<1700000>, <1725000>, <1750000>, <1775000>,
|
|
||||||
<1800000>, <1825000>, <1850000>, <1875000>,
|
|
||||||
<1900000>, <1925000>, <1950000>, <2000000>;
|
|
||||||
off-on-delay-us = <20000>;
|
|
||||||
startup-delay-us = <120>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ldo4: ldo4@17 { /* 40 PIN */
|
ldo4: LDO4 {
|
||||||
reg = <0x17>;
|
|
||||||
vsel-reg = <0x52>;
|
|
||||||
|
|
||||||
regulator-name = "ldo4";
|
regulator-name = "ldo4";
|
||||||
regulator-min-microvolt = <1725000>;
|
regulator-min-microvolt = <1725000>;
|
||||||
regulator-max-microvolt = <1900000>;
|
regulator-max-microvolt = <1900000>;
|
||||||
regulator-boot-on;
|
regulator-boot-on;
|
||||||
|
};
|
||||||
|
|
||||||
enable-mask = <0x01>;
|
ldo9: LDO9 {
|
||||||
idle-mode-mask = <0x10>;
|
regulator-name = "ldo9";
|
||||||
eco-microamp = <10000>;
|
regulator-min-microvolt = <1750000>;
|
||||||
|
regulator-max-microvolt = <3300000>;
|
||||||
|
regulator-boot-on;
|
||||||
|
};
|
||||||
|
|
||||||
hi6421-vsel = <0x52 0x07>;
|
ldo15: LDO15 {
|
||||||
voltage-table = <1725000>, <1750000>, <1775000>, <1800000>,
|
regulator-name = "ldo15";
|
||||||
<1825000>, <1850000>, <1875000>, <1900000>;
|
regulator-min-microvolt = <1800000>;
|
||||||
off-on-delay-us = <20000>;
|
regulator-max-microvolt = <3000000>;
|
||||||
startup-delay-us = <120>;
|
regulator-always-on;
|
||||||
|
};
|
||||||
|
|
||||||
|
ldo16: LDO16 {
|
||||||
|
regulator-name = "ldo16";
|
||||||
|
regulator-min-microvolt = <1800000>;
|
||||||
|
regulator-max-microvolt = <3000000>;
|
||||||
|
regulator-boot-on;
|
||||||
|
};
|
||||||
|
|
||||||
|
ldo17: LDO17 {
|
||||||
|
regulator-name = "ldo17";
|
||||||
|
regulator-min-microvolt = <2500000>;
|
||||||
|
regulator-max-microvolt = <3300000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
ldo33: LDO33 {
|
||||||
|
regulator-name = "ldo33";
|
||||||
|
regulator-min-microvolt = <2500000>;
|
||||||
|
regulator-max-microvolt = <3300000>;
|
||||||
|
regulator-boot-on;
|
||||||
|
};
|
||||||
|
|
||||||
|
ldo34: LDO34 {
|
||||||
|
regulator-name = "ldo34";
|
||||||
|
regulator-min-microvolt = <2600000>;
|
||||||
|
regulator-max-microvolt = <3300000>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -26,14 +26,22 @@ properties:
|
|||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
|
"#address-cells":
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
"#size-cells":
|
||||||
|
const: 0
|
||||||
|
|
||||||
spmi-channel:
|
spmi-channel:
|
||||||
description: |
|
description: |
|
||||||
number of the Kirin 970 SPMI channel where the SPMI devices are connected.
|
number of the Kirin 970 SPMI channel where the SPMI devices are connected.
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
- spmi-channel
|
- spmi-channel
|
||||||
|
- "#address-cells"
|
||||||
|
- "#size-cells"
|
||||||
|
|
||||||
patternProperties:
|
patternProperties:
|
||||||
"^pmic@[0-9a-f]$":
|
"^pmic@[0-9a-f]$":
|
||||||
@@ -43,6 +51,8 @@ patternProperties:
|
|||||||
are documented at
|
are documented at
|
||||||
drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml.
|
drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml.
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
- |
|
- |
|
||||||
bus {
|
bus {
|
||||||
@@ -51,11 +61,14 @@ examples:
|
|||||||
|
|
||||||
spmi: spmi@fff24000 {
|
spmi: spmi@fff24000 {
|
||||||
compatible = "hisilicon,kirin970-spmi-controller";
|
compatible = "hisilicon,kirin970-spmi-controller";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <0>;
|
||||||
status = "ok";
|
status = "ok";
|
||||||
reg = <0x0 0xfff24000 0x0 0x1000>;
|
reg = <0x0 0xfff24000 0x0 0x1000>;
|
||||||
spmi-channel = <2>;
|
spmi-channel = <2>;
|
||||||
|
|
||||||
pmic@0 {
|
pmic@0 {
|
||||||
|
reg = <0 0>;
|
||||||
/* pmic properties */
|
/* pmic properties */
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
* Authors: Yu Chen <chenyu56@huawei.com>
|
* Authors: Yu Chen <chenyu56@huawei.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
@@ -41,15 +42,15 @@
|
|||||||
#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
|
#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
|
||||||
|
|
||||||
#define USB3OTG_CTRL0 (0x00)
|
#define USB3OTG_CTRL0 (0x00)
|
||||||
#define USB3OTG_CTRL3 (0x0C)
|
#define USB3OTG_CTRL3 (0x0c)
|
||||||
#define USB3OTG_CTRL4 (0x10)
|
#define USB3OTG_CTRL4 (0x10)
|
||||||
#define USB3OTG_CTRL5 (0x14)
|
#define USB3OTG_CTRL5 (0x14)
|
||||||
#define USB3OTG_CTRL7 (0x1C)
|
#define USB3OTG_CTRL7 (0x1c)
|
||||||
#define USB_MISC_CFG50 (0x50)
|
#define USB_MISC_CFG50 (0x50)
|
||||||
#define USB_MISC_CFG54 (0x54)
|
#define USB_MISC_CFG54 (0x54)
|
||||||
#define USB_MISC_CFG58 (0x58)
|
#define USB_MISC_CFG58 (0x58)
|
||||||
#define USB_MISC_CFG5C (0x5C)
|
#define USB_MISC_CFG5C (0x5c)
|
||||||
#define USB_MISC_CFGA0 (0xA0)
|
#define USB_MISC_CFGA0 (0xa0)
|
||||||
#define TCA_CLK_RST (0x200)
|
#define TCA_CLK_RST (0x200)
|
||||||
#define TCA_INTR_EN (0x204)
|
#define TCA_INTR_EN (0x204)
|
||||||
#define TCA_INTR_STS (0x208)
|
#define TCA_INTR_STS (0x208)
|
||||||
@@ -66,14 +67,14 @@
|
|||||||
|
|
||||||
#define CTRL5_USB2_SIDDQ BIT(0)
|
#define CTRL5_USB2_SIDDQ BIT(0)
|
||||||
|
|
||||||
#define CTRL7_USB2_REFCLKSEL_MASK (3 << 3)
|
#define CTRL7_USB2_REFCLKSEL_MASK GENMASK(4, 3)
|
||||||
#define CTRL7_USB2_REFCLKSEL_ABB (3 << 3)
|
#define CTRL7_USB2_REFCLKSEL_ABB (BIT(4) | BIT(3))
|
||||||
#define CTRL7_USB2_REFCLKSEL_PAD (2 << 3)
|
#define CTRL7_USB2_REFCLKSEL_PAD BIT(4)
|
||||||
|
|
||||||
#define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23)
|
#define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23)
|
||||||
|
|
||||||
#define CFG54_USB31PHY_CR_ADDR_MASK (0xFFFF)
|
#define CFG54_USB31PHY_CR_ADDR_MASK GENMASK(31, 16)
|
||||||
#define CFG54_USB31PHY_CR_ADDR_SHIFT (16)
|
|
||||||
#define CFG54_USB3PHY_REF_USE_PAD BIT(12)
|
#define CFG54_USB3PHY_REF_USE_PAD BIT(12)
|
||||||
#define CFG54_PHY0_PMA_PWR_STABLE BIT(11)
|
#define CFG54_PHY0_PMA_PWR_STABLE BIT(11)
|
||||||
#define CFG54_PHY0_PCS_PWR_STABLE BIT(9)
|
#define CFG54_PHY0_PCS_PWR_STABLE BIT(9)
|
||||||
@@ -84,8 +85,7 @@
|
|||||||
#define CFG54_USB31PHY_CR_CLK BIT(2)
|
#define CFG54_USB31PHY_CR_CLK BIT(2)
|
||||||
#define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1)
|
#define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1)
|
||||||
|
|
||||||
#define CFG58_USB31PHY_CR_DATA_MASK (0xFFFF)
|
#define CFG58_USB31PHY_CR_DATA_MASK GENMASK(31, 16)
|
||||||
#define CFG58_USB31PHY_CR_DATA_RD_START (16)
|
|
||||||
|
|
||||||
#define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1)
|
#define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1)
|
||||||
|
|
||||||
@@ -102,20 +102,20 @@
|
|||||||
#define CLK_RST_SUSPEND_CLK_EN BIT(0)
|
#define CLK_RST_SUSPEND_CLK_EN BIT(0)
|
||||||
|
|
||||||
#define GCFG_ROLE_HSTDEV BIT(4)
|
#define GCFG_ROLE_HSTDEV BIT(4)
|
||||||
#define GCFG_OP_MODE (3 << 0)
|
#define GCFG_OP_MODE GENMASK(1, 0)
|
||||||
#define GCFG_OP_MODE_CTRL_SYNC_MODE BIT(0)
|
#define GCFG_OP_MODE_CTRL_SYNC_MODE BIT(0)
|
||||||
|
|
||||||
#define TCPC_VALID BIT(4)
|
#define TCPC_VALID BIT(4)
|
||||||
#define TCPC_LOW_POWER_EN BIT(3)
|
#define TCPC_LOW_POWER_EN BIT(3)
|
||||||
#define TCPC_MUX_CONTROL_MASK (3 << 0)
|
#define TCPC_MUX_CONTROL_MASK GENMASK(1, 0)
|
||||||
#define TCPC_MUX_CONTROL_USB31 BIT(0)
|
#define TCPC_MUX_CONTROL_USB31 BIT(0)
|
||||||
|
|
||||||
#define SYSMODE_CFG_TYPEC_DISABLE BIT(3)
|
#define SYSMODE_CFG_TYPEC_DISABLE BIT(3)
|
||||||
|
|
||||||
#define VBUS_CTRL_POWERPRESENT_OVERRD (3 << 2)
|
#define VBUS_CTRL_POWERPRESENT_OVERRD GENMASK(3, 2)
|
||||||
#define VBUS_CTRL_VBUSVALID_OVERRD (3 << 0)
|
#define VBUS_CTRL_VBUSVALID_OVERRD GENMASK(1, 0)
|
||||||
|
|
||||||
#define KIRIN970_USB_DEFAULT_PHY_PARAM (0xFDFEE4)
|
#define KIRIN970_USB_DEFAULT_PHY_PARAM (0xfdfee4)
|
||||||
#define KIRIN970_USB_DEFAULT_PHY_VBOOST (0x5)
|
#define KIRIN970_USB_DEFAULT_PHY_VBOOST (0x5)
|
||||||
|
|
||||||
#define TX_VBOOST_LVL_REG (0xf)
|
#define TX_VBOOST_LVL_REG (0xf)
|
||||||
@@ -162,16 +162,14 @@ static int hi3670_phy_cr_set_sel(struct regmap *usb31misc)
|
|||||||
|
|
||||||
static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
|
static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, reg;
|
||||||
|
|
||||||
if (direction)
|
if (direction)
|
||||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
reg = CFG54_USB31PHY_CR_WR_EN;
|
||||||
CFG54_USB31PHY_CR_WR_EN,
|
|
||||||
CFG54_USB31PHY_CR_WR_EN);
|
|
||||||
else
|
else
|
||||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
reg = CFG54_USB31PHY_CR_RD_EN;
|
||||||
CFG54_USB31PHY_CR_RD_EN,
|
|
||||||
CFG54_USB31PHY_CR_RD_EN);
|
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54, reg, reg);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -180,16 +178,14 @@ static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
return regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||||
CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
|
CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
|
static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int retry = 100000;
|
int retry = 10;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
while (retry-- > 0) {
|
while (retry-- > 0) {
|
||||||
@@ -202,6 +198,8 @@ static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
|
|||||||
ret = hi3670_phy_cr_clk(usb31misc);
|
ret = hi3670_phy_cr_clk(usb31misc);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
usleep_range(10, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
@@ -216,9 +214,9 @@ static int hi3670_phy_cr_set_addr(struct regmap *usb31misc, u32 addr)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
reg &= ~(CFG54_USB31PHY_CR_ADDR_MASK << CFG54_USB31PHY_CR_ADDR_SHIFT);
|
reg = FIELD_PREP(CFG54_USB31PHY_CR_ADDR_MASK, addr);
|
||||||
reg |= ((addr & CFG54_USB31PHY_CR_ADDR_MASK) << CFG54_USB31PHY_CR_ADDR_SHIFT);
|
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||||
ret = regmap_write(usb31misc, USB_MISC_CFG54, reg);
|
CFG54_USB31PHY_CR_ADDR_MASK, reg);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -255,8 +253,7 @@ static int hi3670_phy_cr_read(struct regmap *usb31misc, u32 addr, u32 *val)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*val = (reg >> CFG58_USB31PHY_CR_DATA_RD_START) &
|
*val = FIELD_GET(CFG58_USB31PHY_CR_DATA_MASK, reg);
|
||||||
CFG58_USB31PHY_CR_DATA_MASK;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -281,7 +278,7 @@ static int hi3670_phy_cr_write(struct regmap *usb31misc, u32 addr, u32 val)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = regmap_write(usb31misc, USB_MISC_CFG58,
|
ret = regmap_write(usb31misc, USB_MISC_CFG58,
|
||||||
val & CFG58_USB31PHY_CR_DATA_MASK);
|
FIELD_PREP(CFG58_USB31PHY_CR_DATA_MASK, val));
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -329,24 +326,24 @@ static int hi3670_phy_set_params(struct hi3670_priv *priv)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi3670_is_abbclk_seleted(struct hi3670_priv *priv)
|
static bool hi3670_is_abbclk_selected(struct hi3670_priv *priv)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
if (!priv->sctrl) {
|
if (!priv->sctrl) {
|
||||||
dev_err(priv->dev, "priv->sctrl is null!\n");
|
dev_err(priv->dev, "priv->sctrl is null!\n");
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (regmap_read(priv->sctrl, SCTRL_SCDEEPSLEEPED, ®)) {
|
if (regmap_read(priv->sctrl, SCTRL_SCDEEPSLEEPED, ®)) {
|
||||||
dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n");
|
dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n");
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((reg & USB_CLK_SELECTED) == 0)
|
if ((reg & USB_CLK_SELECTED) == 0)
|
||||||
return 1;
|
return false;
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hi3670_config_phy_clock(struct hi3670_priv *priv)
|
static int hi3670_config_phy_clock(struct hi3670_priv *priv)
|
||||||
@@ -354,7 +351,7 @@ static int hi3670_config_phy_clock(struct hi3670_priv *priv)
|
|||||||
u32 val, mask;
|
u32 val, mask;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (hi3670_is_abbclk_seleted(priv)) {
|
if (!hi3670_is_abbclk_selected(priv)) {
|
||||||
/* usb refclk iso disable */
|
/* usb refclk iso disable */
|
||||||
ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS,
|
ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS,
|
||||||
USB_REFCLK_ISO_EN);
|
USB_REFCLK_ISO_EN);
|
||||||
@@ -571,7 +568,7 @@ static int hi3670_phy_exit(struct phy *phy)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (hi3670_is_abbclk_seleted(priv)) {
|
if (!hi3670_is_abbclk_selected(priv)) {
|
||||||
/* disable usb_tcxo_en */
|
/* disable usb_tcxo_en */
|
||||||
ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3,
|
ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3,
|
||||||
USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START);
|
USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START);
|
||||||
@@ -588,7 +585,7 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct phy_ops hi3670_phy_ops = {
|
static const struct phy_ops hi3670_phy_ops = {
|
||||||
.init = hi3670_phy_init,
|
.init = hi3670_phy_init,
|
||||||
.exit = hi3670_phy_exit,
|
.exit = hi3670_phy_exit,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
@@ -8,6 +8,7 @@ title: Hisilicon Kirin970 USB PHY
|
|||||||
|
|
||||||
maintainers:
|
maintainers:
|
||||||
- Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
- Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||||
|
|
||||||
description: |+
|
description: |+
|
||||||
Bindings for USB3 PHY on HiSilicon Kirin 970.
|
Bindings for USB3 PHY on HiSilicon Kirin 970.
|
||||||
|
|
||||||
|
@@ -269,7 +269,7 @@ ia_css_ptr hmm_alloc(size_t bytes, enum hmm_bo_type type,
|
|||||||
hmm_set(bo->start, 0, bytes);
|
hmm_set(bo->start, 0, bytes);
|
||||||
|
|
||||||
dev_dbg(atomisp_dev,
|
dev_dbg(atomisp_dev,
|
||||||
"%s: pages: 0x%08x (%ld bytes), type: %d from highmem %d, user ptr %p, cached %d\n",
|
"%s: pages: 0x%08x (%zu bytes), type: %d from highmem %d, user ptr %p, cached %d\n",
|
||||||
__func__, bo->start, bytes, type, from_highmem, userptr, cached);
|
__func__, bo->start, bytes, type, from_highmem, userptr, cached);
|
||||||
|
|
||||||
return bo->start;
|
return bo->start;
|
||||||
|
@@ -68,7 +68,7 @@ struct net_dev_context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct list_head net_devices = LIST_HEAD_INIT(net_devices);
|
static struct list_head net_devices = LIST_HEAD_INIT(net_devices);
|
||||||
static struct mutex probe_disc_mt; /* ch->linked = true, most_nd_open */
|
static DEFINE_MUTEX(probe_disc_mt); /* ch->linked = true, most_nd_open */
|
||||||
static DEFINE_SPINLOCK(list_lock); /* list_head, ch->linked = false, dev_hold */
|
static DEFINE_SPINLOCK(list_lock); /* list_head, ch->linked = false, dev_hold */
|
||||||
static struct most_component comp;
|
static struct most_component comp;
|
||||||
|
|
||||||
@@ -520,7 +520,6 @@ static int __init most_net_init(void)
|
|||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mutex_init(&probe_disc_mt);
|
|
||||||
err = most_register_component(&comp);
|
err = most_register_component(&comp);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user