Staging/IIO driver patches for 5.12-rc1
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. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYCqelQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymIDQCguWtPGy6U1sgaL3GAK/ROt2aet3wAn3TP1WgB GeKAKKPshu3cskYQzlou =UPZR -----END PGP SIGNATURE----- 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:
commit
5d99aa093b
|
@ -198,6 +198,7 @@ Description:
|
|||
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_anglY_raw
|
||||
KernelVersion: 4.17
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1812,3 +1813,13 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
Unscaled light intensity according to CIE 1931/DIN 5033 color space.
|
||||
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.
|
||||
|
|
|
@ -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:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
mount-matrix:
|
||||
description: an optional 3x3 mounting rotation matrix.
|
||||
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
Xilinx XADC device driver
|
||||
|
||||
This binding document describes the bindings for both of them since the
|
||||
bindings are very similar. The Xilinx XADC is a ADC that can be found in the
|
||||
series 7 FPGAs from Xilinx. The XADC has a DRP interface for communication.
|
||||
Currently two different frontends for the DRP interface exist. One that is only
|
||||
available on the ZYNQ family as a hardmacro in the SoC portion of the ZYNQ. The
|
||||
other one is available on all series 7 platforms and is a softmacro with a AXI
|
||||
interface. This binding document describes the bindings for both of them since
|
||||
the bindings are very similar.
|
||||
This binding document describes the bindings for the Xilinx 7 Series XADC as well
|
||||
as the UltraScale/UltraScale+ System Monitor.
|
||||
|
||||
The Xilinx XADC is an ADC that can be found in the Series 7 FPGAs from Xilinx.
|
||||
The XADC has a DRP interface for communication. Currently two different
|
||||
frontends for the DRP interface exist. One that is only available on the ZYNQ
|
||||
family as a hardmacro in the SoC portion of the ZYNQ. The other one is available
|
||||
on all series 7 platforms and is a softmacro with a AXI interface. This binding
|
||||
document describes the bindings for both of them since the bindings are very
|
||||
similar.
|
||||
|
||||
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:
|
||||
- compatible: Should be one of
|
||||
|
@ -15,11 +24,14 @@ Required properties:
|
|||
configuration interface to interface to the XADC hardmacro.
|
||||
* "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
|
||||
interface to the XADC hardmacro.
|
||||
* "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
|
||||
- interrupts: Interrupt for the XADC control interface.
|
||||
- clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
|
||||
when using the AXI-XADC pcore this must be the clock that provides the
|
||||
clock to the AXI bus interface of the core.
|
||||
when using the axi-xadc or the axi-system-management-wizard this must be
|
||||
the clock that provides the clock to the AXI bus interface of the core.
|
||||
|
||||
Optional properties:
|
||||
- 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mcp4726
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mcp4725
|
||||
then:
|
||||
properties:
|
||||
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
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
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:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
interrupts:
|
||||
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
|
||||
# mCube 3-axis 8-bit digital accelerometer
|
||||
- mcube,mc3230
|
||||
# MEMSIC magnetometer
|
||||
- memsic,mmc35240
|
||||
# MEMSIC 2-axis 8-bit digital accelerometer
|
||||
- memsic,mxc6225
|
||||
# Measurement Specialities I2C temperature and humidity sensor
|
||||
- meas,htu21
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5637
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5803
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5805
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5837
|
||||
|
@ -166,6 +164,10 @@ properties:
|
|||
- meas,ms8607-temppressure
|
||||
# Measurement Specialties temperature sensor
|
||||
- meas,tsys01
|
||||
# MEMSIC magnetometer
|
||||
- memsic,mmc35240
|
||||
# MEMSIC 2-axis 8-bit digital accelerometer
|
||||
- memsic,mxc6225
|
||||
# Microchip differential I2C ADC, 1 Channel, 18 bit
|
||||
- microchip,mcp3421
|
||||
# Microchip differential I2C ADC, 2 Channel, 18 bit
|
||||
|
|
|
@ -1260,6 +1260,8 @@ patternProperties:
|
|||
description: Shenzhen Xunlong Software CO.,Limited
|
||||
"^xylon,.*":
|
||||
description: Xylon
|
||||
"^yamaha,.*":
|
||||
description: Yamaha Corporation
|
||||
"^yes-optoelectronics,.*":
|
||||
description: Yes Optoelectronics Co.,Ltd.
|
||||
"^ylm,.*":
|
||||
|
|
|
@ -15,6 +15,7 @@ Contents:
|
|||
ethernet/index
|
||||
fddi/index
|
||||
hamradio/index
|
||||
qlogic/index
|
||||
wan/index
|
||||
wifi/index
|
||||
|
||||
|
|
|
@ -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`
|
|
@ -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
|
||||
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
|
||||
M: Akihiro Tsukada <tskd08@gmail.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (c) 2015, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -21,6 +22,7 @@
|
|||
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
|
||||
#define HID_CUSTOM_FIFO_SIZE 4096
|
||||
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
|
||||
#define HID_SENSOR_USAGE_LENGTH (4 + 1)
|
||||
|
||||
struct hid_sensor_custom_field {
|
||||
int report_id;
|
||||
|
@ -50,6 +52,7 @@ struct hid_sensor_custom {
|
|||
struct kfifo data_fifo;
|
||||
unsigned long misc_opened;
|
||||
wait_queue_head_t wait;
|
||||
struct platform_device *custom_pdev;
|
||||
};
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
int ret;
|
||||
int index;
|
||||
|
||||
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
|
||||
GFP_KERNEL);
|
||||
|
@ -764,6 +886,22 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
|
|||
sensor_inst->pdev = pdev;
|
||||
mutex_init(&sensor_inst->mutex);
|
||||
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,
|
||||
&sensor_inst->callbacks);
|
||||
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_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_remove_attributes(sensor_inst);
|
||||
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
||||
|
|
|
@ -23,6 +23,7 @@ enum accel_3d_channel {
|
|||
ACCEL_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX
|
||||
struct accel_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
|
@ -75,7 +76,7 @@ static const struct iio_chan_spec accel_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -110,7 +111,8 @@ static const struct iio_chan_spec gravity_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
@ -133,6 +134,7 @@ enum kx_acpi_type {
|
|||
};
|
||||
|
||||
struct kxcjk1013_data {
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
|
@ -1300,6 +1302,13 @@ static const char *kxcjk1013_match_acpi_device(struct device *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,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -1330,6 +1339,29 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
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) {
|
||||
data->chipset = (enum kx_chipset)(id->driver_data);
|
||||
name = id->name;
|
||||
|
|
|
@ -1228,8 +1228,15 @@ config XILINX_XADC
|
|||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to have support for the Xilinx XADC. The driver does support
|
||||
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
|
||||
Say yes here to have support for the Xilinx 7 Series XADC or
|
||||
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
|
||||
xilinx-xadc.
|
||||
|
|
|
@ -1108,10 +1108,14 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
return gpadc->irq_sw;
|
||||
}
|
||||
|
||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||
if (gpadc->irq_hw < 0) {
|
||||
dev_err(dev, "failed to get platform hw_conv_end irq\n");
|
||||
return gpadc->irq_hw;
|
||||
if (is_ab8500(gpadc->ab8500)) {
|
||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||
if (gpadc->irq_hw < 0) {
|
||||
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 */
|
||||
|
@ -1128,14 +1132,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"ab8500-gpadc-hw", gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Failed to request hw conversion irq: %d\n",
|
||||
gpadc->irq_hw);
|
||||
return ret;
|
||||
if (gpadc->irq_hw) {
|
||||
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"ab8500-gpadc-hw", gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Failed to request hw conversion irq: %d\n",
|
||||
gpadc->irq_hw);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* The VTVout LDO used to power the AB8500 GPADC */
|
||||
|
|
|
@ -67,6 +67,7 @@ enum ad7476_supported_device_ids {
|
|||
ID_ADS7866,
|
||||
ID_ADS7867,
|
||||
ID_ADS7868,
|
||||
ID_LTC2314_14,
|
||||
};
|
||||
|
||||
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[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 = {
|
||||
|
@ -365,6 +370,7 @@ static const struct spi_device_id ad7476_id[] = {
|
|||
{"ads7866", ID_ADS7866},
|
||||
{"ads7867", ID_ADS7867},
|
||||
{"ads7868", ID_ADS7868},
|
||||
{"ltc2314-14", ID_LTC2314_14},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -21,8 +22,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
/*
|
||||
* Definitions for the "user processor" registers lifted from the v3.4
|
||||
* Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -14,12 +15,12 @@
|
|||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
#define ADC5_USR_REVISION1 0x0
|
||||
#define ADC5_USR_STATUS1 0x8
|
||||
|
@ -154,18 +155,6 @@ struct adc5_chip {
|
|||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
@ -511,7 +451,7 @@ static int adc_read_raw_common(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
|
||||
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
|
||||
&adc5_prescale_ratios[prop->prescale],
|
||||
prop->prescale,
|
||||
adc->data,
|
||||
adc_code_volt, val);
|
||||
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);
|
||||
if (!ret) {
|
||||
ret = adc5_decimation_from_dt(value, data->decimation);
|
||||
ret = qcom_adc5_decimation_from_dt(value, data->decimation);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid decimation %d\n",
|
||||
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);
|
||||
if (!ret) {
|
||||
ret = adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
|
||||
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 &&
|
||||
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
|
||||
adc->data->info == &adc7_info)
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_2);
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2);
|
||||
else
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_1);
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
|
||||
|
||||
if (ret < 0) {
|
||||
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);
|
||||
if (!ret) {
|
||||
ret = adc5_avg_samples_from_dt(value);
|
||||
ret = qcom_adc5_avg_samples_from_dt(value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid avg-samples %d\n",
|
||||
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 device_node *child;
|
||||
unsigned int index = 0;
|
||||
const struct of_device_id *id;
|
||||
const struct adc5_data *data;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
iio_chan = adc->iio_chans;
|
||||
id = of_match_node(adc5_match_table, node);
|
||||
if (id)
|
||||
data = id->data;
|
||||
else
|
||||
data = &adc5_data_pmic;
|
||||
adc->data = data;
|
||||
adc->data = of_device_get_match_data(adc->dev);
|
||||
if (!adc->data)
|
||||
adc->data = &adc5_data_pmic;
|
||||
|
||||
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) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn_type =
|
||||
data->adc_chans[prop.channel].scale_fn_type;
|
||||
adc->data->adc_chans[prop.channel].scale_fn_type;
|
||||
*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->datasheet_name = prop.datasheet_name;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -20,8 +21,6 @@
|
|||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
/* VADC register and bit definitions */
|
||||
#define VADC_REVISION2 0x1
|
||||
#define VADC_REVISION2_SUPPORTED_VADC 1
|
||||
|
|
|
@ -2,50 +2,61 @@
|
|||
#include <linux/bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/fixp-arith.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.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 */
|
||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||
{1758, -40},
|
||||
{1742, -35},
|
||||
{1719, -30},
|
||||
{1691, -25},
|
||||
{1654, -20},
|
||||
{1608, -15},
|
||||
{1551, -10},
|
||||
{1483, -5},
|
||||
{1404, 0},
|
||||
{1315, 5},
|
||||
{1218, 10},
|
||||
{1114, 15},
|
||||
{1007, 20},
|
||||
{900, 25},
|
||||
{795, 30},
|
||||
{696, 35},
|
||||
{605, 40},
|
||||
{522, 45},
|
||||
{448, 50},
|
||||
{383, 55},
|
||||
{327, 60},
|
||||
{278, 65},
|
||||
{237, 70},
|
||||
{202, 75},
|
||||
{172, 80},
|
||||
{146, 85},
|
||||
{125, 90},
|
||||
{107, 95},
|
||||
{92, 100},
|
||||
{79, 105},
|
||||
{68, 110},
|
||||
{59, 115},
|
||||
{51, 120},
|
||||
{44, 125}
|
||||
{1758, -40000 },
|
||||
{1742, -35000 },
|
||||
{1719, -30000 },
|
||||
{1691, -25000 },
|
||||
{1654, -20000 },
|
||||
{1608, -15000 },
|
||||
{1551, -10000 },
|
||||
{1483, -5000 },
|
||||
{1404, 0 },
|
||||
{1315, 5000 },
|
||||
{1218, 10000 },
|
||||
{1114, 15000 },
|
||||
{1007, 20000 },
|
||||
{900, 25000 },
|
||||
{795, 30000 },
|
||||
{696, 35000 },
|
||||
{605, 40000 },
|
||||
{522, 45000 },
|
||||
{448, 50000 },
|
||||
{383, 55000 },
|
||||
{327, 60000 },
|
||||
{278, 65000 },
|
||||
{237, 70000 },
|
||||
{202, 75000 },
|
||||
{172, 80000 },
|
||||
{146, 85000 },
|
||||
{125, 90000 },
|
||||
{107, 95000 },
|
||||
{92, 100000 },
|
||||
{79, 105000 },
|
||||
{68, 110000 },
|
||||
{59, 115000 },
|
||||
{51, 120000 },
|
||||
{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[] = {
|
||||
{ 433700, 1967},
|
||||
{ 473100, 1964},
|
||||
{ 512400, 1957},
|
||||
{ 551500, 1949},
|
||||
{ 590500, 1940},
|
||||
{ 629300, 1930},
|
||||
{ 667900, 1921},
|
||||
{ 706400, 1910},
|
||||
{ 744600, 1896},
|
||||
{ 782500, 1878},
|
||||
{ 820100, 1859},
|
||||
{ 857300, 0},
|
||||
{ 857300, 160000 },
|
||||
{ 820100, 140000 },
|
||||
{ 782500, 120000 },
|
||||
{ 744600, 100000 },
|
||||
{ 706400, 80000 },
|
||||
{ 667900, 60000 },
|
||||
{ 629300, 40000 },
|
||||
{ 590500, 20000 },
|
||||
{ 551500, 0 },
|
||||
{ 512400, -20000 },
|
||||
{ 473100, -40000 },
|
||||
{ 433700, -60000 },
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -278,6 +289,18 @@ static const struct vadc_map_pt adcmap7_100k[] = {
|
|||
{ 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(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
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,
|
||||
u32 tablesize, s32 input, int *output)
|
||||
{
|
||||
bool descending = 1;
|
||||
u32 i = 0;
|
||||
|
||||
if (!pts)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if table is descending or ascending */
|
||||
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;
|
||||
}
|
||||
while (i < tablesize && pts[i].x > input)
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
*output = pts[0].y;
|
||||
} else if (i == tablesize) {
|
||||
*output = pts[tablesize - 1].y;
|
||||
} else {
|
||||
/* result is between search_index and search_index-1 */
|
||||
/* interpolate linearly */
|
||||
*output = (((s32)((pts[i].y - pts[i - 1].y) *
|
||||
(input - pts[i - 1].x)) /
|
||||
(pts[i].x - pts[i - 1].x)) +
|
||||
pts[i - 1].y);
|
||||
*output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y,
|
||||
pts[i].x, pts[i].y,
|
||||
input);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -415,8 +418,6 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*result_mdec *= 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -563,33 +564,13 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
|
|||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
|
||||
int voltage, vtemp0, temp, i;
|
||||
int voltage;
|
||||
|
||||
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1);
|
||||
|
||||
if (adcmap7_die_temp[0].x > voltage) {
|
||||
*result_mdec = DIE_TEMP_ADC7_SCALE_1;
|
||||
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;
|
||||
return qcom_vadc_map_voltage_temp(adcmap7_die_temp, ARRAY_SIZE(adcmap7_die_temp),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
u16 adc_code, int *result)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
|
||||
|
||||
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
|
||||
scaletype < SCALE_HW_CALIB_INVALID)) {
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
return (volt * denominator + numerator / 2) / numerator;
|
||||
return DIV_ROUND_CLOSEST(volt * denominator, numerator);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (priv->bclk) {
|
||||
ret = clk_prepare_enable(priv->bclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "bus clk enable failed\n");
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
ret = clk_prepare_enable(priv->bclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "bus clk enable failed\n");
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
|
||||
if (priv->aclk) {
|
||||
ret = clk_prepare_enable(priv->aclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "adc clk enable failed\n");
|
||||
goto err_bclk_disable;
|
||||
}
|
||||
ret = clk_prepare_enable(priv->aclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "adc clk enable failed\n");
|
||||
goto err_bclk_disable;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
err_bclk_disable:
|
||||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
err_regulator_disable:
|
||||
regulator_disable(priv->vref);
|
||||
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) */
|
||||
priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
|
||||
if (priv->aclk)
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
regulator_disable(priv->vref);
|
||||
stm32_adc_core_switches_supply_dis(priv);
|
||||
regulator_disable(priv->vdda);
|
||||
|
|
|
@ -546,8 +546,7 @@ static int stm32_adc_hw_stop(struct device *dev)
|
|||
if (adc->cfg->unprepare)
|
||||
adc->cfg->unprepare(indio_dev);
|
||||
|
||||
if (adc->clk)
|
||||
clk_disable_unprepare(adc->clk);
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -558,11 +557,9 @@ static int stm32_adc_hw_start(struct device *dev)
|
|||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (adc->clk) {
|
||||
ret = clk_prepare_enable(adc->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = clk_prepare_enable(adc->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stm32_adc_set_res(adc);
|
||||
|
||||
|
@ -575,8 +572,7 @@ static int stm32_adc_hw_start(struct device *dev)
|
|||
return 0;
|
||||
|
||||
err_clk_dis:
|
||||
if (adc->clk)
|
||||
clk_disable_unprepare(adc->clk);
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
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);
|
||||
|
||||
if (priv->aclk)
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.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_IPISR 0x60
|
||||
#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_GIER_ENABLE BIT(31)
|
||||
|
@ -447,6 +453,12 @@ static const struct xadc_ops xadc_zynq_ops = {
|
|||
.get_dclk_rate = xadc_zynq_get_dclk_rate,
|
||||
.interrupt_handler = xadc_zynq_interrupt_handler,
|
||||
.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,
|
||||
|
@ -454,7 +466,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
|||
{
|
||||
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;
|
||||
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
@ -541,7 +555,7 @@ static unsigned long xadc_axi_get_dclk(struct xadc *xadc)
|
|||
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,
|
||||
.write = xadc_axi_write_adc_reg,
|
||||
.setup = xadc_axi_setup,
|
||||
|
@ -549,6 +563,18 @@ static const struct xadc_ops xadc_axi_ops = {
|
|||
.update_alarm = xadc_axi_update_alarm,
|
||||
.interrupt_handler = xadc_axi_interrupt_handler,
|
||||
.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,
|
||||
|
@ -585,15 +611,22 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev,
|
|||
const unsigned long *mask)
|
||||
{
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
unsigned int n;
|
||||
size_t new_size, n;
|
||||
void *data;
|
||||
|
||||
n = bitmap_weight(mask, indio_dev->masklength);
|
||||
|
||||
kfree(xadc->data);
|
||||
xadc->data = kcalloc(n, sizeof(*xadc->data), GFP_KERNEL);
|
||||
if (!xadc->data)
|
||||
if (check_mul_overflow(n, sizeof(*xadc->data), &new_size))
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
const char *name)
|
||||
{
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct iio_trigger *trig;
|
||||
int ret;
|
||||
|
||||
trig = iio_trigger_alloc("%s%d-%s", indio_dev->name,
|
||||
indio_dev->id, name);
|
||||
trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name,
|
||||
indio_dev->id, name);
|
||||
if (trig == NULL)
|
||||
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;
|
||||
iio_trigger_set_drvdata(trig, iio_priv(indio_dev));
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
ret = devm_iio_trigger_register(dev, trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
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)
|
||||
{
|
||||
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. */
|
||||
switch (seq_mode) {
|
||||
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;
|
||||
|
||||
/* 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)
|
||||
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 xadc *xadc = iio_priv(indio_dev);
|
||||
unsigned int bits = chan->scan_type.realbits;
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
|
@ -874,17 +918,17 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val16 >>= 4;
|
||||
val16 >>= chan->scan_type.shift;
|
||||
if (chan->scan_type.sign == 'u')
|
||||
*val = val16;
|
||||
else
|
||||
*val = sign_extend32(val16, 11);
|
||||
*val = sign_extend32(val16, bits - 1);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
/* V = (val * 3.0) / 4096 */
|
||||
/* V = (val * 3.0) / 2**bits */
|
||||
switch (chan->address) {
|
||||
case XADC_REG_VCCINT:
|
||||
case XADC_REG_VCCAUX:
|
||||
|
@ -900,19 +944,19 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 1000;
|
||||
break;
|
||||
}
|
||||
*val2 = 12;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/* Temp in C = (val * 503.975) / 4096 - 273.15 */
|
||||
/* Temp in C = (val * 503.975) / 2**bits - 273.15 */
|
||||
*val = 503975;
|
||||
*val2 = 12;
|
||||
*val2 = bits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* Only the temperature channel has an offset */
|
||||
*val = -((273150 << 12) / 503975);
|
||||
*val = -((273150 << bits) / 503975);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
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, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
|
@ -1015,14 +1059,14 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||
.scan_index = (_scan_index), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.shift = 16 - (_bits), \
|
||||
.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, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
|
@ -1035,41 +1079,82 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||
.scan_index = (_scan_index), \
|
||||
.scan_type = { \
|
||||
.sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
|
||||
.realbits = 12, \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.shift = 16 - (_bits), \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.extend_name = _ext, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec xadc_channels[] = {
|
||||
XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||
XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||
XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||
XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||
XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
|
||||
XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
|
||||
XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
|
||||
XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||
XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||
XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||
XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
||||
/* 7 Series */
|
||||
#define XADC_7S_CHAN_TEMP(_chan, _scan_index, _addr) \
|
||||
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 12)
|
||||
#define XADC_7S_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
|
||||
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 12, _ext, _alarm)
|
||||
|
||||
static const struct iio_chan_spec xadc_7s_channels[] = {
|
||||
XADC_7S_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||
XADC_7S_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||
XADC_7S_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||
XADC_7S_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||
XADC_7S_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
|
||||
XADC_7S_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
|
||||
XADC_7S_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
|
||||
XADC_7S_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||
XADC_7S_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||
XADC_7S_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), 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 = {
|
||||
|
@ -1083,8 +1168,16 @@ static const struct iio_info xadc_info = {
|
|||
};
|
||||
|
||||
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);
|
||||
|
@ -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 xadc *xadc = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *channel_templates;
|
||||
struct iio_chan_spec *channels, *chan;
|
||||
struct device_node *chan_node, *child;
|
||||
unsigned int max_channels;
|
||||
unsigned int num_channels;
|
||||
const char *external_mux;
|
||||
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);
|
||||
}
|
||||
|
||||
channels = devm_kmemdup(dev, xadc_channels,
|
||||
sizeof(xadc_channels), GFP_KERNEL);
|
||||
if (xadc->ops->type == XADC_TYPE_S7) {
|
||||
channel_templates = xadc_7s_channels;
|
||||
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)
|
||||
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");
|
||||
if (chan_node) {
|
||||
for_each_child_of_node(chan_node, child) {
|
||||
if (num_channels >= ARRAY_SIZE(xadc_channels)) {
|
||||
if (num_channels >= max_channels) {
|
||||
of_node_put(child);
|
||||
break;
|
||||
}
|
||||
|
@ -1184,8 +1285,28 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||
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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *id;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int bipolar_mask;
|
||||
|
@ -1195,10 +1316,10 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
int i;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
if (!dev->of_node)
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1206,7 +1327,7 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (irq <= 0)
|
||||
return -ENXIO;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1222,43 +1343,44 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (IS_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->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)
|
||||
return ret;
|
||||
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time, &xadc_trigger_handler,
|
||||
&xadc_buffer_ops);
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&xadc_trigger_handler,
|
||||
&xadc_buffer_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst");
|
||||
if (IS_ERR(xadc->convst_trigger)) {
|
||||
ret = PTR_ERR(xadc->convst_trigger);
|
||||
goto err_triggered_buffer_cleanup;
|
||||
}
|
||||
if (IS_ERR(xadc->convst_trigger))
|
||||
return PTR_ERR(xadc->convst_trigger);
|
||||
|
||||
xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev,
|
||||
"samplerate");
|
||||
if (IS_ERR(xadc->samplerate_trigger)) {
|
||||
ret = PTR_ERR(xadc->samplerate_trigger);
|
||||
goto err_free_convst_trigger;
|
||||
}
|
||||
if (IS_ERR(xadc->samplerate_trigger))
|
||||
return PTR_ERR(xadc->samplerate_trigger);
|
||||
}
|
||||
|
||||
xadc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(xadc->clk)) {
|
||||
ret = PTR_ERR(xadc->clk);
|
||||
goto err_free_samplerate_trigger;
|
||||
}
|
||||
xadc->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(xadc->clk))
|
||||
return PTR_ERR(xadc->clk);
|
||||
|
||||
ret = clk_prepare_enable(xadc->clk);
|
||||
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
|
||||
|
@ -1267,22 +1389,28 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||
ret = xadc_read_samplerate(xadc);
|
||||
if (ret < 0)
|
||||
goto err_free_samplerate_trigger;
|
||||
return ret;
|
||||
|
||||
if (ret > XADC_MAX_SAMPLERATE) {
|
||||
ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
|
||||
if (ret < 0)
|
||||
goto err_free_samplerate_trigger;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(dev), indio_dev);
|
||||
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);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 16; 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);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
bipolar_mask = 0;
|
||||
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);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1),
|
||||
bipolar_mask >> 16);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
/* Disable all alarms */
|
||||
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
|
||||
XADC_CONF1_ALARM_MASK);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
/* Set thresholds to min/max */
|
||||
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),
|
||||
xadc->threshold[i]);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Go to non-buffered mode */
|
||||
xadc_postdisable(indio_dev);
|
||||
|
||||
ret = iio_device_register(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;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver xadc_driver = {
|
||||
.probe = xadc_probe,
|
||||
.remove = xadc_remove,
|
||||
.driver = {
|
||||
.name = "xadc",
|
||||
.of_match_table = xadc_of_match_table,
|
||||
|
|
|
@ -155,9 +155,6 @@ err_out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Register value is msb aligned, the lower 4 bits are ignored */
|
||||
#define XADC_THRESHOLD_VALUE_SHIFT 4
|
||||
|
||||
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
|
@ -177,7 +174,8 @@ int xadc_read_event_value(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
/* MSB aligned */
|
||||
*val >>= 16 - chan->scan_type.realbits;
|
||||
|
||||
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);
|
||||
int ret = 0;
|
||||
|
||||
val <<= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
/* MSB aligned */
|
||||
val <<= 16 - chan->scan_type.realbits;
|
||||
|
||||
if (val < 0 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
|
|
@ -70,6 +70,11 @@ struct xadc {
|
|||
int irq;
|
||||
};
|
||||
|
||||
enum xadc_type {
|
||||
XADC_TYPE_S7, /* Series 7 */
|
||||
XADC_TYPE_US, /* UltraScale and UltraScale+ */
|
||||
};
|
||||
|
||||
struct xadc_ops {
|
||||
int (*read)(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);
|
||||
|
||||
unsigned int flags;
|
||||
enum xadc_type type;
|
||||
};
|
||||
|
||||
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));
|
||||
var5 = 131 * calib->res_heat_val + 65536;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ static int pms7003_probe(struct serdev_device *serdev)
|
|||
state->serdev = serdev;
|
||||
indio_dev->info = &pms7003_info;
|
||||
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->modes = INDIO_DIRECT_MODE;
|
||||
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_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,
|
||||
|
|
|
@ -488,24 +488,20 @@ int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
|
|||
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.
|
||||
* This function is only used when reading PROM coefficients
|
||||
*
|
||||
* @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;
|
||||
u16 n_rem = 0x0000, crc_read = prom[0], crc = (*prom & 0xF000) >> 12;
|
||||
u16 n_rem = 0x0000;
|
||||
|
||||
prom[len - 1] = 0;
|
||||
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
|
||||
|
||||
for (cnt = 0; cnt < len * 2; cnt++) {
|
||||
for (cnt = 0; cnt < MS_SENSORS_TP_PROM_WORDS_NB * 2; cnt++) {
|
||||
if (cnt % 2 == 1)
|
||||
n_rem ^= prom[cnt >> 1] & 0x00FF;
|
||||
else
|
||||
|
@ -518,10 +514,55 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
|||
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 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(
|
||||
dev_data->client,
|
||||
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;
|
||||
}
|
||||
|
||||
if (!ms_sensors_tp_crc_valid(dev_data->prom,
|
||||
MS_SENSORS_TP_PROM_WORDS_NB + 1)) {
|
||||
if (dev_data->hw->prom_len == 8)
|
||||
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,
|
||||
"Calibration coefficients crc check error\n");
|
||||
return -ENODEV;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <linux/i2c.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
|
||||
|
@ -25,6 +25,16 @@ struct ms_ht_dev {
|
|||
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
|
||||
* @client: i2c client
|
||||
|
@ -36,7 +46,8 @@ struct ms_ht_dev {
|
|||
struct ms_tp_dev {
|
||||
struct i2c_client *client;
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -189,6 +189,16 @@ config AD5764
|
|||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
tristate "Analog Devices AD5770R IDAC driver"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
@ -19,6 +19,7 @@ obj-$(CONFIG_AD5755) += ad5755.o
|
|||
obj-$(CONFIG_AD5755) += ad5758.o
|
||||
obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5766) += ad5766.o
|
||||
obj-$(CONFIG_AD5770R) += ad5770r.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
|
|
|
@ -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))
|
||||
regulator_disable(st->reg);
|
||||
error_disable_clk:
|
||||
if (clk)
|
||||
clk_disable_unprepare(clk);
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -599,8 +598,7 @@ static int adf4350_remove(struct spi_device *spi)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->clk)
|
||||
clk_disable_unprepare(st->clk);
|
||||
clk_disable_unprepare(st->clk);
|
||||
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "bmg160.h"
|
||||
|
||||
#define BMG160_IRQ_NAME "bmg160_event"
|
||||
|
@ -92,6 +93,7 @@
|
|||
|
||||
struct bmg160_data {
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
struct iio_mount_matrix orientation;
|
||||
|
@ -1061,6 +1063,13 @@ static const char *bmg160_match_acpi_device(struct device *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,
|
||||
const char *name)
|
||||
{
|
||||
|
@ -1077,6 +1086,22 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
data->irq = irq;
|
||||
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",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
|
|
|
@ -23,15 +23,20 @@ enum gyro_3d_channel {
|
|||
GYRO_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP GYRO_3D_CHANNEL_MAX
|
||||
struct gyro_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
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_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
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_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
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);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n");
|
||||
if (atomic_read(&gyro_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
gyro_state->gyro_val,
|
||||
sizeof(gyro_state->gyro_val));
|
||||
if (atomic_read(&gyro_state->common_attributes.data_ready)) {
|
||||
if (!gyro_state->timestamp)
|
||||
gyro_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &gyro_state->scan,
|
||||
gyro_state->timestamp);
|
||||
|
||||
gyro_state->timestamp = 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_Z_AXIS:
|
||||
offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
|
||||
gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
gyro_state->scan.gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
gyro_state->timestamp =
|
||||
hid_sensor_convert_timestamp(&gyro_state->common_attributes,
|
||||
*(s64 *)raw_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ config INV_MPU6050_I2C
|
|||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050/9150,
|
||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
||||
IAM20680 motion tracking devices over I2C.
|
||||
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
|
||||
and IAM20680 motion tracking devices over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
|
@ -28,7 +28,7 @@ config INV_MPU6050_SPI
|
|||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6000,
|
||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
||||
IAM20680 motion tracking devices over SPI.
|
||||
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
|
||||
and IAM20680 motion tracking devices over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
|
|
@ -160,6 +160,14 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
|||
.fifo_size = 512,
|
||||
.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,
|
||||
.name = "MPU6000",
|
||||
|
@ -1323,6 +1331,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
|||
case INV_MPU6000:
|
||||
case INV_MPU6500:
|
||||
case INV_MPU6515:
|
||||
case INV_MPU6880:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/* reset signal path (required for spi connection) */
|
||||
|
|
|
@ -177,6 +177,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
|||
{"mpu6050", INV_MPU6050},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu6515", INV_MPU6515},
|
||||
{"mpu6880", INV_MPU6880},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
|
@ -204,6 +205,10 @@ static const struct of_device_id inv_of_match[] = {
|
|||
.compatible = "invensense,mpu6515",
|
||||
.data = (void *)INV_MPU6515
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6880",
|
||||
.data = (void *)INV_MPU6880
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9150",
|
||||
.data = (void *)INV_MPU9150
|
||||
|
|
|
@ -70,6 +70,7 @@ enum inv_devices {
|
|||
INV_MPU6050,
|
||||
INV_MPU6500,
|
||||
INV_MPU6515,
|
||||
INV_MPU6880,
|
||||
INV_MPU6000,
|
||||
INV_MPU9150,
|
||||
INV_MPU9250,
|
||||
|
@ -373,6 +374,7 @@ struct inv_mpu6050_state {
|
|||
#define INV_MPU6000_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6050_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
||||
#define INV_MPU6880_WHOAMI_VALUE 0x78
|
||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||
|
|
|
@ -70,6 +70,7 @@ static const struct spi_device_id inv_mpu_id[] = {
|
|||
{"mpu6000", INV_MPU6000},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu6515", INV_MPU6515},
|
||||
{"mpu6880", INV_MPU6880},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
|
@ -96,6 +97,10 @@ static const struct of_device_id inv_of_match[] = {
|
|||
.compatible = "invensense,mpu6515",
|
||||
.data = (void *)INV_MPU6515
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6880",
|
||||
.data = (void *)INV_MPU6880
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9250",
|
||||
.data = (void *)INV_MPU9250
|
||||
|
|
|
@ -169,6 +169,36 @@ static const char * const iio_chan_info_postfix[] = {
|
|||
[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)
|
||||
/*
|
||||
* 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)
|
||||
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]);
|
||||
}
|
||||
|
||||
/* replace last space with a newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
@ -492,7 +525,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
|
|||
i = e->get(indio_dev, chan);
|
||||
if (i < 0)
|
||||
return i;
|
||||
else if (i >= e->num_items)
|
||||
else if (i >= e->num_items || !e->items[i])
|
||||
return -EINVAL;
|
||||
|
||||
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)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
|
@ -1473,11 +1506,14 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|||
goto error_clear_attrs;
|
||||
}
|
||||
/* Copy across original attributes */
|
||||
if (indio_dev->info->attrs)
|
||||
if (indio_dev->info->attrs) {
|
||||
memcpy(iio_dev_opaque->chan_attr_group.attrs,
|
||||
indio_dev->info->attrs->attrs,
|
||||
sizeof(iio_dev_opaque->chan_attr_group.attrs[0])
|
||||
*attrcount_orig);
|
||||
iio_dev_opaque->chan_attr_group.is_visible =
|
||||
indio_dev->info->attrs->is_visible;
|
||||
}
|
||||
attrn = attrcount_orig;
|
||||
/* Add all elements from the list. */
|
||||
list_for_each_entry(p, &iio_dev_opaque->channel_attr_list, l)
|
||||
|
|
|
@ -191,8 +191,8 @@ err_free_channel:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||
const char *name)
|
||||
struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||
const char *name)
|
||||
{
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name);
|
||||
|
||||
static struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
|
@ -272,12 +273,6 @@ error_free_chans:
|
|||
|
||||
#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)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -393,6 +388,29 @@ struct iio_channel *devm_iio_channel_get(struct device *dev,
|
|||
}
|
||||
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)
|
||||
{
|
||||
const char *name;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* TODO: gesture + proximity calib offsets
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -1113,6 +1114,12 @@ static const struct i2c_device_id 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[] = {
|
||||
{ .compatible = "avago,apds9960" },
|
||||
{ }
|
||||
|
@ -1124,6 +1131,7 @@ static struct i2c_driver apds9960_driver = {
|
|||
.name = APDS9960_DRV_NAME,
|
||||
.of_match_table = apds9960_of_match,
|
||||
.pm = &apds9960_pm_ops,
|
||||
.acpi_match_table = apds9960_acpi_match,
|
||||
},
|
||||
.probe = apds9960_probe,
|
||||
.remove = apds9960_remove,
|
||||
|
|
|
@ -22,15 +22,21 @@ enum {
|
|||
CHANNEL_SCAN_INDEX_MAX
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
|
||||
|
||||
struct als_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
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_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -54,7 +60,8 @@ static const struct iio_chan_spec als_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
@ -168,14 +175,6 @@ static const struct iio_info als_info = {
|
|||
.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 */
|
||||
static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
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);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "als_proc_event\n");
|
||||
if (atomic_read(&als_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
&als_state->illum,
|
||||
sizeof(als_state->illum));
|
||||
if (atomic_read(&als_state->common_attributes.data_ready)) {
|
||||
if (!als_state->timestamp)
|
||||
als_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &als_state->scan,
|
||||
als_state->timestamp);
|
||||
als_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -206,10 +209,14 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_LIGHT_ILLUM:
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||
als_state->scan.illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||
als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||
ret = 0;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes,
|
||||
*(s64 *)raw_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ static int tsl2583_get_lux(struct iio_dev *indio_dev)
|
|||
lux64 = lux64 * chip->als_settings.als_gain_trim;
|
||||
lux64 >>= 13;
|
||||
lux = lux64;
|
||||
lux = (lux + 500) / 1000;
|
||||
lux = DIV_ROUND_CLOSEST(lux, 1000);
|
||||
|
||||
if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
|
||||
return_max:
|
||||
|
@ -361,12 +361,12 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
|
|||
u8 val;
|
||||
|
||||
/* 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)
|
||||
als_count = 1; /* ensure at least one cycle */
|
||||
|
||||
/* 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;
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2)
|
|||
{
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -205,4 +205,19 @@ config SENSORS_RM3100_SPI
|
|||
To compile this driver as a module, choose M here: the module
|
||||
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
|
||||
|
|
|
@ -28,3 +28,5 @@ obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
|
|||
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
|
||||
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.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/triggered_buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "bmc150_magn.h"
|
||||
|
||||
|
@ -135,6 +136,7 @@ struct bmc150_magn_data {
|
|||
*/
|
||||
struct mutex mutex;
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct iio_mount_matrix orientation;
|
||||
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
|
||||
s32 buffer[6];
|
||||
|
@ -692,12 +694,24 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
|
|||
int ret, chip_id;
|
||||
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,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"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);
|
||||
|
@ -752,6 +766,8 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
|
|||
|
||||
err_poweroff:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -867,6 +883,13 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
|
|||
data->irq = irq;
|
||||
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",
|
||||
&data->orientation);
|
||||
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);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bmc150_magn_remove);
|
||||
|
|
|
@ -24,6 +24,7 @@ enum magn_3d_channel {
|
|||
CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
|
||||
CHANNEL_SCAN_INDEX_NORTH_MAGN,
|
||||
CHANNEL_SCAN_INDEX_NORTH_TRUE,
|
||||
CHANNEL_SCAN_INDEX_TIMESTAMP,
|
||||
MAGN_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
|
@ -47,6 +48,7 @@ struct magn_3d_state {
|
|||
|
||||
struct common_attributes magn_flux_attr;
|
||||
struct common_attributes rot_attr;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
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_MAGN_NORTH,
|
||||
HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
|
||||
HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -124,7 +127,8 @@ static const struct iio_chan_spec magn_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
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);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
|
||||
if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev, magn_state->iio_vals);
|
||||
if (atomic_read(&magn_state->magn_flux_attributes.data_ready)) {
|
||||
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;
|
||||
}
|
||||
|
@ -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)
|
||||
+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
|
||||
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:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -386,9 +395,10 @@ static int magn_3d_parse_report(struct platform_device *pdev,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
|
||||
sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
/* attr_count include timestamp channel, and the iio_vals should be aligned to 8byte */
|
||||
st->iio_vals = devm_kcalloc(&pdev->dev,
|
||||
((attr_count + 1) % 2 + (attr_count + 1) / 2) * 2,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!st->iio_vals) {
|
||||
dev_err(&pdev->dev,
|
||||
"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]).address = i;
|
||||
|
||||
/* Set magn_val_addr to iio value address */
|
||||
st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
|
||||
magn_3d_adjust_channel_bit_mask(_channels,
|
||||
*chan_count,
|
||||
st->magn[i].size);
|
||||
if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) {
|
||||
/* Set magn_val_addr to iio value address */
|
||||
st->magn_val_addr[i] = &st->iio_vals[*chan_count];
|
||||
magn_3d_adjust_channel_bit_mask(_channels,
|
||||
*chan_count,
|
||||
st->magn[i].size);
|
||||
}
|
||||
(*chan_count)++;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,15 +24,21 @@ enum incl_3d_channel {
|
|||
INCLI_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP INCLI_3D_CHANNEL_MAX
|
||||
|
||||
struct incl_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
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_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
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_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
|
||||
};
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
/* 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 */
|
||||
static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
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);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n");
|
||||
if (atomic_read(&incl_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)incl_state->incl_val,
|
||||
sizeof(incl_state->incl_val));
|
||||
if (atomic_read(&incl_state->common_attributes.data_ready)) {
|
||||
if (!incl_state->timestamp)
|
||||
incl_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
&incl_state->scan,
|
||||
incl_state->timestamp);
|
||||
|
||||
incl_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -214,13 +220,18 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
|
||||
switch (usage_id) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -20,11 +20,15 @@ struct dev_rot_state {
|
|||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
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_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
/* 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) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
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 */
|
||||
|
@ -70,7 +76,7 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_RAW:
|
||||
if (size >= 4) {
|
||||
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;
|
||||
*val_len = 4;
|
||||
} else
|
||||
|
@ -132,15 +138,6 @@ static const struct iio_info dev_rot_info = {
|
|||
.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 */
|
||||
static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
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);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
|
||||
if (atomic_read(&rot_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)rot_state->sampled_vals,
|
||||
sizeof(rot_state->sampled_vals));
|
||||
if (atomic_read(&rot_state->common_attributes.data_ready)) {
|
||||
if (!rot_state->timestamp)
|
||||
rot_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
|
||||
rot_state->timestamp);
|
||||
|
||||
rot_state->timestamp = 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);
|
||||
|
||||
if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
|
||||
memcpy(rot_state->sampled_vals, raw_data,
|
||||
sizeof(rot_state->sampled_vals));
|
||||
memcpy(&rot_state->scan.sampled_vals, raw_data,
|
||||
sizeof(rot_state->scan.sampled_vals));
|
||||
|
||||
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;
|
||||
|
|
|
@ -16,4 +16,20 @@ config IQS624_POS
|
|||
To compile this driver as a module, choose M here: the module
|
||||
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
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
# 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
|
||||
|
|
|
@ -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"
|
||||
|
||||
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 };
|
||||
/* 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,
|
||||
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[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -129,6 +145,7 @@ static const struct iio_info ms5637_info = {
|
|||
static int ms5637_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct ms_tp_data *data;
|
||||
struct ms_tp_dev *dev_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
@ -142,17 +159,25 @@ static int ms5637_probe(struct i2c_client *client,
|
|||
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));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_data = iio_priv(indio_dev);
|
||||
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);
|
||||
|
||||
indio_dev->info = &ms5637_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->name = data->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = 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);
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{"ms5637", 0},
|
||||
{"ms5805", 0},
|
||||
{"ms5837", 0},
|
||||
{"ms8607-temppressure", 0},
|
||||
{"ms5637", (kernel_ulong_t)&ms5637_data },
|
||||
{"ms5805", (kernel_ulong_t)&ms5805_data },
|
||||
{"ms5837", (kernel_ulong_t)&ms5837_data },
|
||||
{"ms8607-temppressure", (kernel_ulong_t)&ms8607_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ms5637_id);
|
||||
|
||||
static const struct of_device_id ms5637_of_match[] = {
|
||||
{ .compatible = "meas,ms5637", },
|
||||
{ .compatible = "meas,ms5805", },
|
||||
{ .compatible = "meas,ms5837", },
|
||||
{ .compatible = "meas,ms8607-temppressure", },
|
||||
{ .compatible = "meas,ms5637", .data = &ms5637_data },
|
||||
{ .compatible = "meas,ms5803", .data = &ms5803_data },
|
||||
{ .compatible = "meas,ms5805", .data = &ms5805_data },
|
||||
{ .compatible = "meas,ms5837", .data = &ms5837_data },
|
||||
{ .compatible = "meas,ms8607-temppressure", .data = &ms8607_data },
|
||||
{ },
|
||||
};
|
||||
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",
|
||||
sizeof(struct ashmem_range),
|
||||
0, 0, NULL);
|
||||
0, SLAB_RECLAIM_ACCOUNT, NULL);
|
||||
if (!ashmem_range_cachep) {
|
||||
pr_err("failed to create slab cache\n");
|
||||
goto out_free1;
|
||||
|
|
|
@ -3,7 +3,10 @@ config STAGING_BOARD
|
|||
bool "Staging Board Support"
|
||||
depends on OF_ADDRESS && OF_IRQ && CLKDEV_LOOKUP
|
||||
help
|
||||
Select to enable per-board staging support code.
|
||||
|
||||
If in doubt, say N here.
|
||||
Staging board base is to support continuous upstream
|
||||
in-tree development and integration of platform devices.
|
||||
|
||||
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 divider (output 0 only)
|
||||
- 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
|
||||
- overflow after multiplication?
|
||||
- maximize accuracy before divisions
|
||||
|
|
|
@ -939,8 +939,8 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
|
|||
/* fill devinfo structure */
|
||||
devinfo.version_code = COMEDI_VERSION_CODE;
|
||||
devinfo.n_subdevs = dev->n_subdevices;
|
||||
strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
|
||||
strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
|
||||
strscpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
|
||||
strscpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
|
||||
|
||||
s = comedi_file_read_subdevice(file);
|
||||
if (s)
|
||||
|
|
|
@ -19,14 +19,15 @@
|
|||
* PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
|
||||
* PCI-7434 (adl_pci7434)
|
||||
* Author: H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
* Updated: Thu, 02 Aug 2012 14:27:46 -0700
|
||||
* Status: untested
|
||||
* Updated: Fri, 20 Nov 2020 14:49:36 +0000
|
||||
* Status: works (tested on PCI-7230)
|
||||
*
|
||||
* One or two subdevices are setup by this driver depending on
|
||||
* the number of digital inputs and/or outputs provided by the
|
||||
* 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-7234 - 1 subdevice: 0 - 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
|
||||
* 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
|
||||
* lines of MSB. Interrupts are not currently supported by this
|
||||
* driver.
|
||||
* lines of MSB.
|
||||
*
|
||||
* Currently, this driver only supports interrupts for PCI-7230.
|
||||
*
|
||||
* Configuration Options: not applicable, uses comedi PCI auto config
|
||||
*/
|
||||
|
@ -47,13 +49,22 @@
|
|||
|
||||
#include "../comedi_pci.h"
|
||||
|
||||
#include "plx9052.h"
|
||||
|
||||
/*
|
||||
* Register I/O map (32-bit access only)
|
||||
*/
|
||||
#define PCI7X3X_DIO_REG 0x00
|
||||
#define PCI743X_DIO_REG 0x04
|
||||
#define PCI7X3X_DIO_REG 0x0000 /* in the DigIO Port area */
|
||||
#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_PCI7233,
|
||||
BOARD_PCI7234,
|
||||
|
@ -67,14 +78,16 @@ struct adl_pci7x3x_boardinfo {
|
|||
int nsubdevs;
|
||||
int di_nchan;
|
||||
int do_nchan;
|
||||
int irq_nchan;
|
||||
};
|
||||
|
||||
static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
|
||||
[BOARD_PCI7230] = {
|
||||
.name = "adl_pci7230",
|
||||
.nsubdevs = 2,
|
||||
.nsubdevs = 4, /* IDI, IDO, IRQ_IDI0, IRQ_IDI1 */
|
||||
.di_nchan = 16,
|
||||
.do_nchan = 16,
|
||||
.irq_nchan = 2,
|
||||
},
|
||||
[BOARD_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,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
|
@ -143,15 +328,28 @@ static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
|
|||
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,
|
||||
unsigned long context)
|
||||
{
|
||||
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
|
||||
const struct adl_pci7x3x_boardinfo *board = NULL;
|
||||
struct comedi_subdevice *s;
|
||||
struct adl_pci7x3x_dev_private_data *dev_private;
|
||||
int subdev;
|
||||
int nchan;
|
||||
int ret;
|
||||
int ic;
|
||||
|
||||
if (context < ARRAY_SIZE(adl_pci7x3x_boards))
|
||||
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_name = board->name;
|
||||
|
||||
dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
|
||||
if (!dev_private)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_pci_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.driver_name = "adl_pci7x3x",
|
||||
.module = THIS_MODULE,
|
||||
.auto_attach = adl_pci7x3x_auto_attach,
|
||||
.detach = comedi_pci_detach,
|
||||
.detach = adl_pci7x3x_detach,
|
||||
};
|
||||
|
||||
static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
|
||||
|
|
|
@ -34,9 +34,15 @@
|
|||
*/
|
||||
|
||||
/* PCI-1730, PCI-1733, PCI-1736 interrupt control registers */
|
||||
#define PCI173X_INT_EN_REG 0x08 /* R/W: enable/disable */
|
||||
#define PCI173X_INT_RF_REG 0x0c /* R/W: falling/rising edge */
|
||||
#define PCI173X_INT_CLR_REG 0x10 /* R/W: clear */
|
||||
#define PCI173X_INT_EN_REG 0x0008 /* R/W: enable/disable */
|
||||
#define PCI173X_INT_RF_REG 0x000c /* R/W: falling/rising edge */
|
||||
#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 */
|
||||
#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_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_IRQ_SUBDEVS 4 /* 4 x 1 input IRQ channels max */
|
||||
|
||||
enum pci_dio_boardid {
|
||||
TYPE_PCI1730,
|
||||
|
@ -84,7 +91,12 @@ enum pci_dio_boardid {
|
|||
|
||||
struct diosubd_data {
|
||||
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 {
|
||||
|
@ -93,6 +105,7 @@ struct dio_boardtype {
|
|||
struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
|
||||
struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
|
||||
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 timer_regbase;
|
||||
unsigned int is_16bit:1;
|
||||
|
@ -101,12 +114,17 @@ struct dio_boardtype {
|
|||
static const struct dio_boardtype boardtypes[] = {
|
||||
[TYPE_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[1] = { 16, 0x00, }, /* ISO DI 0-15 */
|
||||
.sdo[0] = { 16, 0x02, }, /* DO 0-15 */
|
||||
.sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
|
||||
.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] = {
|
||||
.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,
|
||||
struct comedi_subdevice *s,
|
||||
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)
|
||||
{
|
||||
struct pci_dio_dev_private_data *dev_private = dev->private;
|
||||
/* disable channel freeze function on the PCI-1752/1756 boards */
|
||||
if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
|
||||
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_PCI1733:
|
||||
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(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;
|
||||
case TYPE_PCI1739:
|
||||
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);
|
||||
const struct dio_boardtype *board = NULL;
|
||||
const struct diosubd_data *d;
|
||||
struct comedi_subdevice *s;
|
||||
struct pci_dio_dev_private_data *dev_private;
|
||||
int ret, subdev, i, j;
|
||||
|
||||
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_name = board->name;
|
||||
|
||||
dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
|
||||
if (!dev_private)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = comedi_pci_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -365,15 +574,25 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
|
|||
else
|
||||
dev->iobase = pci_resource_start(pcidev, 2);
|
||||
|
||||
dev_private->boardtype = 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);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
subdev = 0;
|
||||
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) {
|
||||
s = &dev->subdevices[subdev++];
|
||||
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_b;
|
||||
s->private = (void *)d->addr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
s = &dev->subdevices[subdev++];
|
||||
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++) {
|
||||
d = &board->sdio[i];
|
||||
const struct diosubd_data *d = &board->sdio[i];
|
||||
|
||||
for (j = 0; j < d->chans; j++) {
|
||||
s = &dev->subdevices[subdev++];
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.driver_name = "adv_pci_dio",
|
||||
.module = THIS_MODULE,
|
||||
.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,
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
#define DRIVER_DESC "EMXX UDC driver"
|
||||
#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_desc[] = DRIVER_DESC;
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
/* below hacked up for staging integration */
|
||||
#define GPIO_VBUS 0 /* GPIO_P153 on KZM9D */
|
||||
#define INT_VBUS 0 /* IRQ for GPIO_P153 */
|
||||
struct gpio_desc *vbus_gpio;
|
||||
int vbus_irq;
|
||||
|
||||
/*------------ Board dependence(Wait) */
|
||||
|
||||
|
|
|
@ -7,9 +7,13 @@
|
|||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include "fbtft.h"
|
||||
|
@ -66,6 +70,62 @@ enum st7789v_command {
|
|||
#define MADCTL_MX BIT(6) /* bitmask for column 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
|
||||
*
|
||||
|
@ -82,6 +142,12 @@ enum st7789v_command {
|
|||
*/
|
||||
static int init_display(struct fbtft_par *par)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = init_tearing_effect_line(par);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* turn off sleep mode */
|
||||
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
|
||||
mdelay(120);
|
||||
|
@ -137,6 +203,10 @@ static int init_display(struct fbtft_par *par)
|
|||
*/
|
||||
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);
|
||||
|
||||
if (HSD20_IPS)
|
||||
|
@ -145,6 +215,50 @@ static int init_display(struct fbtft_par *par)
|
|||
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
|
||||
*
|
||||
|
@ -259,6 +373,7 @@ static struct fbtft_display display = {
|
|||
.gamma = HSD20_IPS_GAMMA,
|
||||
.fbtftops = {
|
||||
.init_display = init_display,
|
||||
.write_vmem = write_vmem,
|
||||
.set_var = set_var,
|
||||
.set_gamma = set_gamma,
|
||||
.blank = blank,
|
||||
|
|
|
@ -185,7 +185,7 @@ static struct attribute *controller_attributes[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group controller_attribute_group = {
|
||||
static const struct attribute_group controller_attribute_group = {
|
||||
.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);
|
||||
}
|
||||
|
||||
static struct regulator_ops can_power_ops = {
|
||||
static const struct regulator_ops can_power_ops = {
|
||||
.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;
|
||||
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,
|
||||
&version_major,
|
||||
&version_minor);
|
||||
if (err)
|
||||
strlcpy(drvinfo->fw_version, "N/A",
|
||||
strscpy(drvinfo->fw_version, "N/A",
|
||||
sizeof(drvinfo->fw_version));
|
||||
else
|
||||
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
|
||||
"%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));
|
||||
}
|
||||
|
||||
|
|
|
@ -2189,6 +2189,7 @@ static int fwserial_create(struct fw_unit *unit)
|
|||
err = fw_core_add_address_handler(&port->rx_handler,
|
||||
&fw_high_memory_region);
|
||||
if (err) {
|
||||
tty_port_destroy(&port->port);
|
||||
kfree(port);
|
||||
goto free_ports;
|
||||
}
|
||||
|
@ -2271,6 +2272,7 @@ unregister_ttys:
|
|||
|
||||
free_ports:
|
||||
for (--i; i >= 0; --i) {
|
||||
fw_core_remove_address_handler(&serial->ports[i]->rx_handler);
|
||||
tty_port_destroy(&serial->ports[i]->port);
|
||||
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. */
|
||||
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;
|
||||
struct gasket_page_table_ioctl ibuf;
|
||||
struct gasket_page_table *table;
|
||||
|
||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||
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)
|
||||
return -EFAULT;
|
||||
|
||||
ibuf.size = gasket_page_table_num_entries(
|
||||
gasket_dev->page_table[ibuf.page_table_index]);
|
||||
table = 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,
|
||||
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. */
|
||||
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;
|
||||
struct gasket_page_table_ioctl ibuf;
|
||||
struct gasket_page_table *table;
|
||||
|
||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||
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)
|
||||
return -EFAULT;
|
||||
|
||||
ibuf.size =
|
||||
gasket_page_table_num_simple_entries(gasket_dev->page_table[ibuf.page_table_index]);
|
||||
table = 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,
|
||||
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. */
|
||||
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;
|
||||
struct gasket_page_table_ioctl ibuf;
|
||||
uint max_page_table_size;
|
||||
struct gasket_page_table *table;
|
||||
|
||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||
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)
|
||||
return -EFAULT;
|
||||
max_page_table_size = gasket_page_table_max_size(
|
||||
gasket_dev->page_table[ibuf.page_table_index]);
|
||||
table = 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) {
|
||||
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);
|
||||
|
||||
ret = gasket_page_table_partition(
|
||||
gasket_dev->page_table[ibuf.page_table_index], ibuf.size);
|
||||
ret = gasket_page_table_partition(table, ibuf.size);
|
||||
mutex_unlock(&gasket_dev->mutex);
|
||||
|
||||
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 ibuf;
|
||||
struct gasket_page_table *table;
|
||||
|
||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||
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)
|
||||
return -EFAULT;
|
||||
|
||||
if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[ibuf.page_table_index],
|
||||
ibuf.host_address,
|
||||
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||
if (gasket_page_table_are_addrs_bad(table, ibuf.host_address,
|
||||
ibuf.device_address, ibuf.size))
|
||||
return -EINVAL;
|
||||
|
||||
return gasket_page_table_map(gasket_dev->page_table[ibuf.page_table_index],
|
||||
ibuf.host_address, ibuf.device_address,
|
||||
return gasket_page_table_map(table, ibuf.host_address, ibuf.device_address,
|
||||
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 ibuf;
|
||||
struct gasket_page_table *table;
|
||||
|
||||
if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
|
||||
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)
|
||||
return -EFAULT;
|
||||
|
||||
if (gasket_page_table_is_dev_addr_bad(gasket_dev->page_table[ibuf.page_table_index],
|
||||
ibuf.device_address, ibuf.size))
|
||||
table = gasket_dev->page_table[ibuf.page_table_index];
|
||||
if (gasket_page_table_is_dev_addr_bad(table, ibuf.device_address, ibuf.size))
|
||||
return -EINVAL;
|
||||
|
||||
gasket_page_table_unmap(gasket_dev->page_table[ibuf.page_table_index],
|
||||
ibuf.device_address, ibuf.size / PAGE_SIZE);
|
||||
gasket_page_table_unmap(table, ibuf.device_address, ibuf.size / PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -183,7 +185,7 @@ static int gasket_unmap_buffers(struct gasket_dev *gasket_dev,
|
|||
* corresponding memory.
|
||||
*/
|
||||
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;
|
||||
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)
|
||||
{
|
||||
u8 buf[16] = {0,};
|
||||
struct hci_packet *hci = (struct hci_packet *)buf;
|
||||
struct hci_packet *hci;
|
||||
struct usb_device *usbdev = udev->usbdev;
|
||||
int actual;
|
||||
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->len = gdm_cpu_to_dev16(udev->gdm_ed, 1);
|
||||
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);
|
||||
|
||||
udev->request_mac_addr = 1;
|
||||
kfree(hci);
|
||||
|
||||
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,
|
||||
control->name);
|
||||
else
|
||||
strlcpy(id.name, control->name, sizeof(id.name));
|
||||
strscpy(id.name, control->name, sizeof(id.name));
|
||||
id.numid = 0;
|
||||
id.iface = control->iface;
|
||||
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} };
|
||||
|
||||
int num = sscanf(buf,
|
||||
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s "
|
||||
"vid=%d pid=%d intf_id=%d i/p devices=0x%X o/p devices=0x%X",
|
||||
"name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF
|
||||
"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.ip_devices, &desc.op_devices);
|
||||
|
||||
|
|
|
@ -342,7 +342,7 @@ static int gb_audio_probe(struct gb_bundle *bundle,
|
|||
/* inform above layer for uevent */
|
||||
dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
|
||||
/* 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.pid = 3; /* todo */
|
||||
desc.intf_id = gbmodule->dev_id;
|
||||
|
|
|
@ -200,7 +200,7 @@ static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
|
|||
return -EINVAL;
|
||||
name = gbaudio_map_controlid(module, data->ctl_id,
|
||||
uinfo->value.enumerated.item);
|
||||
strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE);
|
||||
strscpy(uinfo->value.enumerated.name, name, NAME_SIZE);
|
||||
break;
|
||||
default:
|
||||
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 */
|
||||
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);
|
||||
|
||||
switch (w->type) {
|
||||
|
@ -1169,7 +1169,7 @@ static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module,
|
|||
}
|
||||
control->id = curr->id;
|
||||
/* 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,
|
||||
temp_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,
|
||||
unsigned char report_number, __u8 *buf, size_t count,
|
||||
unsigned char report_type)
|
||||
unsigned char report_number, __u8 *buf, size_t count,
|
||||
unsigned char report_type)
|
||||
{
|
||||
struct gb_hid *ghid = hid->driver_data;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -290,8 +290,7 @@ static int channel_attr_groups_set(struct gb_channel *channel,
|
|||
channel->attrs = kcalloc(size + 1, sizeof(*channel->attrs), GFP_KERNEL);
|
||||
if (!channel->attrs)
|
||||
return -ENOMEM;
|
||||
channel->attr_group = kcalloc(1, sizeof(*channel->attr_group),
|
||||
GFP_KERNEL);
|
||||
channel->attr_group = kzalloc(sizeof(*channel->attr_group), GFP_KERNEL);
|
||||
if (!channel->attr_group)
|
||||
return -ENOMEM;
|
||||
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))
|
||||
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))) {
|
||||
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;
|
||||
|
||||
if (dev_type == GB_SPI_SPI_DEV)
|
||||
strlcpy(spi_board.modalias, "spidev",
|
||||
strscpy(spi_board.modalias, "spidev",
|
||||
sizeof(spi_board.modalias));
|
||||
else if (dev_type == GB_SPI_SPI_NOR)
|
||||
strlcpy(spi_board.modalias, "spi-nor",
|
||||
strscpy(spi_board.modalias, "spi-nor",
|
||||
sizeof(spi_board.modalias));
|
||||
else if (dev_type == GB_SPI_SPI_MODALIAS)
|
||||
memcpy(spi_board.modalias, response.name,
|
||||
|
|
|
@ -29,6 +29,7 @@ config MFD_HI6421_SPMI
|
|||
depends on OF
|
||||
depends on SPMI
|
||||
select MFD_CORE
|
||||
select REGMAP_SPMI
|
||||
help
|
||||
Add support for HiSilicon Hi6421v600 SPMI PMIC. Hi6421 includes
|
||||
multi-functions, such as regulators, RTC, codec, Coulomb counter,
|
||||
|
@ -44,6 +45,7 @@ config REGULATOR_HI6421V600
|
|||
tristate "HiSilicon Hi6421v600 PMIC voltage regulator support"
|
||||
depends on MFD_HI6421_SPMI && OF
|
||||
depends on REGULATOR
|
||||
select REGMAP
|
||||
help
|
||||
This driver provides support for the voltage regulators on
|
||||
HiSilicon Hi6421v600 PMU / Codec IC.
|
||||
|
|
|
@ -4,149 +4,105 @@
|
|||
*
|
||||
* Copyright (c) 2013 Linaro Ltd.
|
||||
* Copyright (c) 2011 Hisilicon.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/hi6421-spmi-pmic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spmi.h>
|
||||
|
||||
/* 8-bit register offset in PMIC */
|
||||
#define HISI_MASK_STATE 0xff
|
||||
enum hi6421_spmi_pmic_irq_list {
|
||||
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_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_IRQ0_ADDR 0x0212
|
||||
|
||||
#define HISI_IRQ_KEY_NUM 0
|
||||
#define HISI_IRQ_KEY_VALUE 0xc0
|
||||
#define HISI_IRQ_KEY_DOWN 7
|
||||
#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
|
||||
#define IRQ_MASK_REGISTER(irq_data) (SOC_PMIC_IRQ_MASK_0_ADDR + \
|
||||
(irqd_to_hwirq(irq_data) >> 3))
|
||||
#define IRQ_MASK_BIT(irq_data) BIT(irqd_to_hwirq(irq_data) & 0x07)
|
||||
|
||||
static const struct mfd_cell hi6421v600_devs[] = {
|
||||
{ .name = "hi6421v600-regulator", },
|
||||
};
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static irqreturn_t hi6421_spmi_irq_handler(int irq, void *priv)
|
||||
{
|
||||
struct spmi_device *pdev;
|
||||
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;
|
||||
struct hi6421_spmi_pmic *ddata = (struct hi6421_spmi_pmic *)priv;
|
||||
unsigned long pending;
|
||||
unsigned int in;
|
||||
int i, offset;
|
||||
|
||||
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
|
||||
pending = hi6421_spmi_pmic_read(pmic, (i + SOC_PMIC_IRQ0_ADDR));
|
||||
pending &= HISI_MASK_FIELD;
|
||||
if (pending != 0)
|
||||
pr_debug("pending[%d]=0x%lx\n\r", i, pending);
|
||||
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);
|
||||
pending = HISI_MASK & in;
|
||||
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, pending);
|
||||
|
||||
hi6421_spmi_pmic_write(pmic, (i + SOC_PMIC_IRQ0_ADDR), pending);
|
||||
|
||||
/* solve powerkey order */
|
||||
if ((i == HISI_IRQ_KEY_NUM) &&
|
||||
((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]);
|
||||
if (i == HISI_IRQ_KEY_NUM &&
|
||||
(pending & HISI_IRQ_KEY_VALUE) == HISI_IRQ_KEY_VALUE) {
|
||||
generic_handle_irq(ddata->irqs[POWERKEY_DOWN]);
|
||||
generic_handle_irq(ddata->irqs[POWERKEY_UP]);
|
||||
pending &= (~HISI_IRQ_KEY_VALUE);
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
for_each_set_bit(offset, &pending, HISI_BITS)
|
||||
generic_handle_irq(pmic->irqs[offset + i * HISI_BITS]);
|
||||
}
|
||||
if (!pending)
|
||||
continue;
|
||||
|
||||
for_each_set_bit(offset, &pending, HISI_BITS)
|
||||
generic_handle_irq(ddata->irqs[offset + i * HISI_BITS]);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct hi6421_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
|
||||
u32 data, offset;
|
||||
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
|
||||
unsigned long flags;
|
||||
unsigned int data;
|
||||
u32 offset;
|
||||
|
||||
offset = (irqd_to_hwirq(d) >> 3);
|
||||
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
|
||||
offset = IRQ_MASK_REGISTER(d);
|
||||
|
||||
spin_lock_irqsave(&pmic->lock, flags);
|
||||
data = hi6421_spmi_pmic_read(pmic, offset);
|
||||
data |= (1 << (irqd_to_hwirq(d) & 0x07));
|
||||
hi6421_spmi_pmic_write(pmic, offset, data);
|
||||
spin_unlock_irqrestore(&pmic->lock, flags);
|
||||
spin_lock_irqsave(&ddata->lock, flags);
|
||||
|
||||
regmap_read(ddata->regmap, offset, &data);
|
||||
data |= IRQ_MASK_BIT(d);
|
||||
regmap_write(ddata->regmap, offset, data);
|
||||
|
||||
spin_unlock_irqrestore(&ddata->lock, flags);
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned long flags;
|
||||
|
||||
offset = (irqd_to_hwirq(d) >> 3);
|
||||
offset += SOC_PMIC_IRQ_MASK_0_ADDR;
|
||||
|
||||
spin_lock_irqsave(&pmic->lock, flags);
|
||||
data = hi6421_spmi_pmic_read(pmic, offset);
|
||||
spin_lock_irqsave(&ddata->lock, flags);
|
||||
|
||||
regmap_read(ddata->regmap, offset, &data);
|
||||
data &= ~(1 << (irqd_to_hwirq(d) & 0x07));
|
||||
hi6421_spmi_pmic_write(pmic, offset, data);
|
||||
spin_unlock_irqrestore(&pmic->lock, flags);
|
||||
regmap_write(ddata->regmap, offset, data);
|
||||
|
||||
spin_unlock_irqrestore(&ddata->lock, flags);
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
handle_simple_irq, "hisi");
|
||||
irq_set_chip_data(virq, pmic);
|
||||
irq_set_chip_data(virq, ddata);
|
||||
irq_set_irq_type(virq, IRQ_TYPE_NONE);
|
||||
|
||||
return 0;
|
||||
|
@ -210,118 +170,111 @@ static const struct irq_domain_ops hi6421_spmi_domain_ops = {
|
|||
.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++)
|
||||
hi6421_spmi_pmic_write(pmic, SOC_PMIC_IRQ_MASK_0_ADDR + i,
|
||||
HISI_MASK_STATE);
|
||||
for (i = 0; i < HISI_IRQ_ARRAY; i++)
|
||||
regmap_write(ddata->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
|
||||
HISI_MASK);
|
||||
|
||||
for (i = 0 ; i < HISI_IRQ_ARRAY; i++) {
|
||||
pending = hi6421_spmi_pmic_read(pmic, SOC_PMIC_IRQ0_ADDR + i);
|
||||
|
||||
pr_debug("PMU IRQ address value:irq[0x%x] = 0x%x\n",
|
||||
SOC_PMIC_IRQ0_ADDR + i, pending);
|
||||
hi6421_spmi_pmic_write(pmic, SOC_PMIC_IRQ0_ADDR + i,
|
||||
HISI_MASK_STATE);
|
||||
for (i = 0; i < HISI_IRQ_ARRAY; i++) {
|
||||
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
|
||||
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i,
|
||||
HISI_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct hi6421_spmi_pmic *pmic;
|
||||
struct hi6421_spmi_pmic *ddata;
|
||||
unsigned int virq;
|
||||
int ret, i;
|
||||
|
||||
pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
|
||||
if (!pmic)
|
||||
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
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);
|
||||
if (pmic->gpio < 0)
|
||||
return pmic->gpio;
|
||||
ddata->dev = dev;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
dev_err(dev, "failed to request gpio%d\n", pmic->gpio);
|
||||
dev_err(dev, "Failed to request gpio%d\n", ddata->gpio);
|
||||
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);
|
||||
if (!pmic->irqs) {
|
||||
ret = -ENOMEM;
|
||||
goto irq_malloc;
|
||||
}
|
||||
ddata->irqs = devm_kzalloc(dev, HISI_IRQ_NUM * sizeof(int), GFP_KERNEL);
|
||||
if (!ddata->irqs)
|
||||
return -ENOMEM;
|
||||
|
||||
pmic->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0,
|
||||
&hi6421_spmi_domain_ops, pmic);
|
||||
if (!pmic->domain) {
|
||||
dev_err(dev, "failed irq domain add simple!\n");
|
||||
ret = -ENODEV;
|
||||
goto irq_malloc;
|
||||
ddata->domain = irq_domain_add_simple(np, HISI_IRQ_NUM, 0,
|
||||
&hi6421_spmi_domain_ops, ddata);
|
||||
if (!ddata->domain) {
|
||||
dev_err(dev, "Failed to create IRQ domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < HISI_IRQ_NUM; i++) {
|
||||
virq = irq_create_mapping(pmic->domain, i);
|
||||
virq = irq_create_mapping(ddata->domain, i);
|
||||
if (!virq) {
|
||||
dev_err(dev, "Failed mapping hwirq\n");
|
||||
ret = -ENOSPC;
|
||||
goto irq_malloc;
|
||||
dev_err(dev, "Failed to map H/W IRQ\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
pmic->irqs[i] = virq;
|
||||
dev_dbg(dev, "%s: pmic->irqs[%d] = %d\n",
|
||||
__func__, i, pmic->irqs[i]);
|
||||
ddata->irqs[i] = virq;
|
||||
}
|
||||
|
||||
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,
|
||||
"pmic", pmic);
|
||||
"pmic", ddata);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "could not claim pmic IRQ: error %d\n", ret);
|
||||
goto irq_malloc;
|
||||
dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
|
||||
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,
|
||||
hi6421v600_devs, ARRAY_SIZE(hi6421v600_devs),
|
||||
NULL, 0, NULL);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
dev_err(dev, "Failed to add child devices: %d\n", ret);
|
||||
|
||||
irq_malloc:
|
||||
free_irq(pmic->irq, pmic);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to add child devices: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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[] = {
|
||||
|
|
|
@ -1,183 +1,148 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Device driver for regulators in Hisi IC
|
||||
*
|
||||
* Copyright (c) 2013 Linaro Ltd.
|
||||
* Copyright (c) 2011 Hisilicon.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
//
|
||||
// Device driver for regulators in Hisi IC
|
||||
//
|
||||
// Copyright (c) 2013 Linaro Ltd.
|
||||
// Copyright (c) 2011 Hisilicon.
|
||||
// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
|
||||
//
|
||||
// Guodong Xu <guodong.xu@linaro.org>
|
||||
|
||||
#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/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.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/time.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define rdev_dbg(rdev, fmt, arg...) \
|
||||
pr_debug("%s: %s: " fmt, (rdev)->desc->name, __func__, ##arg)
|
||||
|
||||
struct hi6421v600_regulator {
|
||||
struct regulator_desc rdesc;
|
||||
struct hi6421_spmi_reg_info {
|
||||
struct regulator_desc desc;
|
||||
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
|
||||
};
|
||||
|
||||
/*
|
||||
* helper function to ensure when it returns it is at least 'delay_us'
|
||||
* microseconds after 'since'.
|
||||
static const unsigned int ldo4_voltages[] = {
|
||||
1725000, 1750000, 1775000, 1800000,
|
||||
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
|
||||
*/
|
||||
|
||||
static int hi6421_spmi_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct hi6421v600_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = hi6421_spmi_pmic_read(pmic, rdev->desc->enable_reg);
|
||||
|
||||
rdev_dbg(rdev,
|
||||
"enable_reg=0x%x, val= 0x%x, enable_state=%d\n",
|
||||
rdev->desc->enable_reg,
|
||||
reg_val, (reg_val & rdev->desc->enable_mask));
|
||||
|
||||
return ((reg_val & rdev->desc->enable_mask) != 0);
|
||||
}
|
||||
#define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \
|
||||
odelay, etime, ecomask, ecoamp) \
|
||||
[HI6421V600_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.of_match = of_match_ptr(#_id), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.ops = &hi6421_spmi_ldo_rops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421V600_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.volt_table = vtable, \
|
||||
.n_voltages = ARRAY_SIZE(vtable), \
|
||||
.vsel_mask = (1 << (ARRAY_SIZE(vtable) - 1)) - 1, \
|
||||
.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)
|
||||
{
|
||||
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;
|
||||
int ret;
|
||||
|
||||
/* cannot enable more than one regulator at one time */
|
||||
mutex_lock(&enable_mutex);
|
||||
usleep_range(HISI_REGS_ENA_PROTECT_TIME,
|
||||
HISI_REGS_ENA_PROTECT_TIME + 1000);
|
||||
mutex_lock(&sreg->enable_mutex);
|
||||
|
||||
/* set enable register */
|
||||
rdev_dbg(rdev,
|
||||
"off_on_delay=%d us, enable_reg=0x%x, enable_mask=0x%x\n",
|
||||
rdev->desc->off_on_delay, rdev->desc->enable_reg,
|
||||
rdev->desc->enable_mask);
|
||||
ret = regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
||||
rdev->desc->enable_mask,
|
||||
rdev->desc->enable_mask);
|
||||
|
||||
hi6421_spmi_pmic_rmw(pmic, rdev->desc->enable_reg,
|
||||
rdev->desc->enable_mask,
|
||||
rdev->desc->enable_mask);
|
||||
/* Avoid powering up multiple devices at the same time */
|
||||
usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60);
|
||||
|
||||
mutex_unlock(&enable_mutex);
|
||||
mutex_unlock(&sreg->enable_mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* set enable register to 0 */
|
||||
rdev_dbg(rdev, "enable_reg=0x%x, enable_mask=0x%x\n",
|
||||
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;
|
||||
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
||||
rdev->desc->enable_mask, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned int mode;
|
||||
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)
|
||||
mode = REGULATOR_MODE_IDLE;
|
||||
else
|
||||
mode = REGULATOR_MODE_NORMAL;
|
||||
return REGULATOR_MODE_IDLE;
|
||||
|
||||
rdev_dbg(rdev,
|
||||
"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;
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
|
||||
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;
|
||||
u32 val;
|
||||
|
||||
|
@ -192,14 +157,8 @@ static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set mode */
|
||||
rdev_dbg(rdev, "enable_reg=0x%x, eco_mode_mask=0x%x, value=0x%x\n",
|
||||
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;
|
||||
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
||||
sreg->eco_mode_mask, val);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
|
@ -207,199 +166,84 @@ hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev,
|
|||
int input_uV, int output_uV,
|
||||
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_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 = {
|
||||
.is_enabled = hi6421_spmi_regulator_is_enabled,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = hi6421_spmi_regulator_enable,
|
||||
.disable = hi6421_spmi_regulator_disable,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_iterate,
|
||||
.get_voltage_sel = hi6421_spmi_regulator_get_voltage_sel,
|
||||
.set_voltage_sel = hi6421_spmi_regulator_set_voltage_sel,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_mode = hi6421_spmi_regulator_get_mode,
|
||||
.set_mode = hi6421_spmi_regulator_set_mode,
|
||||
.get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode,
|
||||
};
|
||||
|
||||
static int hi6421_spmi_regulator_probe_ldo(struct platform_device *pdev,
|
||||
struct device_node *np,
|
||||
struct hi6421_spmi_pmic *pmic)
|
||||
{
|
||||
struct regulation_constraints *constraint;
|
||||
struct regulator_init_data *initdata;
|
||||
struct regulator_config config = { };
|
||||
struct hi6421v600_regulator *sreg;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct regulator_desc *rdesc;
|
||||
struct regulator_dev *rdev;
|
||||
const char *supplyname;
|
||||
int ret;
|
||||
/* HI6421v600 regulators with known registers */
|
||||
enum hi6421_spmi_regulator_id {
|
||||
HI6421V600_LDO3,
|
||||
HI6421V600_LDO4,
|
||||
HI6421V600_LDO9,
|
||||
HI6421V600_LDO15,
|
||||
HI6421V600_LDO16,
|
||||
HI6421V600_LDO17,
|
||||
HI6421V600_LDO33,
|
||||
HI6421V600_LDO34,
|
||||
};
|
||||
|
||||
initdata = of_get_regulator_init_data(dev, np, NULL);
|
||||
if (!initdata) {
|
||||
dev_err(dev, "failed to get regulator data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
|
||||
if (!sreg)
|
||||
return -ENOMEM;
|
||||
|
||||
sreg->pmic = pmic;
|
||||
rdesc = &sreg->rdesc;
|
||||
|
||||
rdesc->name = initdata->constraints.name;
|
||||
rdesc->ops = &hi6421_spmi_ldo_rops;
|
||||
rdesc->type = REGULATOR_VOLTAGE;
|
||||
rdesc->min_uV = initdata->constraints.min_uV;
|
||||
|
||||
supplyname = of_get_property(np, "supply_name", NULL);
|
||||
if (supplyname)
|
||||
initdata->supply_regulator = supplyname;
|
||||
|
||||
/* parse device tree data for regulator specific */
|
||||
ret = hi6421_spmi_dt_parse(pdev, sreg, rdesc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* hisi regulator supports two modes */
|
||||
constraint = &initdata->constraints;
|
||||
|
||||
constraint->valid_modes_mask = REGULATOR_MODE_NORMAL;
|
||||
if (sreg->eco_mode_mask) {
|
||||
constraint->valid_modes_mask |= REGULATOR_MODE_IDLE;
|
||||
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 struct hi6421_spmi_reg_info regulator_info[] = {
|
||||
HI6421V600_LDO(LDO3, ldo3_voltages,
|
||||
0x16, 0x01, 0x51,
|
||||
20000, 120,
|
||||
0, 0),
|
||||
HI6421V600_LDO(LDO4, ldo4_voltages,
|
||||
0x17, 0x01, 0x52,
|
||||
20000, 120,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO9, ldo9_voltages,
|
||||
0x1c, 0x01, 0x57,
|
||||
20000, 360,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO15, ldo15_voltages,
|
||||
0x21, 0x01, 0x5c,
|
||||
20000, 360,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO16, ldo15_voltages,
|
||||
0x22, 0x01, 0x5d,
|
||||
20000, 360,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO17, ldo17_voltages,
|
||||
0x23, 0x01, 0x5e,
|
||||
20000, 120,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO33, ldo17_voltages,
|
||||
0x32, 0x01, 0x6d,
|
||||
20000, 120,
|
||||
0, 0),
|
||||
HI6421V600_LDO(LDO34, ldo34_voltages,
|
||||
0x33, 0x01, 0x6e,
|
||||
20000, 120,
|
||||
0, 0),
|
||||
};
|
||||
|
||||
static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *pmic_dev = pdev->dev.parent;
|
||||
struct device_node *np = pmic_dev->of_node;
|
||||
struct device_node *regulators, *child;
|
||||
struct platform_device *new_pdev;
|
||||
struct regulator_config config = { };
|
||||
struct hi6421_spmi_reg_info *sreg;
|
||||
struct hi6421_spmi_reg_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct hi6421_spmi_pmic *pmic;
|
||||
int ret;
|
||||
struct regulator_dev *rdev;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* 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))
|
||||
return -ENODEV;
|
||||
|
||||
regulators = of_get_child_by_name(np, "regulators");
|
||||
if (!regulators) {
|
||||
dev_err(&pdev->dev, "regulator node not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
|
||||
if (!sreg)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Parse all LDO regulator nodes
|
||||
*/
|
||||
for_each_child_of_node(regulators, child) {
|
||||
dev_dbg(&pdev->dev, "adding child %pOF\n", child);
|
||||
sreg->pmic = pmic;
|
||||
mutex_init(&sreg->enable_mutex);
|
||||
|
||||
new_pdev = platform_device_alloc(child->name, -1);
|
||||
new_pdev->dev.parent = pmic_dev;
|
||||
new_pdev->dev.of_node = of_node_get(child);
|
||||
for (i = 0; i < ARRAY_SIZE(regulator_info); i++) {
|
||||
info = ®ulator_info[i];
|
||||
|
||||
ret = platform_device_add(new_pdev);
|
||||
if (ret < 0) {
|
||||
platform_device_put(new_pdev);
|
||||
continue;
|
||||
config.dev = pdev->dev.parent;
|
||||
config.driver_data = sreg;
|
||||
config.regmap = pmic->regmap;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int hi6421_spmi_regulator_remove(struct platform_device *pdev)
|
||||
{
|
||||
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[] = {
|
||||
static const struct platform_device_id hi6421_spmi_regulator_table[] = {
|
||||
{ .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 = {
|
||||
.id_table = hi6421v600_regulator_table,
|
||||
static struct platform_driver hi6421_spmi_regulator_driver = {
|
||||
.id_table = hi6421_spmi_regulator_table,
|
||||
.driver = {
|
||||
.name = "hi6421v600-regulator",
|
||||
.name = "hi6421v600-regulator",
|
||||
},
|
||||
.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");
|
||||
|
||||
|
|
|
@ -55,52 +55,13 @@ properties:
|
|||
|
||||
$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:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
/* pmic properties */
|
||||
|
@ -117,43 +78,58 @@ examples:
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ldo3: ldo3@16 {
|
||||
reg = <0x16>;
|
||||
vsel-reg = <0x51>;
|
||||
|
||||
ldo3: LDO3 {
|
||||
regulator-name = "ldo3";
|
||||
regulator-min-microvolt = <1500000>;
|
||||
regulator-max-microvolt = <2000000>;
|
||||
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 */
|
||||
reg = <0x17>;
|
||||
vsel-reg = <0x52>;
|
||||
|
||||
ldo4: LDO4 {
|
||||
regulator-name = "ldo4";
|
||||
regulator-min-microvolt = <1725000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
|
||||
enable-mask = <0x01>;
|
||||
idle-mode-mask = <0x10>;
|
||||
eco-microamp = <10000>;
|
||||
ldo9: LDO9 {
|
||||
regulator-name = "ldo9";
|
||||
regulator-min-microvolt = <1750000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
|
||||
hi6421-vsel = <0x52 0x07>;
|
||||
voltage-table = <1725000>, <1750000>, <1775000>, <1800000>,
|
||||
<1825000>, <1850000>, <1875000>, <1900000>;
|
||||
off-on-delay-us = <20000>;
|
||||
startup-delay-us = <120>;
|
||||
ldo15: LDO15 {
|
||||
regulator-name = "ldo15";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
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:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
spmi-channel:
|
||||
description: |
|
||||
number of the Kirin 970 SPMI channel where the SPMI devices are connected.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spmi-channel
|
||||
- compatible
|
||||
- reg
|
||||
- spmi-channel
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
patternProperties:
|
||||
"^pmic@[0-9a-f]$":
|
||||
|
@ -43,6 +51,8 @@ patternProperties:
|
|||
are documented at
|
||||
drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
bus {
|
||||
|
@ -51,11 +61,14 @@ examples:
|
|||
|
||||
spmi: spmi@fff24000 {
|
||||
compatible = "hisilicon,kirin970-spmi-controller";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
status = "ok";
|
||||
reg = <0x0 0xfff24000 0x0 0x1000>;
|
||||
spmi-channel = <2>;
|
||||
|
||||
pmic@0 {
|
||||
reg = <0 0>;
|
||||
/* pmic properties */
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* Authors: Yu Chen <chenyu56@huawei.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
@ -41,15 +42,15 @@
|
|||
#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
|
||||
|
||||
#define USB3OTG_CTRL0 (0x00)
|
||||
#define USB3OTG_CTRL3 (0x0C)
|
||||
#define USB3OTG_CTRL3 (0x0c)
|
||||
#define USB3OTG_CTRL4 (0x10)
|
||||
#define USB3OTG_CTRL5 (0x14)
|
||||
#define USB3OTG_CTRL7 (0x1C)
|
||||
#define USB3OTG_CTRL7 (0x1c)
|
||||
#define USB_MISC_CFG50 (0x50)
|
||||
#define USB_MISC_CFG54 (0x54)
|
||||
#define USB_MISC_CFG58 (0x58)
|
||||
#define USB_MISC_CFG5C (0x5C)
|
||||
#define USB_MISC_CFGA0 (0xA0)
|
||||
#define USB_MISC_CFG5C (0x5c)
|
||||
#define USB_MISC_CFGA0 (0xa0)
|
||||
#define TCA_CLK_RST (0x200)
|
||||
#define TCA_INTR_EN (0x204)
|
||||
#define TCA_INTR_STS (0x208)
|
||||
|
@ -66,14 +67,14 @@
|
|||
|
||||
#define CTRL5_USB2_SIDDQ BIT(0)
|
||||
|
||||
#define CTRL7_USB2_REFCLKSEL_MASK (3 << 3)
|
||||
#define CTRL7_USB2_REFCLKSEL_ABB (3 << 3)
|
||||
#define CTRL7_USB2_REFCLKSEL_PAD (2 << 3)
|
||||
#define CTRL7_USB2_REFCLKSEL_MASK GENMASK(4, 3)
|
||||
#define CTRL7_USB2_REFCLKSEL_ABB (BIT(4) | BIT(3))
|
||||
#define CTRL7_USB2_REFCLKSEL_PAD BIT(4)
|
||||
|
||||
#define CFG50_USB3_PHY_TEST_POWERDOWN BIT(23)
|
||||
|
||||
#define CFG54_USB31PHY_CR_ADDR_MASK (0xFFFF)
|
||||
#define CFG54_USB31PHY_CR_ADDR_SHIFT (16)
|
||||
#define CFG54_USB31PHY_CR_ADDR_MASK GENMASK(31, 16)
|
||||
|
||||
#define CFG54_USB3PHY_REF_USE_PAD BIT(12)
|
||||
#define CFG54_PHY0_PMA_PWR_STABLE BIT(11)
|
||||
#define CFG54_PHY0_PCS_PWR_STABLE BIT(9)
|
||||
|
@ -84,8 +85,7 @@
|
|||
#define CFG54_USB31PHY_CR_CLK BIT(2)
|
||||
#define CFG54_USB3_PHY0_ANA_PWR_EN BIT(1)
|
||||
|
||||
#define CFG58_USB31PHY_CR_DATA_MASK (0xFFFF)
|
||||
#define CFG58_USB31PHY_CR_DATA_RD_START (16)
|
||||
#define CFG58_USB31PHY_CR_DATA_MASK GENMASK(31, 16)
|
||||
|
||||
#define CFG5C_USB3_PHY0_SS_MPLLA_SSC_EN BIT(1)
|
||||
|
||||
|
@ -102,20 +102,20 @@
|
|||
#define CLK_RST_SUSPEND_CLK_EN BIT(0)
|
||||
|
||||
#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 TCPC_VALID BIT(4)
|
||||
#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 SYSMODE_CFG_TYPEC_DISABLE BIT(3)
|
||||
|
||||
#define VBUS_CTRL_POWERPRESENT_OVERRD (3 << 2)
|
||||
#define VBUS_CTRL_VBUSVALID_OVERRD (3 << 0)
|
||||
#define VBUS_CTRL_POWERPRESENT_OVERRD GENMASK(3, 2)
|
||||
#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 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)
|
||||
{
|
||||
int ret;
|
||||
int ret, reg;
|
||||
|
||||
if (direction)
|
||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||
CFG54_USB31PHY_CR_WR_EN,
|
||||
CFG54_USB31PHY_CR_WR_EN);
|
||||
reg = CFG54_USB31PHY_CR_WR_EN;
|
||||
else
|
||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||
CFG54_USB31PHY_CR_RD_EN,
|
||||
CFG54_USB31PHY_CR_RD_EN);
|
||||
reg = CFG54_USB31PHY_CR_RD_EN;
|
||||
|
||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54, reg, reg);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -180,16 +178,14 @@ static int hi3670_phy_cr_start(struct regmap *usb31misc, int direction)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||
CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
|
||||
|
||||
return ret;
|
||||
return regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||
CFG54_USB31PHY_CR_RD_EN | CFG54_USB31PHY_CR_WR_EN, 0);
|
||||
}
|
||||
|
||||
static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
|
||||
{
|
||||
u32 reg;
|
||||
int retry = 100000;
|
||||
int retry = 10;
|
||||
int ret;
|
||||
|
||||
while (retry-- > 0) {
|
||||
|
@ -202,6 +198,8 @@ static int hi3670_phy_cr_wait_ack(struct regmap *usb31misc)
|
|||
ret = hi3670_phy_cr_clk(usb31misc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
|
@ -216,9 +214,9 @@ static int hi3670_phy_cr_set_addr(struct regmap *usb31misc, u32 addr)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg &= ~(CFG54_USB31PHY_CR_ADDR_MASK << CFG54_USB31PHY_CR_ADDR_SHIFT);
|
||||
reg |= ((addr & CFG54_USB31PHY_CR_ADDR_MASK) << CFG54_USB31PHY_CR_ADDR_SHIFT);
|
||||
ret = regmap_write(usb31misc, USB_MISC_CFG54, reg);
|
||||
reg = FIELD_PREP(CFG54_USB31PHY_CR_ADDR_MASK, addr);
|
||||
ret = regmap_update_bits(usb31misc, USB_MISC_CFG54,
|
||||
CFG54_USB31PHY_CR_ADDR_MASK, reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -255,8 +253,7 @@ static int hi3670_phy_cr_read(struct regmap *usb31misc, u32 addr, u32 *val)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (reg >> CFG58_USB31PHY_CR_DATA_RD_START) &
|
||||
CFG58_USB31PHY_CR_DATA_MASK;
|
||||
*val = FIELD_GET(CFG58_USB31PHY_CR_DATA_MASK, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -281,7 +278,7 @@ static int hi3670_phy_cr_write(struct regmap *usb31misc, u32 addr, u32 val)
|
|||
return ret;
|
||||
|
||||
ret = regmap_write(usb31misc, USB_MISC_CFG58,
|
||||
val & CFG58_USB31PHY_CR_DATA_MASK);
|
||||
FIELD_PREP(CFG58_USB31PHY_CR_DATA_MASK, val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -329,24 +326,24 @@ static int hi3670_phy_set_params(struct hi3670_priv *priv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int hi3670_is_abbclk_seleted(struct hi3670_priv *priv)
|
||||
static bool hi3670_is_abbclk_selected(struct hi3670_priv *priv)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
if (!priv->sctrl) {
|
||||
dev_err(priv->dev, "priv->sctrl is null!\n");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (regmap_read(priv->sctrl, SCTRL_SCDEEPSLEEPED, ®)) {
|
||||
dev_err(priv->dev, "SCTRL_SCDEEPSLEEPED read failed!\n");
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((reg & USB_CLK_SELECTED) == 0)
|
||||
return 1;
|
||||
return false;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
if (hi3670_is_abbclk_seleted(priv)) {
|
||||
if (!hi3670_is_abbclk_selected(priv)) {
|
||||
/* usb refclk iso disable */
|
||||
ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS,
|
||||
USB_REFCLK_ISO_EN);
|
||||
|
@ -571,7 +568,7 @@ static int hi3670_phy_exit(struct phy *phy)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (hi3670_is_abbclk_seleted(priv)) {
|
||||
if (!hi3670_is_abbclk_selected(priv)) {
|
||||
/* disable usb_tcxo_en */
|
||||
ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3,
|
||||
USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START);
|
||||
|
@ -588,7 +585,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops hi3670_phy_ops = {
|
||||
static const struct phy_ops hi3670_phy_ops = {
|
||||
.init = hi3670_phy_init,
|
||||
.exit = hi3670_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -8,6 +8,7 @@ title: Hisilicon Kirin970 USB PHY
|
|||
|
||||
maintainers:
|
||||
- Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
|
||||
description: |+
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
return bo->start;
|
||||
|
|
|
@ -68,7 +68,7 @@ struct net_dev_context {
|
|||
};
|
||||
|
||||
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 struct most_component comp;
|
||||
|
||||
|
@ -520,7 +520,6 @@ static int __init most_net_init(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
mutex_init(&probe_disc_mt);
|
||||
err = most_register_component(&comp);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue