Staging / IIO driver update for 5.3-rc1
Here is the big Staging and IIO driver update for 5.3-rc1. Lots of new IIO drivers are in here, along with loads of tiny staging driver cleanups and fixes. Overall we almost break even with the same lines added as removed. Full details are in the shortlog, they are too large to list here. All of these changes have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXSXlWA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ym5lgCgwNVvum2lwWzAVPkVEMqpGYLZPLgAoJqegC9o JsdX6tFoAC8q8+FXWgZ4 =di3h -----END PGP SIGNATURE----- Merge tag 'staging-5.3-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 Staging and IIO driver update for 5.3-rc1. Lots of new IIO drivers are in here, along with loads of tiny staging driver cleanups and fixes. Overall we almost break even with the same lines added as removed. Full details are in the shortlog, they are too large to list here. All of these changes have been in linux-next for a while with no reported issues" * tag 'staging-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (608 commits) staging: kpc2000: simplify comparison to NULL in fileops.c staging: kpc2000: simplify comparison to NULL in dma.c staging: kpc2000: simplify comparison to NULL in kpc2000_spi.c staging: rtl8723bs: hal: remove redundant assignment to packetType staging: rtl8723bs: Change return type of hal_btcoex_IsBtDisabled() staging: rtl8723bs: Remove rtw_btcoex_DisplayBtCoexInfo() staging: rtl8723bs: Remove function rtw_btcoex_GetDBG() staging: rtl8723bs: Remove function rtw_btcoex_SetDBG() staging: rtl8723bs: Remove rtw_btcoex_IsBTCoexCtrlAMPDUSize() staging: rtl8723bs: Remove rtw_btcoex_BtInfoNotify() staging: rtl8723bs: Remove rtw_btcoex_ScanNotify() staging: rtl8723bs: Remove rtw_btcoex_SetSingleAntPath() staging: rtl8723bs: Remove rtw_btcoex_SetPGAntNum() staging: rtl8192e: remove redundant initialization of rtstatus staging: rtl8723bs: Remove rtw_btcoex_GetRaMask() staging: rtl8723bs: Remove rtw_btcoex_SetChipType() staging: rtl8723bs: Remove rtw_btcoex_ConnectNotify() staging: rtl8723bs: Remove rtw_btcoex_SetBTCoexist() staging: rtl8723bs: Remove rtw_btcoex_IsBtDisabled() staging: rtl8723bs: Remove rtw_btcoex_IsBtControlLps() ...
This commit is contained in:
commit
e786741ff1
|
@ -61,8 +61,11 @@ What: /sys/bus/iio/devices/triggerX/sampling_frequency_available
|
|||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
When the internal sampling clock can only take a small
|
||||
discrete set of values, this file lists those available.
|
||||
When the internal sampling clock can only take a specific set of
|
||||
frequencies, we can specify the available values with:
|
||||
- a small discrete set of values like "0 2 4 6 8"
|
||||
- a range with minimum, step and maximum frequencies like
|
||||
"[min step max]"
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/oversampling_ratio
|
||||
KernelVersion: 2.6.38
|
||||
|
|
|
@ -18,11 +18,11 @@ Description:
|
|||
values are 'base' and 'lid'.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/id
|
||||
Date: Septembre 2017
|
||||
Date: September 2017
|
||||
KernelVersion: 4.14
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is exposed by the CrOS EC legacy accelerometer
|
||||
driver and represents the sensor ID as exposed by the EC. This
|
||||
ID is used by the Android sensor service hardware abstraction
|
||||
layer (sensor HAL) through the Android container on ChromeOS.
|
||||
This attribute is exposed by the CrOS EC sensors driver and
|
||||
represents the sensor ID as exposed by the EC. This ID is used
|
||||
by the Android sensor service hardware abstraction layer (sensor
|
||||
HAL) through the Android container on ChromeOS.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Stores the PLL frequency in Hz for channel Y.
|
||||
Reading returns the actual frequency in Hz.
|
||||
The ADF4371 has an integrated VCO with fundamendal output
|
||||
frequency ranging from 4000000000 Hz 8000000000 Hz.
|
||||
|
||||
out_altvoltage0_frequency:
|
||||
A divide by 1, 2, 4, 8, 16, 32 or circuit generates
|
||||
frequencies from 62500000 Hz to 8000000000 Hz.
|
||||
out_altvoltage1_frequency:
|
||||
This channel duplicates the channel 0 frequency
|
||||
out_altvoltage2_frequency:
|
||||
A frequency doubler generates frequencies from
|
||||
8000000000 Hz to 16000000000 Hz.
|
||||
out_altvoltage3_frequency:
|
||||
A frequency quadrupler generates frequencies from
|
||||
16000000000 Hz to 32000000000 Hz.
|
||||
|
||||
Note: writes to one of the channels will affect the frequency of
|
||||
all the other channels, since it involves changing the VCO
|
||||
fundamental output frequency.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_name
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Reading returns the datasheet name for channel Y:
|
||||
|
||||
out_altvoltage0_name: RF8x
|
||||
out_altvoltage1_name: RFAUX8x
|
||||
out_altvoltage2_name: RF16x
|
||||
out_altvoltage3_name: RF32x
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute allows the user to power down the PLL and it's
|
||||
RFOut buffers.
|
||||
Writing 1 causes the specified channel to power down.
|
||||
Clearing returns to normal operation.
|
|
@ -0,0 +1,72 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/accelerometers/adi,adxl345.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADXL345/ADXL375 3-Axis Digital Accelerometers
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADXL345/ADXL375 3-Axis Digital Accelerometers that supports
|
||||
both I2C & SPI interfaces.
|
||||
http://www.analog.com/en/products/mems/accelerometers/adxl345.html
|
||||
http://www.analog.com/en/products/sensors-mems/accelerometers/adxl375.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adxl345
|
||||
- adi,adxl375
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a I2C device node */
|
||||
accelerometer@2a {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a SPI device node */
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/accelerometers/adi,adxl372.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer
|
||||
|
||||
maintainers:
|
||||
- Stefan Popa <stefan.popa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer that supports
|
||||
both I2C & SPI interfaces
|
||||
https://www.analog.com/en/products/adxl372.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adxl372
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a I2C device node */
|
||||
accelerometer@53 {
|
||||
compatible = "adi,adxl372";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl372";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
Analog Devices ADXL345/ADXL375 3-Axis Digital Accelerometers
|
||||
|
||||
http://www.analog.com/en/products/mems/accelerometers/adxl345.html
|
||||
http://www.analog.com/en/products/sensors-mems/accelerometers/adxl375.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of
|
||||
"adi,adxl345"
|
||||
"adi,adxl375"
|
||||
- reg : the I2C address or SPI chip select number of the sensor
|
||||
|
||||
Required properties for SPI bus usage:
|
||||
- spi-max-frequency : set maximum clock frequency, must be 5000000
|
||||
- spi-cpol and spi-cpha : must be defined for adxl345 to enable SPI mode 3
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt mapping for IRQ as documented in
|
||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Example for a I2C device node:
|
||||
|
||||
accelerometer@2a {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
Example for a SPI device node:
|
||||
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer
|
||||
|
||||
http://www.analog.com/media/en/technical-documentation/data-sheets/adxl372.pdf
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "adi,adxl372"
|
||||
- reg: the I2C address or SPI chip select number for the device
|
||||
|
||||
Required properties for SPI bus usage:
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt mapping for IRQ as documented in
|
||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Example for a I2C device node:
|
||||
|
||||
accelerometer@53 {
|
||||
compatible = "adi,adxl372";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
|
||||
Example for a SPI device node:
|
||||
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl372";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
|
@ -1,75 +0,0 @@
|
|||
Analog Devices AD7124 ADC device driver
|
||||
|
||||
Required properties for the AD7124:
|
||||
- compatible: Must be one of "adi,ad7124-4" or "adi,ad7124-8"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- clocks: phandle to the master clock (mclk)
|
||||
see: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- clock-names: Must be "mclk".
|
||||
- interrupts: IRQ line for the ADC
|
||||
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Required properties:
|
||||
* #address-cells: Must be 1.
|
||||
* #size-cells: Must be 0.
|
||||
|
||||
Subnode(s) represent the external channels which are connected to the ADC.
|
||||
Each subnode represents one channel and has the following properties:
|
||||
Required properties:
|
||||
* reg: The channel number. It can have up to 4 channels on ad7124-4
|
||||
and 8 channels on ad7124-8, numbered from 0 to 15.
|
||||
* diff-channels: see: Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
|
||||
Optional properties:
|
||||
* bipolar: see: Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
* adi,reference-select: Select the reference source to use when
|
||||
converting on the the specific channel. Valid values are:
|
||||
0: REFIN1(+)/REFIN1(−).
|
||||
1: REFIN2(+)/REFIN2(−).
|
||||
3: AVDD
|
||||
If this field is left empty, internal reference is selected.
|
||||
|
||||
Optional properties:
|
||||
- refin1-supply: refin1 supply can be used as reference for conversion.
|
||||
- refin2-supply: refin2 supply can be used as reference for conversion.
|
||||
- avdd-supply: avdd supply can be used as reference for conversion.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "adi,ad7124-4";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
interrupts = <25 2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
refin1-supply = <&adc_vref>;
|
||||
clocks = <&ad7124_mclk>;
|
||||
clock-names = "mclk";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <0 1>;
|
||||
adi,reference-select = <0>;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
bipolar;
|
||||
diff-channels = <2 3>;
|
||||
adi,reference-select = <0>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
diff-channels = <4 5>;
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
diff-channels = <6 7>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,155 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2019 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/bindings/iio/adc/adi,ad7124.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7124 ADC device driver
|
||||
|
||||
maintainers:
|
||||
- Stefan Popa <stefan.popa@analog.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Analog Devices AD7124 ADC device. Datasheet can be
|
||||
found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7124-8.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7124-4
|
||||
- adi,ad7124-8
|
||||
|
||||
reg:
|
||||
description: SPI chip select number for the device
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: phandle to the master clock (mclk)
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: mclk
|
||||
|
||||
interrupts:
|
||||
description: IRQ line for the ADC
|
||||
maxItems: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
refin1-supply:
|
||||
description: refin1 supply can be used as reference for conversion.
|
||||
maxItems: 1
|
||||
|
||||
refin2-supply:
|
||||
description: refin2 supply can be used as reference for conversion.
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description: avdd supply can be used as reference for conversion.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
patternProperties:
|
||||
"^channel@([0-9]|1[0-5])$":
|
||||
type: object
|
||||
description: |
|
||||
Represents the external channels which are connected to the ADC.
|
||||
See Documentation/devicetree/bindings/iio/adc/adc.txt.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: |
|
||||
The channel number. It can have up to 8 channels on ad7124-4
|
||||
and 16 channels on ad7124-8, numbered from 0 to 15.
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
adi,reference-select:
|
||||
description: |
|
||||
Select the reference source to use when converting on
|
||||
the specific channel. Valid values are:
|
||||
0: REFIN1(+)/REFIN1(−).
|
||||
1: REFIN2(+)/REFIN2(−).
|
||||
3: AVDD
|
||||
If this field is left empty, internal reference is selected.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [0, 1, 3]
|
||||
|
||||
diff-channels:
|
||||
description: see Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
bipolar:
|
||||
description: see Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
type: boolean
|
||||
|
||||
adi,buffered-positive:
|
||||
description: Enable buffered mode for positive input.
|
||||
type: boolean
|
||||
|
||||
adi,buffered-negative:
|
||||
description: Enable buffered mode for negative input.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- reg
|
||||
- diff-channels
|
||||
|
||||
examples:
|
||||
- |
|
||||
adc@0 {
|
||||
compatible = "adi,ad7124-4";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
interrupts = <25 2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
refin1-supply = <&adc_vref>;
|
||||
clocks = <&ad7124_mclk>;
|
||||
clock-names = "mclk";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <0 1>;
|
||||
adi,reference-select = <0>;
|
||||
adi,buffered-positive;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
bipolar;
|
||||
diff-channels = <2 3>;
|
||||
adi,reference-select = <0>;
|
||||
adi,buffered-positive;
|
||||
adi,buffered-negative;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
diff-channels = <4 5>;
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
diff-channels = <6 7>;
|
||||
};
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
* Analog Devices AD7170/AD7171/AD7780/AD7781
|
||||
|
||||
Data sheets:
|
||||
|
||||
- AD7170:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/AD7170.pdf
|
||||
- AD7171:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/AD7171.pdf
|
||||
- AD7780:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad7780.pdf
|
||||
- AD7781:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/AD7781.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be one of
|
||||
* "adi,ad7170"
|
||||
* "adi,ad7171"
|
||||
* "adi,ad7780"
|
||||
* "adi,ad7781"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: the regulator supply for the ADC reference voltage
|
||||
|
||||
Optional properties:
|
||||
|
||||
- powerdown-gpios: must be the device tree identifier of the PDRST pin. If
|
||||
specified, it will be asserted during driver probe. As the
|
||||
line is active high, it should be marked GPIO_ACTIVE_HIGH.
|
||||
- adi,gain-gpios: must be the device tree identifier of the GAIN pin. Only for
|
||||
the ad778x chips. If specified, it will be asserted during
|
||||
driver probe. As the line is active low, it should be marked
|
||||
GPIO_ACTIVE_LOW.
|
||||
- adi,filter-gpios: must be the device tree identifier of the FILTER pin. Only
|
||||
for the ad778x chips. If specified, it will be asserted
|
||||
during driver probe. As the line is active low, it should be
|
||||
marked GPIO_ACTIVE_LOW.
|
||||
|
||||
Example:
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7780";
|
||||
reg = <0>;
|
||||
vref-supply = <&vdd_supply>
|
||||
|
||||
powerdown-gpios = <&gpio 12 GPIO_ACTIVE_HIGH>;
|
||||
adi,gain-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
|
||||
adi,filter-gpios = <&gpio 15 GPIO_ACTIVE_LOW>;
|
||||
};
|
|
@ -0,0 +1,87 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7780.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7170/AD7171/AD7780/AD7781 analog to digital converters
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
|
||||
description: |
|
||||
The ad7780 is a sigma-delta analog to digital converter. This driver provides
|
||||
reading voltage values and status bits from both the ad778x and ad717x series.
|
||||
Its interface also allows writing on the FILTER and GAIN GPIO pins on the
|
||||
ad778x.
|
||||
|
||||
Specifications on the converters can be found at:
|
||||
AD7170:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7170.pdf
|
||||
AD7171:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7171.pdf
|
||||
AD7780:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7780.pdf
|
||||
AD7781:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7781.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7170
|
||||
- adi,ad7171
|
||||
- adi,ad7780
|
||||
- adi,ad7781
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description:
|
||||
The regulator supply for the ADC reference voltage.
|
||||
maxItems: 1
|
||||
|
||||
powerdown-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the PDRST pin. If
|
||||
specified, it will be asserted during driver probe. As the
|
||||
line is active high, it should be marked GPIO_ACTIVE_HIGH.
|
||||
maxItems: 1
|
||||
|
||||
adi,gain-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the GAIN pin. Only for
|
||||
the ad778x chips. If specified, it will be asserted during
|
||||
driver probe. As the line is active low, it should be marked
|
||||
GPIO_ACTIVE_LOW.
|
||||
maxItems: 1
|
||||
|
||||
adi,filter-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the FILTER pin. Only
|
||||
for the ad778x chips. If specified, it will be asserted
|
||||
during driver probe. As the line is active low, it should be
|
||||
marked GPIO_ACTIVE_LOW.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7780";
|
||||
reg = <0>;
|
||||
|
||||
avdd-supply = <&vdd_supply>;
|
||||
powerdown-gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
|
||||
adi,gain-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
|
||||
adi,filter-gpios = <&gpio2 15 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
|
@ -13,8 +13,10 @@ Required properties:
|
|||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-auxadc": For MT2701 family of SoCs
|
||||
- "mediatek,mt2712-auxadc": For MT2712 family of SoCs
|
||||
- "mediatek,mt6765-auxadc": For MT6765 family of SoCs
|
||||
- "mediatek,mt7622-auxadc": For MT7622 family of SoCs
|
||||
- "mediatek,mt8173-auxadc": For MT8173 family of SoCs
|
||||
- "mediatek,mt8183-auxadc", "mediatek,mt8173-auxadc": For MT8183 family of SoCs
|
||||
- reg: Address range of the AUXADC unit.
|
||||
- clocks: Should contain a clock specifier for each entry in clock-names
|
||||
- clock-names: Should contain "main".
|
||||
|
|
|
@ -38,6 +38,7 @@ Required properties:
|
|||
It's required on stm32h7.
|
||||
- clock-names: Must be "adc" and/or "bus" depending on part used.
|
||||
- interrupt-controller: Identifies the controller node as interrupt-parent
|
||||
- vdda-supply: Phandle to the vdda input analog voltage.
|
||||
- vref-supply: Phandle to the vref input analog reference voltage.
|
||||
- #interrupt-cells = <1>;
|
||||
- #address-cells = <1>;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
* Sensirion SPS30 particulate matter sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "sensirion,sps30"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
sps30@69 {
|
||||
compatible = "sensirion,sps30";
|
||||
reg = <0x69>;
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/sensirion,sps30.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sensirion SPS30 particulate matter sensor
|
||||
|
||||
maintainers:
|
||||
- Tomasz Duszynski <tduszyns@gmail.com>
|
||||
|
||||
description: |
|
||||
Air pollution sensor capable of measuring mass concentration of dust
|
||||
particles.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sensirion,sps30
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
air-pollution-sensor@69 {
|
||||
compatible = "sensirion,sps30";
|
||||
reg = <0x69>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,63 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/frequency/adf4371.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADF4371/ADF4372 Wideband Synthesizers
|
||||
|
||||
maintainers:
|
||||
- Popa Stefan <stefan.popa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADF4371/ADF4372 SPI Wideband Synthesizers
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/adf4371.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/adf4372.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adf4371
|
||||
- adi,adf4372
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description:
|
||||
Definition of the external clock (see clock/clock-bindings.txt)
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
description:
|
||||
Must be "clkin"
|
||||
maxItems: 1
|
||||
|
||||
adi,mute-till-lock-en:
|
||||
type: boolean
|
||||
description:
|
||||
If this property is present, then the supply current to RF8P and RF8N
|
||||
output stage will shut down until the ADF4371/ADF4372 achieves lock as
|
||||
measured by the digital lock detect circuitry.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
frequency@0 {
|
||||
compatible = "adi,adf4371";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
clocks = <&adf4371_clkin>;
|
||||
clock-names = "clkin";
|
||||
};
|
||||
};
|
||||
...
|
|
@ -1,27 +0,0 @@
|
|||
* ISL 29018/29023/29035 I2C ALS, Proximity, and Infrared sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"isil,isl29018"
|
||||
"isil,isl29023"
|
||||
"isil,isl29035"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor.
|
||||
|
||||
Example:
|
||||
|
||||
isl29018@44 {
|
||||
compatible = "isil,isl29018";
|
||||
reg = <0x44>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <TEGRA_GPIO(Z, 2) IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/isl29018.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: |
|
||||
Intersil 29018/29023/29035 Ambient Light, Infrared Light, and Proximity Sensor
|
||||
|
||||
maintainers:
|
||||
- Brian Masney <masneyb@onstation.org>
|
||||
|
||||
description: |
|
||||
Ambient and infrared light sensing with proximity detection over an i2c
|
||||
interface.
|
||||
|
||||
https://www.renesas.com/us/en/www/doc/datasheet/isl29018.pdf
|
||||
https://www.renesas.com/us/en/www/doc/datasheet/isl29023.pdf
|
||||
https://www.renesas.com/us/en/www/doc/datasheet/isl29035.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- isil,isl29018
|
||||
- isil,isl29023
|
||||
- isil,isl29035
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sensor@44 {
|
||||
compatible = "isil,isl29018";
|
||||
reg = <0x44>;
|
||||
interrupts-extended = <&msmgpio 61 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -1,25 +0,0 @@
|
|||
* TAOS TSL 2580/2581/2583 ALS sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"amstaos,tsl2580"
|
||||
"amstaos,tsl2581"
|
||||
"amstaos,tsl2583"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor.
|
||||
|
||||
Example:
|
||||
|
||||
tsl2581@29 {
|
||||
compatible = "amstaos,tsl2581";
|
||||
reg = <0x29>;
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/tsl2583.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: AMS/TAOS Ambient Light Sensor (ALS)
|
||||
|
||||
maintainers:
|
||||
- Brian Masney <masneyb@onstation.org>
|
||||
|
||||
description: |
|
||||
Ambient light sensing with an i2c interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amstaos,tsl2580
|
||||
- amstaos,tsl2581
|
||||
- amstaos,tsl2583
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@29 {
|
||||
compatible = "amstaos,tsl2581";
|
||||
reg = <0x29>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -1,42 +0,0 @@
|
|||
* AMS/TAOS ALS and proximity sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"amstaos,tsl2571"
|
||||
"amstaos,tsl2671"
|
||||
"amstaos,tmd2671"
|
||||
"amstaos,tsl2771"
|
||||
"amstaos,tmd2771"
|
||||
"amstaos,tsl2572"
|
||||
"amstaos,tsl2672"
|
||||
"amstaos,tmd2672"
|
||||
"amstaos,tsl2772"
|
||||
"amstaos,tmd2772"
|
||||
"avago,apds9930"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- amstaos,proximity-diodes - proximity diodes to enable. <0>, <1>, or <0 1>
|
||||
are the only valid values.
|
||||
- led-max-microamp - current for the proximity LED. Must be 100000, 50000,
|
||||
25000, or 13000.
|
||||
- vdd-supply: phandle to the regulator that provides power to the sensor.
|
||||
- vddio-supply: phandle to the regulator that provides power to the bus.
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
tsl2772@39 {
|
||||
compatible = "amstaos,tsl2772";
|
||||
reg = <0x39>;
|
||||
interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&pm8941_l17>;
|
||||
vddio-supply = <&pm8941_lvs1>;
|
||||
amstaos,proximity-diodes = <0>;
|
||||
led-max-microamp = <100000>;
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/tsl2772.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: AMS/TAOS Ambient Light Sensor (ALS) and Proximity Detector
|
||||
|
||||
maintainers:
|
||||
- Brian Masney <masneyb@onstation.org>
|
||||
|
||||
description: |
|
||||
Ambient light sensing and proximity detection with an i2c interface.
|
||||
https://ams.com/documents/20143/36005/TSL2772_DS000181_2-00.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amstaos,tsl2571
|
||||
- amstaos,tsl2671
|
||||
- amstaos,tmd2671
|
||||
- amstaos,tsl2771
|
||||
- amstaos,tmd2771
|
||||
- amstaos,tsl2572
|
||||
- amstaos,tsl2672
|
||||
- amstaos,tmd2672
|
||||
- amstaos,tsl2772
|
||||
- amstaos,tmd2772
|
||||
- avago,apds9930
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
amstaos,proximity-diodes:
|
||||
description: Proximity diodes to enable
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
led-max-microamp:
|
||||
description: Current for the proximity LED
|
||||
enum:
|
||||
- 13000
|
||||
- 25000
|
||||
- 50000
|
||||
- 100000
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
vddio-supply:
|
||||
description: Regulator that provides power to the bus
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sensor@39 {
|
||||
compatible = "amstaos,tsl2772";
|
||||
reg = <0x39>;
|
||||
interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&pm8941_l17>;
|
||||
vddio-supply = <&pm8941_lvs1>;
|
||||
amstaos,proximity-diodes = <0>;
|
||||
led-max-microamp = <100000>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -1,12 +1,16 @@
|
|||
Cirrus Logic EP93xx ADC driver.
|
||||
==============================
|
||||
Cirrus Logic EP93xx ADC driver
|
||||
==============================
|
||||
|
||||
1. Overview
|
||||
===========
|
||||
|
||||
The driver is intended to work on both low-end (EP9301, EP9302) devices with
|
||||
5-channel ADC and high-end (EP9307, EP9312, EP9315) devices with 10-channel
|
||||
touchscreen/ADC module.
|
||||
|
||||
2. Channel numbering
|
||||
====================
|
||||
|
||||
Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets.
|
||||
EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is
|
||||
|
@ -17,13 +21,20 @@ Assuming ep93xx_adc is IIO device0, you'd find the following entries under
|
|||
|
||||
+-----------------+---------------+
|
||||
| sysfs entry | ball/pin name |
|
||||
+-----------------+---------------+
|
||||
+=================+===============+
|
||||
| in_voltage0_raw | YM |
|
||||
+-----------------+---------------+
|
||||
| in_voltage1_raw | SXP |
|
||||
+-----------------+---------------+
|
||||
| in_voltage2_raw | SXM |
|
||||
+-----------------+---------------+
|
||||
| in_voltage3_raw | SYP |
|
||||
+-----------------+---------------+
|
||||
| in_voltage4_raw | SYM |
|
||||
+-----------------+---------------+
|
||||
| in_voltage5_raw | XP |
|
||||
+-----------------+---------------+
|
||||
| in_voltage6_raw | XM |
|
||||
+-----------------+---------------+
|
||||
| in_voltage7_raw | YP |
|
||||
+-----------------+---------------+
|
|
@ -1,6 +1,9 @@
|
|||
===============================
|
||||
Industrial IIO configfs support
|
||||
===============================
|
||||
|
||||
1. Overview
|
||||
===========
|
||||
|
||||
Configfs is a filesystem-based manager of kernel objects. IIO uses some
|
||||
objects that could be easily configured using configfs (e.g.: devices,
|
||||
|
@ -10,20 +13,22 @@ See Documentation/filesystems/configfs/configfs.txt for more information
|
|||
about how configfs works.
|
||||
|
||||
2. Usage
|
||||
========
|
||||
|
||||
In order to use configfs support in IIO we need to select it at compile
|
||||
time via CONFIG_IIO_CONFIGFS config option.
|
||||
|
||||
Then, mount the configfs filesystem (usually under /config directory):
|
||||
Then, mount the configfs filesystem (usually under /config directory)::
|
||||
|
||||
$ mkdir /config
|
||||
$ mount -t configfs none /config
|
||||
$ mkdir /config
|
||||
$ mount -t configfs none /config
|
||||
|
||||
At this point, all default IIO groups will be created and can be accessed
|
||||
under /config/iio. Next chapters will describe available IIO configuration
|
||||
objects.
|
||||
|
||||
3. Software triggers
|
||||
====================
|
||||
|
||||
One of the IIO default configfs groups is the "triggers" group. It is
|
||||
automagically accessible when the configfs is mounted and can be found
|
||||
|
@ -31,40 +36,40 @@ under /config/iio/triggers.
|
|||
|
||||
IIO software triggers implementation offers support for creating multiple
|
||||
trigger types. A new trigger type is usually implemented as a separate
|
||||
kernel module following the interface in include/linux/iio/sw_trigger.h:
|
||||
kernel module following the interface in include/linux/iio/sw_trigger.h::
|
||||
|
||||
/*
|
||||
* drivers/iio/trigger/iio-trig-sample.c
|
||||
* sample kernel module implementing a new trigger type
|
||||
*/
|
||||
#include <linux/iio/sw_trigger.h>
|
||||
/*
|
||||
* drivers/iio/trigger/iio-trig-sample.c
|
||||
* sample kernel module implementing a new trigger type
|
||||
*/
|
||||
#include <linux/iio/sw_trigger.h>
|
||||
|
||||
|
||||
static struct iio_sw_trigger *iio_trig_sample_probe(const char *name)
|
||||
{
|
||||
static struct iio_sw_trigger *iio_trig_sample_probe(const char *name)
|
||||
{
|
||||
/*
|
||||
* This allocates and registers an IIO trigger plus other
|
||||
* trigger type specific initialization.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
|
||||
{
|
||||
static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
|
||||
{
|
||||
/*
|
||||
* This undoes the actions in iio_trig_sample_probe
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_sw_trigger_ops iio_trig_sample_ops = {
|
||||
static const struct iio_sw_trigger_ops iio_trig_sample_ops = {
|
||||
.probe = iio_trig_sample_probe,
|
||||
.remove = iio_trig_sample_remove,
|
||||
};
|
||||
};
|
||||
|
||||
static struct iio_sw_trigger_type iio_trig_sample = {
|
||||
static struct iio_sw_trigger_type iio_trig_sample = {
|
||||
.name = "trig-sample",
|
||||
.owner = THIS_MODULE,
|
||||
.ops = &iio_trig_sample_ops,
|
||||
};
|
||||
};
|
||||
|
||||
module_iio_sw_trigger_driver(iio_trig_sample);
|
||||
|
||||
|
@ -73,21 +78,24 @@ iio-trig-sample module will create 'trig-sample' trigger type directory
|
|||
/config/iio/triggers/trig-sample.
|
||||
|
||||
We support the following interrupt sources (trigger types):
|
||||
|
||||
* hrtimer, uses high resolution timers as interrupt source
|
||||
|
||||
3.1 Hrtimer triggers creation and destruction
|
||||
---------------------------------------------
|
||||
|
||||
Loading iio-trig-hrtimer module will register hrtimer trigger types allowing
|
||||
users to create hrtimer triggers under /config/iio/triggers/hrtimer.
|
||||
|
||||
e.g:
|
||||
e.g::
|
||||
|
||||
$ mkdir /config/iio/triggers/hrtimer/instance1
|
||||
$ rmdir /config/iio/triggers/hrtimer/instance1
|
||||
$ mkdir /config/iio/triggers/hrtimer/instance1
|
||||
$ rmdir /config/iio/triggers/hrtimer/instance1
|
||||
|
||||
Each trigger can have one or more attributes specific to the trigger type.
|
||||
|
||||
3.2 "hrtimer" trigger types attributes
|
||||
--------------------------------------
|
||||
|
||||
"hrtimer" trigger type doesn't have any configurable attribute from /config dir.
|
||||
It does introduce the sampling_frequency attribute to trigger directory.
|
|
@ -0,0 +1,12 @@
|
|||
:orphan:
|
||||
|
||||
==============
|
||||
Industrial I/O
|
||||
==============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
iio_configfs
|
||||
|
||||
ep93xx_adc
|
50
MAINTAINERS
50
MAINTAINERS
|
@ -551,6 +551,7 @@ W: http://wiki.analog.com/ADXL345
|
|||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/input/misc/adxl34x.c
|
||||
F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
|
||||
|
||||
ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
|
@ -559,7 +560,7 @@ S: Supported
|
|||
F: drivers/iio/accel/adxl372.c
|
||||
F: drivers/iio/accel/adxl372_spi.c
|
||||
F: drivers/iio/accel/adxl372_i2c.c
|
||||
F: Documentation/devicetree/bindings/iio/accel/adxl372.txt
|
||||
F: Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml
|
||||
|
||||
AF9013 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
|
@ -916,6 +917,15 @@ S: Supported
|
|||
F: drivers/iio/adc/ad7768-1.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt
|
||||
|
||||
ANALOG DEVICES INC AD7780 DRIVER
|
||||
M: Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
M: Renato Lui Geh <renatogeh@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7780.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
|
||||
|
||||
ANALOG DEVICES INC AD9389B DRIVER
|
||||
M: Hans Verkuil <hverkuil-cisco@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -928,6 +938,13 @@ S: Supported
|
|||
F: drivers/mux/adgs1408.c
|
||||
F: Documentation/devicetree/bindings/mux/adi,adgs1408.txt
|
||||
|
||||
ANALOG DEVICES INC ADIS DRIVER LIBRARY
|
||||
M: Alexandru Ardelean <alexandru.ardelean@analog.com>
|
||||
S: Supported
|
||||
L: linux-iio@vger.kernel.org
|
||||
F: include/linux/iio/imu/adis.h
|
||||
F: drivers/iio/imu/adis.c
|
||||
|
||||
ANALOG DEVICES INC ADP5061 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
|
@ -6258,6 +6275,14 @@ M: Philip Kelleher <pjk1939@linux.ibm.com>
|
|||
S: Maintained
|
||||
F: drivers/block/rsxx/
|
||||
|
||||
FLEXTIMER FTM-QUADDEC DRIVER
|
||||
M: Patrick Havelange <patrick.havelange@essensium.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-counter-ftm-quadddec
|
||||
F: Documentation/devicetree/bindings/counter/ftm-quaddec.txt
|
||||
F: drivers/counter/ftm-quaddec.c
|
||||
|
||||
FLOPPY DRIVER
|
||||
M: Jiri Kosina <jikos@kernel.org>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git
|
||||
|
@ -7855,6 +7880,12 @@ W: http://industrypack.sourceforge.net
|
|||
S: Maintained
|
||||
F: drivers/ipack/
|
||||
|
||||
INFINEON DPS310 Driver
|
||||
M: Eddie James <eajames@linux.ibm.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
F: drivers/iio/pressure/dps310.c
|
||||
S: Maintained
|
||||
|
||||
INFINIBAND SUBSYSTEM
|
||||
M: Doug Ledford <dledford@redhat.com>
|
||||
M: Jason Gunthorpe <jgg@mellanox.com>
|
||||
|
@ -14279,6 +14310,12 @@ S: Maintained
|
|||
F: drivers/misc/phantom.c
|
||||
F: include/uapi/linux/phantom.h
|
||||
|
||||
SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/chemical/sps30.c
|
||||
F: Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.yaml
|
||||
|
||||
SERIAL DEVICE BUS
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
L: linux-serial@vger.kernel.org
|
||||
|
@ -15053,6 +15090,17 @@ L: linux-erofs@lists.ozlabs.org
|
|||
S: Maintained
|
||||
F: drivers/staging/erofs/
|
||||
|
||||
STAGING - FIELDBUS SUBSYSTEM
|
||||
M: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/fieldbus/*
|
||||
F: drivers/staging/fieldbus/Documentation/
|
||||
|
||||
STAGING - HMS ANYBUS-S BUS
|
||||
M: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/fieldbus/anybuss/
|
||||
|
||||
STAGING - INDUSTRIAL IO
|
||||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
|
|
@ -352,5 +352,5 @@ static struct platform_driver ftm_quaddec_driver = {
|
|||
module_platform_driver(ftm_quaddec_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com");
|
||||
MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com");
|
||||
MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
|
||||
MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
|
||||
|
|
|
@ -28,7 +28,7 @@ config IIO_CONFIGFS
|
|||
help
|
||||
This allows configuring various IIO bits through configfs
|
||||
(e.g. software triggers). For more info see
|
||||
Documentation/iio/iio_configfs.txt.
|
||||
Documentation/iio/iio_configfs.rst.
|
||||
|
||||
config IIO_TRIGGER
|
||||
bool "Enable triggered sampling support"
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
#define ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
/* Power supply below 2.975 V */
|
||||
#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* System Command Register Definition */
|
||||
|
@ -230,7 +230,7 @@ static const char * const adis16201_status_error_msgs[] = {
|
|||
[ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
|
||||
[ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
[ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16201_data = {
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
#define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16209_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
/* Power supply below 2.975 V */
|
||||
#define ADIS16209_STAT_POWER_LOW_BIT 0
|
||||
|
||||
#define ADIS16209_CMD_REG 0x3E
|
||||
|
@ -240,7 +240,7 @@ static const char * const adis16209_status_error_msgs[] = {
|
|||
[ADIS16209_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16209_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
|
||||
[ADIS16209_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
[ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16209_data = {
|
||||
|
|
|
@ -782,10 +782,14 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
|
|||
unsigned int mask;
|
||||
int i, ret;
|
||||
|
||||
ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0);
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
mask = *indio_dev->active_scan_mask;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) {
|
||||
|
@ -793,8 +797,10 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
|
|||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(adxl372_axis_lookup_table))
|
||||
return -EINVAL;
|
||||
if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
|
||||
st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
|
||||
|
@ -814,26 +820,25 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
|
|||
if (ret < 0) {
|
||||
st->fifo_mode = ADXL372_FIFO_BYPASSED;
|
||||
adxl372_set_interrupts(st, 0, 0);
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
adxl372_set_interrupts(st, 0, 0);
|
||||
st->fifo_mode = ADXL372_FIFO_BYPASSED;
|
||||
adxl372_configure_fifo(st);
|
||||
|
||||
return 0;
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adxl372_buffer_ops = {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "adxl372.h"
|
||||
|
@ -37,9 +39,16 @@ static const struct spi_device_id adxl372_spi_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, adxl372_spi_id);
|
||||
|
||||
static const struct of_device_id adxl372_of_match[] = {
|
||||
{ .compatible = "adi,adxl372" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adxl372_of_match);
|
||||
|
||||
static struct spi_driver adxl372_spi_driver = {
|
||||
.driver = {
|
||||
.name = "adxl372_spi",
|
||||
.of_match_table = adxl372_of_match,
|
||||
},
|
||||
.probe = adxl372_spi_probe,
|
||||
.id_table = adxl372_spi_id,
|
||||
|
|
|
@ -1487,6 +1487,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
|
|||
{"KIOX0009", KXTJ21009},
|
||||
{"KIOX000A", KXCJ91008},
|
||||
{"KIOX010A", KXCJ91008}, /* KXCJ91008 inside the display of a 2-in-1 */
|
||||
{"KIOX020A", KXCJ91008},
|
||||
{"KXTJ1009", KXTJ21009},
|
||||
{"KXJ2109", KXTJ21009},
|
||||
{"SMO8500", KXCJ91008},
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -41,10 +43,17 @@ static const struct spi_device_id kxsd9_spi_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, kxsd9_spi_id);
|
||||
|
||||
static const struct of_device_id kxsd9_of_match[] = {
|
||||
{ .compatible = "kionix,kxsd9" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, kxsd9_of_match);
|
||||
|
||||
static struct spi_driver kxsd9_spi_driver = {
|
||||
.driver = {
|
||||
.name = "kxsd9",
|
||||
.pm = &kxsd9_dev_pm_ops,
|
||||
.of_match_table = kxsd9_of_match,
|
||||
},
|
||||
.probe = kxsd9_spi_probe,
|
||||
.remove = kxsd9_spi_remove,
|
||||
|
|
|
@ -869,8 +869,9 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev,
|
|||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
int ret, i;
|
||||
struct sca3000_state *st = iio_priv(indio_dev);
|
||||
long ret;
|
||||
int i;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
|
@ -882,11 +883,11 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
*val = 0;
|
||||
if (chan->channel2 == IIO_MOD_Y)
|
||||
for_each_set_bit(i, (unsigned long *)&ret,
|
||||
for_each_set_bit(i, &ret,
|
||||
ARRAY_SIZE(st->info->mot_det_mult_y))
|
||||
*val += st->info->mot_det_mult_y[i];
|
||||
else
|
||||
for_each_set_bit(i, (unsigned long *)&ret,
|
||||
for_each_set_bit(i, &ret,
|
||||
ARRAY_SIZE(st->info->mot_det_mult_xz))
|
||||
*val += st->info->mot_det_mult_xz[i];
|
||||
|
||||
|
|
|
@ -45,17 +45,19 @@ static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
|||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_sensors_set_axis_enable_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_sensors_set_axis_enable_error:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
st_accel_buffer_postenable_error:
|
||||
kfree(adata->buffer_data);
|
||||
allocate_memory_error:
|
||||
|
@ -64,20 +66,22 @@ static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
|||
|
||||
static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
int err, err2;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
st_accel_buffer_predisable_error:
|
||||
err2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (!err)
|
||||
err = err2;
|
||||
|
||||
kfree(adata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -1085,7 +1085,6 @@ config VIPERBOARD_ADC
|
|||
|
||||
config XILINX_XADC
|
||||
tristate "Xilinx XADC driver"
|
||||
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
#define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x)
|
||||
#define AD7124_CONFIG_PGA_MSK GENMASK(2, 0)
|
||||
#define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x)
|
||||
#define AD7124_CONFIG_IN_BUFF_MSK GENMASK(7, 6)
|
||||
#define AD7124_CONFIG_IN_BUFF(x) FIELD_PREP(AD7124_CONFIG_IN_BUFF_MSK, x)
|
||||
|
||||
/* AD7124_FILTER_X */
|
||||
#define AD7124_FILTER_FS_MSK GENMASK(10, 0)
|
||||
|
@ -108,6 +110,8 @@ struct ad7124_chip_info {
|
|||
struct ad7124_channel_config {
|
||||
enum ad7124_ref_sel refsel;
|
||||
bool bipolar;
|
||||
bool buf_positive;
|
||||
bool buf_negative;
|
||||
unsigned int ain;
|
||||
unsigned int vref_mv;
|
||||
unsigned int pga_bits;
|
||||
|
@ -117,7 +121,7 @@ struct ad7124_channel_config {
|
|||
struct ad7124_state {
|
||||
const struct ad7124_chip_info *chip_info;
|
||||
struct ad_sigma_delta sd;
|
||||
struct ad7124_channel_config channel_config[4];
|
||||
struct ad7124_channel_config *channel_config;
|
||||
struct regulator *vref[4];
|
||||
struct clk *mclk;
|
||||
unsigned int adc_control;
|
||||
|
@ -435,6 +439,7 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
|||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
struct device_node *child;
|
||||
struct iio_chan_spec *chan;
|
||||
struct ad7124_channel_config *chan_config;
|
||||
unsigned int ain[2], channel = 0, tmp;
|
||||
int ret;
|
||||
|
||||
|
@ -449,8 +454,14 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
|||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
chan_config = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
|
||||
sizeof(*chan_config), GFP_KERNEL);
|
||||
if (!chan_config)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->channels = chan;
|
||||
indio_dev->num_channels = st->num_channels;
|
||||
st->channel_config = chan_config;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = of_property_read_u32(child, "reg", &channel);
|
||||
|
@ -462,13 +473,6 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (ain[0] >= st->chip_info->num_inputs ||
|
||||
ain[1] >= st->chip_info->num_inputs) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Input pin number out of range.\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
|
||||
AD7124_CHANNEL_AINM(ain[1]);
|
||||
st->channel_config[channel].bipolar =
|
||||
|
@ -480,6 +484,11 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
|||
else
|
||||
st->channel_config[channel].refsel = tmp;
|
||||
|
||||
st->channel_config[channel].buf_positive =
|
||||
of_property_read_bool(child, "adi,buffered-positive");
|
||||
st->channel_config[channel].buf_negative =
|
||||
of_property_read_bool(child, "adi,buffered-negative");
|
||||
|
||||
*chan = ad7124_channel_template;
|
||||
chan->address = channel;
|
||||
chan->scan_index = channel;
|
||||
|
@ -499,7 +508,7 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
|||
static int ad7124_setup(struct ad7124_state *st)
|
||||
{
|
||||
unsigned int val, fclk, power_mode;
|
||||
int i, ret;
|
||||
int i, ret, tmp;
|
||||
|
||||
fclk = clk_get_rate(st->mclk);
|
||||
if (!fclk)
|
||||
|
@ -532,8 +541,12 @@ static int ad7124_setup(struct ad7124_state *st)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = (st->channel_config[i].buf_positive << 1) +
|
||||
st->channel_config[i].buf_negative;
|
||||
|
||||
val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) |
|
||||
AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel);
|
||||
AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel) |
|
||||
AD7124_CONFIG_IN_BUFF(tmp);
|
||||
ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -140,7 +140,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
|||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
int ret, ch = 0;
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
|
@ -157,8 +157,10 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
|||
*val = (short)ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (st->sw_mode_en)
|
||||
ch = chan->address;
|
||||
*val = 0;
|
||||
*val2 = st->scale_avail[st->range];
|
||||
*val2 = st->scale_avail[st->range[ch]];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*val = st->oversampling;
|
||||
|
@ -194,6 +196,32 @@ static ssize_t in_voltage_scale_available_show(struct device *dev,
|
|||
|
||||
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
|
||||
|
||||
static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
gpiod_set_value(st->gpio_range, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
DECLARE_BITMAP(values, 3);
|
||||
|
||||
values[0] = val;
|
||||
|
||||
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
|
||||
st->gpio_os->info, values);
|
||||
|
||||
/* AD7616 requires a reset to update value */
|
||||
if (st->chip_info->os_req_reset)
|
||||
ad7606_reset(st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7606_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
|
@ -201,15 +229,20 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
DECLARE_BITMAP(values, 3);
|
||||
int i;
|
||||
int i, ret, ch = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
mutex_lock(&st->lock);
|
||||
i = find_closest(val2, st->scale_avail, st->num_scales);
|
||||
gpiod_set_value(st->gpio_range, i);
|
||||
st->range = i;
|
||||
if (st->sw_mode_en)
|
||||
ch = chan->address;
|
||||
ret = st->write_scale(indio_dev, ch, i);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
st->range[ch] = i;
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return 0;
|
||||
|
@ -218,17 +251,12 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
i = find_closest(val, st->oversampling_avail,
|
||||
st->num_os_ratios);
|
||||
|
||||
values[0] = i;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
|
||||
st->gpio_os->info, values);
|
||||
|
||||
/* AD7616 requires a reset to update value */
|
||||
if (st->chip_info->os_req_reset)
|
||||
ad7606_reset(st);
|
||||
|
||||
ret = st->write_os(indio_dev, i);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
st->oversampling = st->oversampling_avail[i];
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
|
@ -536,7 +564,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
|||
st->bops = bops;
|
||||
st->base_address = base_address;
|
||||
/* tied to logic low, analog input range is +/- 5V */
|
||||
st->range = 0;
|
||||
st->range[0] = 0;
|
||||
st->oversampling = 1;
|
||||
st->scale_avail = ad7606_scale_avail;
|
||||
st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
|
||||
|
@ -589,6 +617,39 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
|||
if (ret)
|
||||
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
|
||||
|
||||
st->write_scale = ad7606_write_scale_hw;
|
||||
st->write_os = ad7606_write_os_hw;
|
||||
|
||||
if (st->chip_info->sw_mode_config)
|
||||
st->sw_mode_en = device_property_present(st->dev,
|
||||
"adi,sw-mode");
|
||||
|
||||
if (st->sw_mode_en) {
|
||||
/* After reset, in software mode, ±10 V is set by default */
|
||||
memset32(st->range, 2, ARRAY_SIZE(st->range));
|
||||
indio_dev->info = &ad7606_info_os_and_range;
|
||||
|
||||
/*
|
||||
* In software mode, the range gpio has no longer its function.
|
||||
* Instead, the scale can be configured individually for each
|
||||
* channel from the range registers.
|
||||
*/
|
||||
if (st->chip_info->write_scale_sw)
|
||||
st->write_scale = st->chip_info->write_scale_sw;
|
||||
|
||||
/*
|
||||
* In software mode, the oversampling is no longer configured
|
||||
* with GPIO pins. Instead, the oversampling can be configured
|
||||
* in configuratiion register.
|
||||
*/
|
||||
if (st->chip_info->write_os_sw)
|
||||
st->write_os = st->chip_info->write_os_sw;
|
||||
|
||||
ret = st->chip_info->sw_mode_config(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
if (!st->trig)
|
||||
|
@ -643,7 +704,7 @@ static int ad7606_resume(struct device *dev)
|
|||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->gpio_standby) {
|
||||
gpiod_set_value(st->gpio_range, st->range);
|
||||
gpiod_set_value(st->gpio_range, st->range[0]);
|
||||
gpiod_set_value(st->gpio_standby, 1);
|
||||
ad7606_reset(st);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
* oversampling ratios.
|
||||
* @oversampling_num number of elements stored in oversampling_avail array
|
||||
* @os_req_reset some devices require a reset to update oversampling
|
||||
* @write_scale_sw pointer to the function which writes the scale via spi
|
||||
in software mode
|
||||
* @write_os_sw pointer to the function which writes the os via spi
|
||||
in software mode
|
||||
* @sw_mode_config: pointer to a function which configured the device
|
||||
* for software mode
|
||||
*/
|
||||
struct ad7606_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
|
@ -23,6 +29,9 @@ struct ad7606_chip_info {
|
|||
const unsigned int *oversampling_avail;
|
||||
unsigned int oversampling_num;
|
||||
bool os_req_reset;
|
||||
int (*write_scale_sw)(struct iio_dev *indio_dev, int ch, int val);
|
||||
int (*write_os_sw)(struct iio_dev *indio_dev, int val);
|
||||
int (*sw_mode_config)(struct iio_dev *indio_dev);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -34,11 +43,14 @@ struct ad7606_chip_info {
|
|||
* @range voltage range selection, selects which scale to apply
|
||||
* @oversampling oversampling selection
|
||||
* @base_address address from where to read data in parallel operation
|
||||
* @sw_mode_en software mode enabled
|
||||
* @scale_avail pointer to the array which stores the available scales
|
||||
* @num_scales number of elements stored in the scale_avail array
|
||||
* @oversampling_avail pointer to the array which stores the available
|
||||
* oversampling ratios.
|
||||
* @num_os_ratios number of elements stored in oversampling_avail array
|
||||
* @write_scale pointer to the function which writes the scale
|
||||
* @write_os pointer to the function which writes the os
|
||||
* @lock protect sensor state from concurrent accesses to GPIOs
|
||||
* @gpio_convst GPIO descriptor for conversion start signal (CONVST)
|
||||
* @gpio_reset GPIO descriptor for device hard-reset
|
||||
|
@ -57,13 +69,16 @@ struct ad7606_state {
|
|||
const struct ad7606_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
const struct ad7606_bus_ops *bops;
|
||||
unsigned int range;
|
||||
unsigned int range[16];
|
||||
unsigned int oversampling;
|
||||
void __iomem *base_address;
|
||||
bool sw_mode_en;
|
||||
const unsigned int *scale_avail;
|
||||
unsigned int num_scales;
|
||||
const unsigned int *oversampling_avail;
|
||||
unsigned int num_os_ratios;
|
||||
int (*write_scale)(struct iio_dev *indio_dev, int ch, int val);
|
||||
int (*write_os)(struct iio_dev *indio_dev, int val);
|
||||
|
||||
struct mutex lock; /* protect sensor state */
|
||||
struct gpio_desc *gpio_convst;
|
||||
|
|
|
@ -357,7 +357,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
|
|||
ret = ad_sigma_delta_set_channel(sigma_delta,
|
||||
indio_dev->channels[channel].address);
|
||||
if (ret)
|
||||
goto err_predisable;
|
||||
return ret;
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
|
@ -374,7 +374,6 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
|
|||
|
||||
err_unlock:
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
err_predisable:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1578,8 +1578,7 @@ static void at91_adc_hw_init(struct at91_adc_state *st)
|
|||
static ssize_t at91_adc_get_fifo_state(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan);
|
||||
|
@ -1588,8 +1587,7 @@ static ssize_t at91_adc_get_fifo_state(struct device *dev,
|
|||
static ssize_t at91_adc_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark);
|
||||
|
@ -1841,8 +1839,7 @@ static int at91_adc_remove(struct platform_device *pdev)
|
|||
|
||||
static __maybe_unused int at91_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
|
@ -1862,8 +1859,7 @@ static __maybe_unused int at91_adc_suspend(struct device *dev)
|
|||
|
||||
static __maybe_unused int at91_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -1359,7 +1359,7 @@ static int at91_adc_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int at91_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct iio_dev *idev = dev_get_drvdata(dev);
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
@ -1370,7 +1370,7 @@ static int at91_adc_suspend(struct device *dev)
|
|||
|
||||
static int at91_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
|
||||
struct iio_dev *idev = dev_get_drvdata(dev);
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
|
||||
clk_prepare_enable(st->clk);
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
|
||||
|
||||
#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
|
||||
#define IMX7D_ADC_INPUT_CLK 24000000
|
||||
|
||||
enum imx7d_adc_clk_pre_div {
|
||||
IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
|
||||
|
@ -100,8 +101,6 @@ struct imx7d_adc_feature {
|
|||
enum imx7d_adc_average_num avg_num;
|
||||
|
||||
u32 core_time_unit; /* impact the sample rate */
|
||||
|
||||
bool average_en;
|
||||
};
|
||||
|
||||
struct imx7d_adc {
|
||||
|
@ -179,7 +178,6 @@ static void imx7d_adc_feature_config(struct imx7d_adc *info)
|
|||
info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
|
||||
info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
|
||||
info->adc_feature.core_time_unit = 1;
|
||||
info->adc_feature.average_en = true;
|
||||
}
|
||||
|
||||
static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
|
||||
|
@ -240,9 +238,8 @@ static void imx7d_adc_channel_set(struct imx7d_adc *info)
|
|||
|
||||
/* the channel choose single conversion, and enable average mode */
|
||||
cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
|
||||
IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
|
||||
if (info->adc_feature.average_en)
|
||||
cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
|
||||
IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
|
||||
IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
|
||||
|
||||
/*
|
||||
* physical channel 0 chose logical channel A
|
||||
|
@ -272,13 +269,11 @@ static void imx7d_adc_channel_set(struct imx7d_adc *info)
|
|||
|
||||
static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
|
||||
{
|
||||
/* input clock is always 24MHz */
|
||||
u32 input_clk = 24000000;
|
||||
u32 analogue_core_clk;
|
||||
u32 core_time_unit = info->adc_feature.core_time_unit;
|
||||
u32 tmp;
|
||||
|
||||
analogue_core_clk = input_clk / info->pre_div_num;
|
||||
analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
|
||||
tmp = (core_time_unit + 1) * 6;
|
||||
|
||||
return analogue_core_clk / tmp;
|
||||
|
@ -493,11 +488,8 @@ static int imx7d_adc_probe(struct platform_device *pdev)
|
|||
info->dev = dev;
|
||||
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs)) {
|
||||
ret = PTR_ERR(info->regs);
|
||||
dev_err(dev, "Failed to remap adc memory, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
|
@ -531,9 +523,7 @@ static int imx7d_adc_probe(struct platform_device *pdev)
|
|||
indio_dev->channels = imx7d_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
|
||||
|
||||
ret = devm_request_irq(dev, irq,
|
||||
imx7d_adc_isr, 0,
|
||||
dev_name(dev), info);
|
||||
ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Amlogic Meson Successive Approximation Register (SAR) A/D Converter
|
||||
*
|
||||
|
|
|
@ -34,10 +34,26 @@
|
|||
#define MT6577_AUXADC_POWER_READY_MS 1
|
||||
#define MT6577_AUXADC_SAMPLE_READY_US 25
|
||||
|
||||
struct mtk_auxadc_compatible {
|
||||
bool sample_data_cali;
|
||||
bool check_global_idle;
|
||||
};
|
||||
|
||||
struct mt6577_auxadc_device {
|
||||
void __iomem *reg_base;
|
||||
struct clk *adc_clk;
|
||||
struct mutex lock;
|
||||
const struct mtk_auxadc_compatible *dev_comp;
|
||||
};
|
||||
|
||||
static const struct mtk_auxadc_compatible mt8173_compat = {
|
||||
.sample_data_cali = false,
|
||||
.check_global_idle = true,
|
||||
};
|
||||
|
||||
static const struct mtk_auxadc_compatible mt6765_compat = {
|
||||
.sample_data_cali = true,
|
||||
.check_global_idle = false,
|
||||
};
|
||||
|
||||
#define MT6577_AUXADC_CHANNEL(idx) { \
|
||||
|
@ -66,6 +82,11 @@ static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
|
|||
MT6577_AUXADC_CHANNEL(15),
|
||||
};
|
||||
|
||||
static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali)
|
||||
{
|
||||
return rawdata;
|
||||
}
|
||||
|
||||
static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
|
||||
u32 or_mask, u32 and_mask)
|
||||
{
|
||||
|
@ -112,15 +133,17 @@ static int mt6577_auxadc_read(struct iio_dev *indio_dev,
|
|||
/* we must delay here for hardware sample channel data */
|
||||
udelay(MT6577_AUXADC_SAMPLE_READY_US);
|
||||
|
||||
/* check MTK_AUXADC_CON2 if auxadc is idle */
|
||||
ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
|
||||
((val & MT6577_AUXADC_STA) == 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for auxadc idle time out\n");
|
||||
goto err_timeout;
|
||||
if (adc_dev->dev_comp->check_global_idle) {
|
||||
/* check MTK_AUXADC_CON2 if auxadc is idle */
|
||||
ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2,
|
||||
val, ((val & MT6577_AUXADC_STA) == 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for auxadc idle time out\n");
|
||||
goto err_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
/* read channel and make sure ready bit == 1 */
|
||||
|
@ -155,6 +178,8 @@ static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
|
|||
int *val2,
|
||||
long info)
|
||||
{
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
*val = mt6577_auxadc_read(indio_dev, chan);
|
||||
|
@ -164,6 +189,8 @@ static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
|
|||
chan->channel);
|
||||
return *val;
|
||||
}
|
||||
if (adc_dev->dev_comp->sample_data_cali)
|
||||
*val = mt_auxadc_get_cali_data(*val, true);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
|
@ -296,10 +323,11 @@ static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops,
|
|||
mt6577_auxadc_resume);
|
||||
|
||||
static const struct of_device_id mt6577_auxadc_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2701-auxadc", },
|
||||
{ .compatible = "mediatek,mt2712-auxadc", },
|
||||
{ .compatible = "mediatek,mt7622-auxadc", },
|
||||
{ .compatible = "mediatek,mt8173-auxadc", },
|
||||
{ .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat},
|
||||
{ .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat},
|
||||
{ .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat},
|
||||
{ .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat},
|
||||
{ .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
|
||||
|
|
|
@ -485,10 +485,8 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "Failed to allocate IIO device.\n");
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->dev = dev;
|
||||
|
|
|
@ -87,6 +87,7 @@ struct stm32_adc_priv_cfg {
|
|||
* @domain: irq domain reference
|
||||
* @aclk: clock reference for the analog circuitry
|
||||
* @bclk: bus clock common for all ADCs, depends on part used
|
||||
* @vdda: vdda analog supply reference
|
||||
* @vref: regulator reference
|
||||
* @cfg: compatible configuration data
|
||||
* @common: common data for all ADC instances
|
||||
|
@ -97,6 +98,7 @@ struct stm32_adc_priv {
|
|||
struct irq_domain *domain;
|
||||
struct clk *aclk;
|
||||
struct clk *bclk;
|
||||
struct regulator *vdda;
|
||||
struct regulator *vref;
|
||||
const struct stm32_adc_priv_cfg *cfg;
|
||||
struct stm32_adc_common common;
|
||||
|
@ -394,10 +396,16 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(priv->vdda);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdda enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref enable failed\n");
|
||||
return ret;
|
||||
goto err_vdda_disable;
|
||||
}
|
||||
|
||||
if (priv->bclk) {
|
||||
|
@ -425,6 +433,8 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||
clk_disable_unprepare(priv->bclk);
|
||||
err_regulator_disable:
|
||||
regulator_disable(priv->vref);
|
||||
err_vdda_disable:
|
||||
regulator_disable(priv->vdda);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -441,6 +451,7 @@ static void stm32_adc_core_hw_stop(struct device *dev)
|
|||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
regulator_disable(priv->vref);
|
||||
regulator_disable(priv->vdda);
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct platform_device *pdev)
|
||||
|
@ -468,6 +479,14 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(priv->common.base);
|
||||
priv->common.phys_base = res->start;
|
||||
|
||||
priv->vdda = devm_regulator_get(&pdev->dev, "vdda");
|
||||
if (IS_ERR(priv->vdda)) {
|
||||
ret = PTR_ERR(priv->vdda);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
ret = PTR_ERR(priv->vref);
|
||||
|
|
|
@ -39,9 +39,16 @@
|
|||
#define DFSDM_MAX_INT_OVERSAMPLING 256
|
||||
#define DFSDM_MAX_FL_OVERSAMPLING 1024
|
||||
|
||||
/* Max sample resolutions */
|
||||
#define DFSDM_MAX_RES BIT(31)
|
||||
#define DFSDM_DATA_RES BIT(23)
|
||||
/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */
|
||||
#define DFSDM_DATA_MAX BIT(30)
|
||||
/*
|
||||
* Data are output as two's complement data in a 24 bit field.
|
||||
* Data from filters are in the range +/-2^(n-1)
|
||||
* 2^(n-1) maximum positive value cannot be coded in 2's complement n bits
|
||||
* An extra bit is required to avoid wrap-around of the binary code for 2^(n-1)
|
||||
* So, the resolution of samples from filter is actually limited to 23 bits
|
||||
*/
|
||||
#define DFSDM_DATA_RES 24
|
||||
|
||||
/* Filter configuration */
|
||||
#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
|
||||
|
@ -181,14 +188,15 @@ static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
|
||||
unsigned int fast, unsigned int oversamp)
|
||||
static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
|
||||
unsigned int fast, unsigned int oversamp)
|
||||
{
|
||||
unsigned int i, d, fosr, iosr;
|
||||
u64 res;
|
||||
s64 delta;
|
||||
u64 res, max;
|
||||
int bits, shift;
|
||||
unsigned int m = 1; /* multiplication factor */
|
||||
unsigned int p = fl->ford; /* filter order (ford) */
|
||||
struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast];
|
||||
|
||||
pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp);
|
||||
/*
|
||||
|
@ -207,11 +215,8 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
|
|||
|
||||
/*
|
||||
* Look for filter and integrator oversampling ratios which allows
|
||||
* to reach 24 bits data output resolution.
|
||||
* Leave as soon as if exact resolution if reached.
|
||||
* Otherwise the higher resolution below 32 bits is kept.
|
||||
* to maximize data output resolution.
|
||||
*/
|
||||
fl->res = 0;
|
||||
for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
|
||||
for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
|
||||
if (fast)
|
||||
|
@ -236,37 +241,95 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
|
|||
res = fosr;
|
||||
for (i = p - 1; i > 0; i--) {
|
||||
res = res * (u64)fosr;
|
||||
if (res > DFSDM_MAX_RES)
|
||||
if (res > DFSDM_DATA_MAX)
|
||||
break;
|
||||
}
|
||||
if (res > DFSDM_MAX_RES)
|
||||
if (res > DFSDM_DATA_MAX)
|
||||
continue;
|
||||
|
||||
res = res * (u64)m * (u64)iosr;
|
||||
if (res > DFSDM_MAX_RES)
|
||||
if (res > DFSDM_DATA_MAX)
|
||||
continue;
|
||||
|
||||
delta = res - DFSDM_DATA_RES;
|
||||
if (res >= flo->res) {
|
||||
flo->res = res;
|
||||
flo->fosr = fosr;
|
||||
flo->iosr = iosr;
|
||||
|
||||
if (res >= fl->res) {
|
||||
fl->res = res;
|
||||
fl->fosr = fosr;
|
||||
fl->iosr = iosr;
|
||||
fl->fast = fast;
|
||||
pr_debug("%s: fosr = %d, iosr = %d\n",
|
||||
__func__, fl->fosr, fl->iosr);
|
||||
bits = fls(flo->res);
|
||||
/* 8 LBSs in data register contain chan info */
|
||||
max = flo->res << 8;
|
||||
|
||||
/* if resolution is not a power of two */
|
||||
if (flo->res > BIT(bits - 1))
|
||||
bits++;
|
||||
else
|
||||
max--;
|
||||
|
||||
shift = DFSDM_DATA_RES - bits;
|
||||
/*
|
||||
* Compute right/left shift
|
||||
* Right shift is performed by hardware
|
||||
* when transferring samples to data register.
|
||||
* Left shift is done by software on buffer
|
||||
*/
|
||||
if (shift > 0) {
|
||||
/* Resolution is lower than 24 bits */
|
||||
flo->rshift = 0;
|
||||
flo->lshift = shift;
|
||||
} else {
|
||||
/*
|
||||
* If resolution is 24 bits or more,
|
||||
* max positive value may be ambiguous
|
||||
* (equal to max negative value as sign
|
||||
* bit is dropped).
|
||||
* Reduce resolution to 23 bits (rshift)
|
||||
* to keep the sign on bit 23 and treat
|
||||
* saturation before rescaling on 24
|
||||
* bits (lshift).
|
||||
*/
|
||||
flo->rshift = 1 - shift;
|
||||
flo->lshift = 1;
|
||||
max >>= flo->rshift;
|
||||
}
|
||||
flo->max = (s32)max;
|
||||
|
||||
pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
|
||||
__func__, fast, flo->fosr, flo->iosr,
|
||||
flo->res, bits, flo->rshift,
|
||||
flo->lshift);
|
||||
}
|
||||
|
||||
if (!delta)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fl->res)
|
||||
if (!flo->res)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev,
|
||||
unsigned int oversamp)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
||||
int ret0, ret1;
|
||||
|
||||
memset(&fl->flo[0], 0, sizeof(fl->flo[0]));
|
||||
memset(&fl->flo[1], 0, sizeof(fl->flo[1]));
|
||||
|
||||
ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp);
|
||||
ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp);
|
||||
if (ret0 < 0 && ret1 < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Filter parameters not found: errors %d/%d\n",
|
||||
ret0, ret1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
|
@ -384,6 +447,50 @@ static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_channels_configure(struct stm32_dfsdm_adc *adc,
|
||||
unsigned int fl_id,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
|
||||
struct stm32_dfsdm_filter_osr *flo = &fl->flo[0];
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int bit;
|
||||
int ret;
|
||||
|
||||
fl->fast = 0;
|
||||
|
||||
/*
|
||||
* In continuous mode, use fast mode configuration,
|
||||
* if it provides a better resolution.
|
||||
*/
|
||||
if (adc->nconv == 1 && !trig &&
|
||||
(indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)) {
|
||||
if (fl->flo[1].res >= fl->flo[0].res) {
|
||||
fl->fast = 1;
|
||||
flo = &fl->flo[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (!flo->res)
|
||||
return -EINVAL;
|
||||
|
||||
for_each_set_bit(bit, &adc->smask,
|
||||
sizeof(adc->smask) * BITS_PER_BYTE) {
|
||||
chan = indio_dev->channels + bit;
|
||||
|
||||
ret = regmap_update_bits(regmap,
|
||||
DFSDM_CHCFGR2(chan->channel),
|
||||
DFSDM_CHCFGR2_DTRBS_MASK,
|
||||
DFSDM_CHCFGR2_DTRBS(flo->rshift));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
|
||||
unsigned int fl_id,
|
||||
struct iio_trigger *trig)
|
||||
|
@ -391,6 +498,7 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
|
|||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
|
||||
struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
|
||||
u32 cr1;
|
||||
const struct iio_chan_spec *chan;
|
||||
unsigned int bit, jchg = 0;
|
||||
|
@ -398,13 +506,13 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
|
|||
|
||||
/* Average integrator oversampling */
|
||||
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
|
||||
DFSDM_FCR_IOSR(fl->iosr - 1));
|
||||
DFSDM_FCR_IOSR(flo->iosr - 1));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Filter order and Oversampling */
|
||||
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
|
||||
DFSDM_FCR_FOSR(fl->fosr - 1));
|
||||
DFSDM_FCR_FOSR(flo->fosr - 1));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -417,6 +525,12 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
|
||||
DFSDM_CR1_FAST_MASK,
|
||||
DFSDM_CR1_FAST(fl->fast));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* DFSDM modes configuration W.R.T audio/iio type modes
|
||||
* ----------------------------------------------------------------
|
||||
|
@ -563,7 +677,6 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
|
|||
unsigned int spi_freq)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
||||
unsigned int oversamp;
|
||||
int ret;
|
||||
|
||||
|
@ -573,11 +686,10 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
|
|||
"Rate not accurate. requested (%u), actual (%u)\n",
|
||||
sample_freq, spi_freq / oversamp);
|
||||
|
||||
ret = stm32_dfsdm_set_osrs(fl, 0, oversamp);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "No filter parameters that match!\n");
|
||||
ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc->sample_freq = spi_freq / oversamp;
|
||||
adc->oversamp = oversamp;
|
||||
|
||||
|
@ -623,6 +735,10 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
|
|||
struct regmap *regmap = adc->dfsdm->regmap;
|
||||
int ret;
|
||||
|
||||
ret = stm32_dfsdm_channels_configure(adc, adc->fl_id, trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = stm32_dfsdm_start_channel(adc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -702,6 +818,30 @@ static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc,
|
||||
s32 *buffer)
|
||||
{
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
||||
struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
|
||||
unsigned int i = adc->nconv;
|
||||
s32 *ptr = buffer;
|
||||
|
||||
while (i--) {
|
||||
/* Mask 8 LSB that contains the channel ID */
|
||||
*ptr &= 0xFFFFFF00;
|
||||
/* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */
|
||||
if (*ptr > flo->max)
|
||||
*ptr -= 1;
|
||||
/*
|
||||
* Samples from filter are retrieved with 23 bits resolution
|
||||
* or less. Shift left to align MSB on 24 bits.
|
||||
*/
|
||||
*ptr <<= flo->lshift;
|
||||
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
|
@ -710,7 +850,9 @@ static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
|
|||
int available = stm32_dfsdm_adc_dma_residue(adc);
|
||||
|
||||
while (available >= indio_dev->scan_bytes) {
|
||||
u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
|
||||
s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
|
||||
|
||||
stm32_dfsdm_process_data(adc, buffer);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
pf->timestamp);
|
||||
|
@ -751,10 +893,10 @@ static void stm32_dfsdm_dma_buffer_done(void *data)
|
|||
old_pos = adc->bufi;
|
||||
|
||||
while (available >= indio_dev->scan_bytes) {
|
||||
u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
|
||||
s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
|
||||
|
||||
stm32_dfsdm_process_data(adc, buffer);
|
||||
|
||||
/* Mask 8 LSB that contains the channel ID */
|
||||
*buffer = (*buffer & 0xFFFFFF00) << 8;
|
||||
available -= indio_dev->scan_bytes;
|
||||
adc->bufi += indio_dev->scan_bytes;
|
||||
if (adc->bufi >= adc->buf_sz) {
|
||||
|
@ -776,6 +918,11 @@ static void stm32_dfsdm_dma_buffer_done(void *data)
|
|||
static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
/*
|
||||
* The DFSDM supports half-word transfers. However, for 16 bits record,
|
||||
* 4 bytes buswidth is kept, to avoid losing samples LSBs when left
|
||||
* shift is required.
|
||||
*/
|
||||
struct dma_slave_config config = {
|
||||
.src_addr = (dma_addr_t)adc->dfsdm->phys_base,
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
|
@ -1068,7 +1215,6 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
||||
struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
|
||||
unsigned int spi_freq;
|
||||
int ret = -EINVAL;
|
||||
|
@ -1078,7 +1224,7 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
|||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = stm32_dfsdm_set_osrs(fl, 0, val);
|
||||
ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
|
||||
if (!ret)
|
||||
adc->oversamp = val;
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
@ -1277,11 +1423,11 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
|
||||
if (adc->dev_data->type == DFSDM_AUDIO) {
|
||||
ch->scan_type.sign = 's';
|
||||
ch->ext_info = dfsdm_adc_audio_ext_info;
|
||||
} else {
|
||||
ch->scan_type.sign = 'u';
|
||||
ch->scan_type.shift = 8;
|
||||
}
|
||||
ch->scan_type.sign = 's';
|
||||
ch->scan_type.realbits = 24;
|
||||
ch->scan_type.storagebits = 32;
|
||||
|
||||
|
@ -1327,8 +1473,7 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
|
|||
int ret, chan_idx;
|
||||
|
||||
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
|
||||
ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0,
|
||||
adc->oversamp);
|
||||
ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -1456,6 +1601,12 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
|||
* So IRQ associated to filter instance 0 is dedicated to the Filter 0.
|
||||
*/
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
if (irq != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get IRQ: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
|
||||
0, pdev->name, adc);
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -233,6 +233,8 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
|
|||
}
|
||||
priv->dfsdm.phys_base = res->start;
|
||||
priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->dfsdm.base))
|
||||
return PTR_ERR(priv->dfsdm.base);
|
||||
|
||||
/*
|
||||
* "dfsdm" clock is mandatory for DFSDM peripheral clocking.
|
||||
|
@ -242,8 +244,10 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
|
|||
*/
|
||||
priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
|
||||
return -EINVAL;
|
||||
ret = PTR_ERR(priv->clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get clock (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->aclk = devm_clk_get(&pdev->dev, "audio");
|
||||
|
|
|
@ -243,19 +243,33 @@ enum stm32_dfsdm_sinc_order {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
|
||||
* struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling
|
||||
* @iosr: integrator oversampling
|
||||
* @fosr: filter oversampling
|
||||
* @ford: filter order
|
||||
* @rshift: output sample right shift (hardware shift)
|
||||
* @lshift: output sample left shift (software shift)
|
||||
* @res: output sample resolution
|
||||
* @max: output sample maximum positive value
|
||||
*/
|
||||
struct stm32_dfsdm_filter_osr {
|
||||
unsigned int iosr;
|
||||
unsigned int fosr;
|
||||
unsigned int rshift;
|
||||
unsigned int lshift;
|
||||
u64 res;
|
||||
s32 max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
|
||||
* @ford: filter order
|
||||
* @flo: filter oversampling data table indexed by fast mode flag
|
||||
* @sync_mode: filter synchronized with filter 0
|
||||
* @fast: filter fast mode
|
||||
*/
|
||||
struct stm32_dfsdm_filter {
|
||||
unsigned int iosr;
|
||||
unsigned int fosr;
|
||||
enum stm32_dfsdm_sinc_order ford;
|
||||
u64 res;
|
||||
struct stm32_dfsdm_filter_osr flo[2];
|
||||
unsigned int sync_mode;
|
||||
unsigned int fast;
|
||||
};
|
||||
|
|
|
@ -65,6 +65,8 @@ static int stmpe_read_voltage(struct stmpe_adc *info,
|
|||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
info->channel = (u8)chan->channel;
|
||||
|
||||
if (info->channel > STMPE_ADC_LAST_NR) {
|
||||
|
@ -72,23 +74,16 @@ static int stmpe_read_voltage(struct stmpe_adc *info,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN,
|
||||
STMPE_ADC_CH(info->channel));
|
||||
|
||||
stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT,
|
||||
STMPE_ADC_CH(info->channel));
|
||||
|
||||
*val = info->value;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, STMPE_ADC_TIMEOUT);
|
||||
ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
|
||||
|
||||
if (ret <= 0) {
|
||||
stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
|
||||
STMPE_ADC_CH(info->channel));
|
||||
mutex_unlock(&info->lock);
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
else
|
||||
return ret;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*val = info->value;
|
||||
|
@ -105,6 +100,8 @@ static int stmpe_read_temp(struct stmpe_adc *info,
|
|||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
info->channel = (u8)chan->channel;
|
||||
|
||||
if (info->channel != STMPE_TEMP_CHANNEL) {
|
||||
|
@ -115,15 +112,11 @@ static int stmpe_read_temp(struct stmpe_adc *info,
|
|||
stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL,
|
||||
STMPE_START_ONE_TEMP_CONV);
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, STMPE_ADC_TIMEOUT);
|
||||
ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
|
||||
|
||||
if (ret <= 0) {
|
||||
mutex_unlock(&info->lock);
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
else
|
||||
return ret;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -331,6 +324,12 @@ static int stmpe_adc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN,
|
||||
~(norequest_mask & 0xFF));
|
||||
|
||||
stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
|
||||
~(norequest_mask & 0xFF));
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
|
@ -353,9 +352,14 @@ static struct platform_driver stmpe_adc_driver = {
|
|||
.pm = &stmpe_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(stmpe_adc_driver);
|
||||
|
||||
static const struct of_device_id stmpe_adc_ids[] = {
|
||||
{ .compatible = "st,stmpe-adc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stmpe_adc_ids);
|
||||
|
||||
MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
|
||||
MODULE_DESCRIPTION("STMPEXXX ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
|
||||
*
|
||||
* Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Gain Amplifiers, etc.
|
||||
#
|
||||
|
@ -7,12 +7,17 @@
|
|||
menu "Amplifiers"
|
||||
|
||||
config AD8366
|
||||
tristate "Analog Devices AD8366 VGA"
|
||||
tristate "Analog Devices AD8366 and similar Gain Amplifiers"
|
||||
depends on SPI
|
||||
depends on GPIOLIB
|
||||
select BITREVERSE
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD8366
|
||||
SPI Dual-Digital Variable Gain Amplifier (VGA).
|
||||
Say yes here to build support for Analog Devices AD8366 and similar
|
||||
gain amplifiers. This driver supports the following gain amplifiers
|
||||
from Analog Devices:
|
||||
AD8366 Dual-Digital Variable Gain Amplifier (VGA)
|
||||
ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
|
||||
ADL5240 Digitally controlled variable gain amplifier (VGA)
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad8366.
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA)
|
||||
* AD8366 and similar Gain Amplifiers
|
||||
* This driver supports the following gain amplifiers:
|
||||
* AD8366 Dual-Digital Variable Gain Amplifier (VGA)
|
||||
* ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
|
||||
* ADL5240 Digitally controlled variable gain amplifier (VGA)
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Copyright 2012-2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
@ -11,6 +15,7 @@
|
|||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitrev.h>
|
||||
|
@ -18,10 +23,25 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
enum ad8366_type {
|
||||
ID_AD8366,
|
||||
ID_ADA4961,
|
||||
ID_ADL5240,
|
||||
};
|
||||
|
||||
struct ad8366_info {
|
||||
int gain_min;
|
||||
int gain_max;
|
||||
};
|
||||
|
||||
struct ad8366_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct mutex lock; /* protect sensor state */
|
||||
struct gpio_desc *reset_gpio;
|
||||
unsigned char ch[2];
|
||||
enum ad8366_type type;
|
||||
struct ad8366_info *info;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
|
@ -29,19 +49,44 @@ struct ad8366_state {
|
|||
unsigned char data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static struct ad8366_info ad8366_infos[] = {
|
||||
[ID_AD8366] = {
|
||||
.gain_min = 4500,
|
||||
.gain_max = 20500,
|
||||
},
|
||||
[ID_ADA4961] = {
|
||||
.gain_min = -6000,
|
||||
.gain_max = 15000,
|
||||
},
|
||||
[ID_ADL5240] = {
|
||||
.gain_min = -11500,
|
||||
.gain_max = 20000,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad8366_write(struct iio_dev *indio_dev,
|
||||
unsigned char ch_a, unsigned char ch_b)
|
||||
{
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ch_a = bitrev8(ch_a & 0x3F);
|
||||
ch_b = bitrev8(ch_b & 0x3F);
|
||||
switch (st->type) {
|
||||
case ID_AD8366:
|
||||
ch_a = bitrev8(ch_a & 0x3F);
|
||||
ch_b = bitrev8(ch_b & 0x3F);
|
||||
|
||||
st->data[0] = ch_b >> 4;
|
||||
st->data[1] = (ch_b << 4) | (ch_a >> 2);
|
||||
st->data[0] = ch_b >> 4;
|
||||
st->data[1] = (ch_b << 4) | (ch_a >> 2);
|
||||
break;
|
||||
case ID_ADA4961:
|
||||
st->data[0] = ch_a & 0x1F;
|
||||
break;
|
||||
case ID_ADL5240:
|
||||
st->data[0] = (ch_a & 0x3F);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data));
|
||||
ret = spi_write(st->spi, st->data, indio_dev->num_channels);
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "write failed (%d)", ret);
|
||||
|
||||
|
@ -56,24 +101,35 @@ static int ad8366_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned code;
|
||||
int code, gain = 0;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
code = st->ch[chan->channel];
|
||||
|
||||
switch (st->type) {
|
||||
case ID_AD8366:
|
||||
gain = code * 253 + 4500;
|
||||
break;
|
||||
case ID_ADA4961:
|
||||
gain = 15000 - code * 1000;
|
||||
break;
|
||||
case ID_ADL5240:
|
||||
gain = 20000 - 31500 + code * 500;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Values in dB */
|
||||
code = code * 253 + 4500;
|
||||
*val = code / 1000;
|
||||
*val2 = (code % 1000) * 1000;
|
||||
*val = gain / 1000;
|
||||
*val2 = (gain % 1000) * 1000;
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_MICRO_DB;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
@ -85,21 +141,32 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
unsigned code;
|
||||
struct ad8366_info *inf = st->info;
|
||||
int code = 0, gain;
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val2 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Values in dB */
|
||||
code = (((u8)val * 1000) + ((u32)val2 / 1000));
|
||||
if (val < 0)
|
||||
gain = (val * 1000) - (val2 / 1000);
|
||||
else
|
||||
gain = (val * 1000) + (val2 / 1000);
|
||||
|
||||
if (code > 20500 || code < 4500)
|
||||
if (gain > inf->gain_max || gain < inf->gain_min)
|
||||
return -EINVAL;
|
||||
|
||||
code = (code - 4500) / 253;
|
||||
switch (st->type) {
|
||||
case ID_AD8366:
|
||||
code = (gain - 4500) / 253;
|
||||
break;
|
||||
case ID_ADA4961:
|
||||
code = (15000 - gain) / 1000;
|
||||
break;
|
||||
case ID_ADL5240:
|
||||
code = ((gain - 500 - 20000) / 500) & 0x3F;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
st->ch[chan->channel] = code;
|
||||
|
@ -108,7 +175,7 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
|
|||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -131,6 +198,10 @@ static const struct iio_chan_spec ad8366_channels[] = {
|
|||
AD8366_CHAN(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ada4961_channels[] = {
|
||||
AD8366_CHAN(0),
|
||||
};
|
||||
|
||||
static int ad8366_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
|
@ -151,14 +222,33 @@ static int ad8366_probe(struct spi_device *spi)
|
|||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
st->spi = spi;
|
||||
st->type = spi_get_device_id(spi)->driver_data;
|
||||
|
||||
switch (st->type) {
|
||||
case ID_AD8366:
|
||||
indio_dev->channels = ad8366_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
|
||||
break;
|
||||
case ID_ADA4961:
|
||||
case ID_ADL5240:
|
||||
st->reset_gpio = devm_gpiod_get(&spi->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
indio_dev->channels = ada4961_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ada4961_channels);
|
||||
break;
|
||||
default:
|
||||
dev_err(&spi->dev, "Invalid device ID\n");
|
||||
ret = -EINVAL;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
st->info = &ad8366_infos[st->type];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad8366_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad8366_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
|
||||
|
||||
ret = ad8366_write(indio_dev, 0 , 0);
|
||||
if (ret < 0)
|
||||
|
@ -192,7 +282,9 @@ static int ad8366_remove(struct spi_device *spi)
|
|||
}
|
||||
|
||||
static const struct spi_device_id ad8366_id[] = {
|
||||
{"ad8366", 0},
|
||||
{"ad8366", ID_AD8366},
|
||||
{"ada4961", ID_ADA4961},
|
||||
{"adl5240", ID_ADL5240},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad8366_id);
|
||||
|
@ -209,5 +301,5 @@ static struct spi_driver ad8366_driver = {
|
|||
module_spi_driver(ad8366_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD8366 VGA");
|
||||
MODULE_DESCRIPTION("Analog Devices AD8366 and similar Gain Amplifiers");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -125,6 +125,15 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
|
|||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", st->param.info.sensor_num);
|
||||
}
|
||||
|
||||
static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
|
@ -140,6 +149,11 @@ const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
|
|||
.shared = IIO_SHARED_BY_ALL,
|
||||
.write = cros_ec_sensors_calibrate
|
||||
},
|
||||
{
|
||||
.name = "id",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = cros_ec_sensors_id
|
||||
},
|
||||
{
|
||||
.name = "location",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
|
@ -582,7 +584,7 @@ static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
unsigned int dc_dc_mode, dac_config_mode, val;
|
||||
unsigned int dac_config_mode, val;
|
||||
unsigned long int dac_config_msk;
|
||||
int ret;
|
||||
|
||||
|
@ -591,13 +593,10 @@ static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (pwr_down) {
|
||||
dc_dc_mode = AD5758_DCDC_MODE_POWER_OFF;
|
||||
if (pwr_down)
|
||||
val = 0;
|
||||
} else {
|
||||
dc_dc_mode = st->dc_dc_mode;
|
||||
else
|
||||
val = 1;
|
||||
}
|
||||
|
||||
dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
|
||||
AD5758_DAC_CONFIG_INT_EN_MODE(val);
|
||||
|
@ -885,9 +884,16 @@ static const struct spi_device_id ad5758_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5758_id);
|
||||
|
||||
static const struct of_device_id ad5758_of_match[] = {
|
||||
{ .compatible = "adi,ad5758" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5758_of_match);
|
||||
|
||||
static struct spi_driver ad5758_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = ad5758_of_match,
|
||||
},
|
||||
.probe = ad5758_probe,
|
||||
.id_table = ad5758_id,
|
||||
|
|
|
@ -233,12 +233,6 @@ static int ds4424_probe(struct i2c_client *client,
|
|||
indio_dev->dev.of_node = client->dev.of_node;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
if (!client->dev.of_node) {
|
||||
dev_err(&client->dev,
|
||||
"Not found DT.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->vcc_reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (IS_ERR(data->vcc_reg)) {
|
||||
dev_err(&client->dev,
|
||||
|
|
|
@ -39,5 +39,15 @@ config ADF4350
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called adf4350.
|
||||
|
||||
config ADF4371
|
||||
tristate "Analog Devices ADF4371/ADF4372 Wideband Synthesizers"
|
||||
depends on SPI
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADF4371 and ADF4372
|
||||
Wideband Synthesizers. The driver provides direct access via sysfs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adf4371.
|
||||
endmenu
|
||||
endmenu
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD9523) += ad9523.o
|
||||
obj-$(CONFIG_ADF4350) += adf4350.o
|
||||
obj-$(CONFIG_ADF4371) += adf4371.o
|
||||
|
|
|
@ -861,9 +861,11 @@ static int ad9523_setup(struct iio_dev *indio_dev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
|
||||
/ pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
|
||||
pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
|
||||
st->vco_freq = div_u64((unsigned long long)pdata->vcxo_freq *
|
||||
(pdata->pll2_freq_doubler_en ? 2 : 1) *
|
||||
AD9523_PLL2_FB_NDIV(pdata->pll2_ndiv_a_cnt,
|
||||
pdata->pll2_ndiv_b_cnt),
|
||||
pdata->pll2_r2_div);
|
||||
|
||||
ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
|
||||
AD9523_PLL2_VCO_CALIBRATE);
|
||||
|
|
|
@ -0,0 +1,632 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Analog Devices ADF4371 SPI Wideband Synthesizer driver
|
||||
*
|
||||
* Copyright 2019 Analog Devices Inc.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/* Registers address macro */
|
||||
#define ADF4371_REG(x) (x)
|
||||
|
||||
/* ADF4371_REG0 */
|
||||
#define ADF4371_ADDR_ASC_MSK BIT(2)
|
||||
#define ADF4371_ADDR_ASC(x) FIELD_PREP(ADF4371_ADDR_ASC_MSK, x)
|
||||
#define ADF4371_ADDR_ASC_R_MSK BIT(5)
|
||||
#define ADF4371_ADDR_ASC_R(x) FIELD_PREP(ADF4371_ADDR_ASC_R_MSK, x)
|
||||
#define ADF4371_RESET_CMD 0x81
|
||||
|
||||
/* ADF4371_REG17 */
|
||||
#define ADF4371_FRAC2WORD_L_MSK GENMASK(7, 1)
|
||||
#define ADF4371_FRAC2WORD_L(x) FIELD_PREP(ADF4371_FRAC2WORD_L_MSK, x)
|
||||
#define ADF4371_FRAC1WORD_MSK BIT(0)
|
||||
#define ADF4371_FRAC1WORD(x) FIELD_PREP(ADF4371_FRAC1WORD_MSK, x)
|
||||
|
||||
/* ADF4371_REG18 */
|
||||
#define ADF4371_FRAC2WORD_H_MSK GENMASK(6, 0)
|
||||
#define ADF4371_FRAC2WORD_H(x) FIELD_PREP(ADF4371_FRAC2WORD_H_MSK, x)
|
||||
|
||||
/* ADF4371_REG1A */
|
||||
#define ADF4371_MOD2WORD_MSK GENMASK(5, 0)
|
||||
#define ADF4371_MOD2WORD(x) FIELD_PREP(ADF4371_MOD2WORD_MSK, x)
|
||||
|
||||
/* ADF4371_REG24 */
|
||||
#define ADF4371_RF_DIV_SEL_MSK GENMASK(6, 4)
|
||||
#define ADF4371_RF_DIV_SEL(x) FIELD_PREP(ADF4371_RF_DIV_SEL_MSK, x)
|
||||
|
||||
/* ADF4371_REG25 */
|
||||
#define ADF4371_MUTE_LD_MSK BIT(7)
|
||||
#define ADF4371_MUTE_LD(x) FIELD_PREP(ADF4371_MUTE_LD_MSK, x)
|
||||
|
||||
/* ADF4371_REG32 */
|
||||
#define ADF4371_TIMEOUT_MSK GENMASK(1, 0)
|
||||
#define ADF4371_TIMEOUT(x) FIELD_PREP(ADF4371_TIMEOUT_MSK, x)
|
||||
|
||||
/* ADF4371_REG34 */
|
||||
#define ADF4371_VCO_ALC_TOUT_MSK GENMASK(4, 0)
|
||||
#define ADF4371_VCO_ALC_TOUT(x) FIELD_PREP(ADF4371_VCO_ALC_TOUT_MSK, x)
|
||||
|
||||
/* Specifications */
|
||||
#define ADF4371_MIN_VCO_FREQ 4000000000ULL /* 4000 MHz */
|
||||
#define ADF4371_MAX_VCO_FREQ 8000000000ULL /* 8000 MHz */
|
||||
#define ADF4371_MAX_OUT_RF8_FREQ ADF4371_MAX_VCO_FREQ /* Hz */
|
||||
#define ADF4371_MIN_OUT_RF8_FREQ (ADF4371_MIN_VCO_FREQ / 64) /* Hz */
|
||||
#define ADF4371_MAX_OUT_RF16_FREQ (ADF4371_MAX_VCO_FREQ * 2) /* Hz */
|
||||
#define ADF4371_MIN_OUT_RF16_FREQ (ADF4371_MIN_VCO_FREQ * 2) /* Hz */
|
||||
#define ADF4371_MAX_OUT_RF32_FREQ (ADF4371_MAX_VCO_FREQ * 4) /* Hz */
|
||||
#define ADF4371_MIN_OUT_RF32_FREQ (ADF4371_MIN_VCO_FREQ * 4) /* Hz */
|
||||
|
||||
#define ADF4371_MAX_FREQ_PFD 250000000UL /* Hz */
|
||||
#define ADF4371_MAX_FREQ_REFIN 600000000UL /* Hz */
|
||||
|
||||
/* MOD1 is a 24-bit primary modulus with fixed value of 2^25 */
|
||||
#define ADF4371_MODULUS1 33554432ULL
|
||||
/* MOD2 is the programmable, 14-bit auxiliary fractional modulus */
|
||||
#define ADF4371_MAX_MODULUS2 BIT(14)
|
||||
|
||||
#define ADF4371_CHECK_RANGE(freq, range) \
|
||||
((freq > ADF4371_MAX_ ## range) || (freq < ADF4371_MIN_ ## range))
|
||||
|
||||
enum {
|
||||
ADF4371_FREQ,
|
||||
ADF4371_POWER_DOWN,
|
||||
ADF4371_CHANNEL_NAME
|
||||
};
|
||||
|
||||
enum {
|
||||
ADF4371_CH_RF8,
|
||||
ADF4371_CH_RFAUX8,
|
||||
ADF4371_CH_RF16,
|
||||
ADF4371_CH_RF32
|
||||
};
|
||||
|
||||
enum adf4371_variant {
|
||||
ADF4371,
|
||||
ADF4372
|
||||
};
|
||||
|
||||
struct adf4371_pwrdown {
|
||||
unsigned int reg;
|
||||
unsigned int bit;
|
||||
};
|
||||
|
||||
static const char * const adf4371_ch_names[] = {
|
||||
"RF8x", "RFAUX8x", "RF16x", "RF32x"
|
||||
};
|
||||
|
||||
static const struct adf4371_pwrdown adf4371_pwrdown_ch[4] = {
|
||||
[ADF4371_CH_RF8] = { ADF4371_REG(0x25), 2 },
|
||||
[ADF4371_CH_RFAUX8] = { ADF4371_REG(0x72), 3 },
|
||||
[ADF4371_CH_RF16] = { ADF4371_REG(0x25), 3 },
|
||||
[ADF4371_CH_RF32] = { ADF4371_REG(0x25), 4 },
|
||||
};
|
||||
|
||||
static const struct reg_sequence adf4371_reg_defaults[] = {
|
||||
{ ADF4371_REG(0x0), 0x18 },
|
||||
{ ADF4371_REG(0x12), 0x40 },
|
||||
{ ADF4371_REG(0x1E), 0x48 },
|
||||
{ ADF4371_REG(0x20), 0x14 },
|
||||
{ ADF4371_REG(0x22), 0x00 },
|
||||
{ ADF4371_REG(0x23), 0x00 },
|
||||
{ ADF4371_REG(0x24), 0x80 },
|
||||
{ ADF4371_REG(0x25), 0x07 },
|
||||
{ ADF4371_REG(0x27), 0xC5 },
|
||||
{ ADF4371_REG(0x28), 0x83 },
|
||||
{ ADF4371_REG(0x2C), 0x44 },
|
||||
{ ADF4371_REG(0x2D), 0x11 },
|
||||
{ ADF4371_REG(0x2E), 0x12 },
|
||||
{ ADF4371_REG(0x2F), 0x94 },
|
||||
{ ADF4371_REG(0x32), 0x04 },
|
||||
{ ADF4371_REG(0x35), 0xFA },
|
||||
{ ADF4371_REG(0x36), 0x30 },
|
||||
{ ADF4371_REG(0x39), 0x07 },
|
||||
{ ADF4371_REG(0x3A), 0x55 },
|
||||
{ ADF4371_REG(0x3E), 0x0C },
|
||||
{ ADF4371_REG(0x3F), 0x80 },
|
||||
{ ADF4371_REG(0x40), 0x50 },
|
||||
{ ADF4371_REG(0x41), 0x28 },
|
||||
{ ADF4371_REG(0x47), 0xC0 },
|
||||
{ ADF4371_REG(0x52), 0xF4 },
|
||||
{ ADF4371_REG(0x70), 0x03 },
|
||||
{ ADF4371_REG(0x71), 0x60 },
|
||||
{ ADF4371_REG(0x72), 0x32 },
|
||||
};
|
||||
|
||||
static const struct regmap_config adf4371_regmap_config = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = BIT(7),
|
||||
};
|
||||
|
||||
struct adf4371_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
};
|
||||
|
||||
struct adf4371_state {
|
||||
struct spi_device *spi;
|
||||
struct regmap *regmap;
|
||||
struct clk *clkin;
|
||||
/*
|
||||
* Lock for accessing device registers. Some operations require
|
||||
* multiple consecutive R/W operations, during which the device
|
||||
* shouldn't be interrupted. The buffers are also shared across
|
||||
* all operations so need to be protected on stand alone reads and
|
||||
* writes.
|
||||
*/
|
||||
struct mutex lock;
|
||||
const struct adf4371_chip_info *chip_info;
|
||||
unsigned long clkin_freq;
|
||||
unsigned long fpfd;
|
||||
unsigned int integer;
|
||||
unsigned int fract1;
|
||||
unsigned int fract2;
|
||||
unsigned int mod2;
|
||||
unsigned int rf_div_sel;
|
||||
unsigned int ref_div_factor;
|
||||
u8 buf[10] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static unsigned long long adf4371_pll_fract_n_get_rate(struct adf4371_state *st,
|
||||
u32 channel)
|
||||
{
|
||||
unsigned long long val, tmp;
|
||||
unsigned int ref_div_sel;
|
||||
|
||||
val = (((u64)st->integer * ADF4371_MODULUS1) + st->fract1) * st->fpfd;
|
||||
tmp = (u64)st->fract2 * st->fpfd;
|
||||
do_div(tmp, st->mod2);
|
||||
val += tmp + ADF4371_MODULUS1 / 2;
|
||||
|
||||
if (channel == ADF4371_CH_RF8 || channel == ADF4371_CH_RFAUX8)
|
||||
ref_div_sel = st->rf_div_sel;
|
||||
else
|
||||
ref_div_sel = 0;
|
||||
|
||||
do_div(val, ADF4371_MODULUS1 * (1 << ref_div_sel));
|
||||
|
||||
if (channel == ADF4371_CH_RF16)
|
||||
val <<= 1;
|
||||
else if (channel == ADF4371_CH_RF32)
|
||||
val <<= 2;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void adf4371_pll_fract_n_compute(unsigned long long vco,
|
||||
unsigned long long pfd,
|
||||
unsigned int *integer,
|
||||
unsigned int *fract1,
|
||||
unsigned int *fract2,
|
||||
unsigned int *mod2)
|
||||
{
|
||||
unsigned long long tmp;
|
||||
u32 gcd_div;
|
||||
|
||||
tmp = do_div(vco, pfd);
|
||||
tmp = tmp * ADF4371_MODULUS1;
|
||||
*fract2 = do_div(tmp, pfd);
|
||||
|
||||
*integer = vco;
|
||||
*fract1 = tmp;
|
||||
|
||||
*mod2 = pfd;
|
||||
|
||||
while (*mod2 > ADF4371_MAX_MODULUS2) {
|
||||
*mod2 >>= 1;
|
||||
*fract2 >>= 1;
|
||||
}
|
||||
|
||||
gcd_div = gcd(*fract2, *mod2);
|
||||
*mod2 /= gcd_div;
|
||||
*fract2 /= gcd_div;
|
||||
}
|
||||
|
||||
static int adf4371_set_freq(struct adf4371_state *st, unsigned long long freq,
|
||||
unsigned int channel)
|
||||
{
|
||||
u32 cp_bleed;
|
||||
u8 int_mode = 0;
|
||||
int ret;
|
||||
|
||||
switch (channel) {
|
||||
case ADF4371_CH_RF8:
|
||||
case ADF4371_CH_RFAUX8:
|
||||
if (ADF4371_CHECK_RANGE(freq, OUT_RF8_FREQ))
|
||||
return -EINVAL;
|
||||
|
||||
st->rf_div_sel = 0;
|
||||
|
||||
while (freq < ADF4371_MIN_VCO_FREQ) {
|
||||
freq <<= 1;
|
||||
st->rf_div_sel++;
|
||||
}
|
||||
break;
|
||||
case ADF4371_CH_RF16:
|
||||
/* ADF4371 RF16 8000...16000 MHz */
|
||||
if (ADF4371_CHECK_RANGE(freq, OUT_RF16_FREQ))
|
||||
return -EINVAL;
|
||||
|
||||
freq >>= 1;
|
||||
break;
|
||||
case ADF4371_CH_RF32:
|
||||
/* ADF4371 RF32 16000...32000 MHz */
|
||||
if (ADF4371_CHECK_RANGE(freq, OUT_RF32_FREQ))
|
||||
return -EINVAL;
|
||||
|
||||
freq >>= 2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adf4371_pll_fract_n_compute(freq, st->fpfd, &st->integer, &st->fract1,
|
||||
&st->fract2, &st->mod2);
|
||||
st->buf[0] = st->integer >> 8;
|
||||
st->buf[1] = 0x40; /* REG12 default */
|
||||
st->buf[2] = 0x00;
|
||||
st->buf[3] = st->fract2 & 0xFF;
|
||||
st->buf[4] = st->fract2 >> 7;
|
||||
st->buf[5] = st->fract2 >> 15;
|
||||
st->buf[6] = ADF4371_FRAC2WORD_L(st->fract2 & 0x7F) |
|
||||
ADF4371_FRAC1WORD(st->fract1 >> 23);
|
||||
st->buf[7] = ADF4371_FRAC2WORD_H(st->fract2 >> 7);
|
||||
st->buf[8] = st->mod2 & 0xFF;
|
||||
st->buf[9] = ADF4371_MOD2WORD(st->mod2 >> 8);
|
||||
|
||||
ret = regmap_bulk_write(st->regmap, ADF4371_REG(0x11), st->buf, 10);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* The R counter allows the input reference frequency to be
|
||||
* divided down to produce the reference clock to the PFD
|
||||
*/
|
||||
ret = regmap_write(st->regmap, ADF4371_REG(0x1F), st->ref_div_factor);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x24),
|
||||
ADF4371_RF_DIV_SEL_MSK,
|
||||
ADF4371_RF_DIV_SEL(st->rf_div_sel));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cp_bleed = DIV_ROUND_UP(400 * 1750, st->integer * 375);
|
||||
cp_bleed = clamp(cp_bleed, 1U, 255U);
|
||||
ret = regmap_write(st->regmap, ADF4371_REG(0x26), cp_bleed);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Set to 1 when in INT mode (when FRAC1 = FRAC2 = 0),
|
||||
* and set to 0 when in FRAC mode.
|
||||
*/
|
||||
if (st->fract1 == 0 && st->fract2 == 0)
|
||||
int_mode = 0x01;
|
||||
|
||||
ret = regmap_write(st->regmap, ADF4371_REG(0x2B), int_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_write(st->regmap, ADF4371_REG(0x10), st->integer & 0xFF);
|
||||
}
|
||||
|
||||
static ssize_t adf4371_read(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct adf4371_state *st = iio_priv(indio_dev);
|
||||
unsigned long long val = 0;
|
||||
unsigned int readval, reg, bit;
|
||||
int ret;
|
||||
|
||||
switch ((u32)private) {
|
||||
case ADF4371_FREQ:
|
||||
val = adf4371_pll_fract_n_get_rate(st, chan->channel);
|
||||
ret = regmap_read(st->regmap, ADF4371_REG(0x7C), &readval);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
if (readval == 0x00) {
|
||||
dev_dbg(&st->spi->dev, "PLL un-locked\n");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
break;
|
||||
case ADF4371_POWER_DOWN:
|
||||
reg = adf4371_pwrdown_ch[chan->channel].reg;
|
||||
bit = adf4371_pwrdown_ch[chan->channel].bit;
|
||||
|
||||
ret = regmap_read(st->regmap, reg, &readval);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
val = !(readval & BIT(bit));
|
||||
break;
|
||||
case ADF4371_CHANNEL_NAME:
|
||||
return sprintf(buf, "%s\n", adf4371_ch_names[chan->channel]);
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
static ssize_t adf4371_write(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct adf4371_state *st = iio_priv(indio_dev);
|
||||
unsigned long long freq;
|
||||
bool power_down;
|
||||
unsigned int bit, readval, reg;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
switch ((u32)private) {
|
||||
case ADF4371_FREQ:
|
||||
ret = kstrtoull(buf, 10, &freq);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = adf4371_set_freq(st, freq, chan->channel);
|
||||
break;
|
||||
case ADF4371_POWER_DOWN:
|
||||
ret = kstrtobool(buf, &power_down);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
reg = adf4371_pwrdown_ch[chan->channel].reg;
|
||||
bit = adf4371_pwrdown_ch[chan->channel].bit;
|
||||
ret = regmap_read(st->regmap, reg, &readval);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
readval &= ~BIT(bit);
|
||||
readval |= (!power_down << bit);
|
||||
|
||||
ret = regmap_write(st->regmap, reg, readval);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
#define _ADF4371_EXT_INFO(_name, _ident) { \
|
||||
.name = _name, \
|
||||
.read = adf4371_read, \
|
||||
.write = adf4371_write, \
|
||||
.private = _ident, \
|
||||
.shared = IIO_SEPARATE, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info adf4371_ext_info[] = {
|
||||
/*
|
||||
* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
|
||||
* values > 2^32 in order to support the entire frequency range
|
||||
* in Hz. Using scale is a bit ugly.
|
||||
*/
|
||||
_ADF4371_EXT_INFO("frequency", ADF4371_FREQ),
|
||||
_ADF4371_EXT_INFO("powerdown", ADF4371_POWER_DOWN),
|
||||
_ADF4371_EXT_INFO("name", ADF4371_CHANNEL_NAME),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define ADF4371_CHANNEL(index) { \
|
||||
.type = IIO_ALTVOLTAGE, \
|
||||
.output = 1, \
|
||||
.channel = index, \
|
||||
.ext_info = adf4371_ext_info, \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adf4371_chan[] = {
|
||||
ADF4371_CHANNEL(ADF4371_CH_RF8),
|
||||
ADF4371_CHANNEL(ADF4371_CH_RFAUX8),
|
||||
ADF4371_CHANNEL(ADF4371_CH_RF16),
|
||||
ADF4371_CHANNEL(ADF4371_CH_RF32),
|
||||
};
|
||||
|
||||
static const struct adf4371_chip_info adf4371_chip_info[] = {
|
||||
[ADF4371] = {
|
||||
.channels = adf4371_chan,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ADF4372] = {
|
||||
.channels = adf4371_chan,
|
||||
.num_channels = 3,
|
||||
}
|
||||
};
|
||||
|
||||
static int adf4371_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct adf4371_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (readval)
|
||||
return regmap_read(st->regmap, reg, readval);
|
||||
else
|
||||
return regmap_write(st->regmap, reg, writeval);
|
||||
}
|
||||
|
||||
static const struct iio_info adf4371_info = {
|
||||
.debugfs_reg_access = &adf4371_reg_access,
|
||||
};
|
||||
|
||||
static int adf4371_setup(struct adf4371_state *st)
|
||||
{
|
||||
unsigned int synth_timeout = 2, timeout = 1, vco_alc_timeout = 1;
|
||||
unsigned int vco_band_div, tmp;
|
||||
int ret;
|
||||
|
||||
/* Perform a software reset */
|
||||
ret = regmap_write(st->regmap, ADF4371_REG(0x0), ADF4371_RESET_CMD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_multi_reg_write(st->regmap, adf4371_reg_defaults,
|
||||
ARRAY_SIZE(adf4371_reg_defaults));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Mute to Lock Detect */
|
||||
if (device_property_read_bool(&st->spi->dev, "adi,mute-till-lock-en")) {
|
||||
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x25),
|
||||
ADF4371_MUTE_LD_MSK,
|
||||
ADF4371_MUTE_LD(1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set address in ascending order, so the bulk_write() will work */
|
||||
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x0),
|
||||
ADF4371_ADDR_ASC_MSK | ADF4371_ADDR_ASC_R_MSK,
|
||||
ADF4371_ADDR_ASC(1) | ADF4371_ADDR_ASC_R(1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Calculate and maximize PFD frequency
|
||||
* fPFD = REFIN × ((1 + D)/(R × (1 + T)))
|
||||
* Where D is the REFIN doubler bit, T is the reference divide by 2,
|
||||
* R is the reference division factor
|
||||
* TODO: it is assumed D and T equal 0.
|
||||
*/
|
||||
do {
|
||||
st->ref_div_factor++;
|
||||
st->fpfd = st->clkin_freq / st->ref_div_factor;
|
||||
} while (st->fpfd > ADF4371_MAX_FREQ_PFD);
|
||||
|
||||
/* Calculate Timeouts */
|
||||
vco_band_div = DIV_ROUND_UP(st->fpfd, 2400000U);
|
||||
|
||||
tmp = DIV_ROUND_CLOSEST(st->fpfd, 1000000U);
|
||||
do {
|
||||
timeout++;
|
||||
if (timeout > 1023) {
|
||||
timeout = 2;
|
||||
synth_timeout++;
|
||||
}
|
||||
} while (synth_timeout * 1024 + timeout <= 20 * tmp);
|
||||
|
||||
do {
|
||||
vco_alc_timeout++;
|
||||
} while (vco_alc_timeout * 1024 - timeout <= 50 * tmp);
|
||||
|
||||
st->buf[0] = vco_band_div;
|
||||
st->buf[1] = timeout & 0xFF;
|
||||
st->buf[2] = ADF4371_TIMEOUT(timeout >> 8) | 0x04;
|
||||
st->buf[3] = synth_timeout;
|
||||
st->buf[4] = ADF4371_VCO_ALC_TOUT(vco_alc_timeout);
|
||||
|
||||
return regmap_bulk_write(st->regmap, ADF4371_REG(0x30), st->buf, 5);
|
||||
}
|
||||
|
||||
static void adf4371_clk_disable(void *data)
|
||||
{
|
||||
struct adf4371_state *st = data;
|
||||
|
||||
clk_disable_unprepare(st->clkin);
|
||||
}
|
||||
|
||||
static int adf4371_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct adf4371_state *st;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &adf4371_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
|
||||
PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
st->regmap = regmap;
|
||||
mutex_init(&st->lock);
|
||||
|
||||
st->chip_info = &adf4371_chip_info[id->driver_data];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &adf4371_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
st->clkin = devm_clk_get(&spi->dev, "clkin");
|
||||
if (IS_ERR(st->clkin))
|
||||
return PTR_ERR(st->clkin);
|
||||
|
||||
ret = clk_prepare_enable(st->clkin);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, adf4371_clk_disable, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->clkin_freq = clk_get_rate(st->clkin);
|
||||
|
||||
ret = adf4371_setup(st);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "ADF4371 setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id adf4371_id_table[] = {
|
||||
{ "adf4371", ADF4371 },
|
||||
{ "adf4372", ADF4372 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adf4371_id_table);
|
||||
|
||||
static const struct of_device_id adf4371_of_match[] = {
|
||||
{ .compatible = "adi,adf4371" },
|
||||
{ .compatible = "adi,adf4372" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adf4371_of_match);
|
||||
|
||||
static struct spi_driver adf4371_driver = {
|
||||
.driver = {
|
||||
.name = "adf4371",
|
||||
.of_match_table = adf4371_of_match,
|
||||
},
|
||||
.probe = adf4371_probe,
|
||||
.id_table = adf4371_id_table,
|
||||
};
|
||||
module_spi_driver(adf4371_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADF4371 SPI PLL");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -22,8 +22,7 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -72,7 +71,7 @@
|
|||
struct dht11 {
|
||||
struct device *dev;
|
||||
|
||||
int gpio;
|
||||
struct gpio_desc *gpiod;
|
||||
int irq;
|
||||
|
||||
struct completion completion;
|
||||
|
@ -179,7 +178,7 @@ static irqreturn_t dht11_handle_irq(int irq, void *data)
|
|||
if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
|
||||
dht11->edges[dht11->num_edges].ts = ktime_get_boottime_ns();
|
||||
dht11->edges[dht11->num_edges++].value =
|
||||
gpio_get_value(dht11->gpio);
|
||||
gpiod_get_value(dht11->gpiod);
|
||||
|
||||
if (dht11->num_edges >= DHT11_EDGES_PER_READ)
|
||||
complete(&dht11->completion);
|
||||
|
@ -217,12 +216,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
|||
reinit_completion(&dht11->completion);
|
||||
|
||||
dht11->num_edges = 0;
|
||||
ret = gpio_direction_output(dht11->gpio, 0);
|
||||
ret = gpiod_direction_output(dht11->gpiod, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
usleep_range(DHT11_START_TRANSMISSION_MIN,
|
||||
DHT11_START_TRANSMISSION_MAX);
|
||||
ret = gpio_direction_input(dht11->gpio);
|
||||
ret = gpiod_direction_input(dht11->gpiod);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -294,10 +293,8 @@ MODULE_DEVICE_TABLE(of, dht11_dt_ids);
|
|||
static int dht11_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct dht11 *dht11;
|
||||
struct iio_dev *iio;
|
||||
int ret;
|
||||
|
||||
iio = devm_iio_device_alloc(dev, sizeof(*dht11));
|
||||
if (!iio) {
|
||||
|
@ -307,18 +304,13 @@ static int dht11_probe(struct platform_device *pdev)
|
|||
|
||||
dht11 = iio_priv(iio);
|
||||
dht11->dev = dev;
|
||||
dht11->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
|
||||
if (IS_ERR(dht11->gpiod))
|
||||
return PTR_ERR(dht11->gpiod);
|
||||
|
||||
ret = of_get_gpio(node, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dht11->gpio = ret;
|
||||
ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dht11->irq = gpio_to_irq(dht11->gpio);
|
||||
dht11->irq = gpiod_to_irq(dht11->gpiod);
|
||||
if (dht11->irq < 0) {
|
||||
dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio);
|
||||
dev_err(dev, "GPIO %d has no interrupt\n", desc_to_gpio(dht11->gpiod));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ struct st_lsm6dsx_ext_dev_settings {
|
|||
* struct st_lsm6dsx_settings - ST IMU sensor settings
|
||||
* @wai: Sensor WhoAmI default value.
|
||||
* @max_fifo_size: Sensor max fifo length in FIFO words.
|
||||
* @id: List of hw id supported by the driver configuration.
|
||||
* @id: List of hw id/device name supported by the driver configuration.
|
||||
* @decimator: List of decimator register info (addr + mask).
|
||||
* @batch: List of FIFO batching register info (addr + mask).
|
||||
* @fifo_ops: Sensor hw FIFO parameters.
|
||||
|
@ -207,7 +207,10 @@ struct st_lsm6dsx_ext_dev_settings {
|
|||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
u16 max_fifo_size;
|
||||
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
|
||||
struct {
|
||||
enum st_lsm6dsx_hw_id hw_id;
|
||||
const char *name;
|
||||
} id[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_fifo_ops fifo_ops;
|
||||
|
@ -303,7 +306,7 @@ struct st_lsm6dsx_hw {
|
|||
static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
|
||||
extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
struct regmap *regmap);
|
||||
int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
|
||||
bool enable);
|
||||
|
|
|
@ -124,7 +124,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.wai = 0x69,
|
||||
.max_fifo_size = 1365,
|
||||
.id = {
|
||||
[0] = ST_LSM6DS3_ID,
|
||||
{
|
||||
.hw_id = ST_LSM6DS3_ID,
|
||||
.name = ST_LSM6DS3_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.decimator = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
|
@ -171,7 +174,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.wai = 0x69,
|
||||
.max_fifo_size = 682,
|
||||
.id = {
|
||||
[0] = ST_LSM6DS3H_ID,
|
||||
{
|
||||
.hw_id = ST_LSM6DS3H_ID,
|
||||
.name = ST_LSM6DS3H_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.decimator = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
|
@ -218,9 +224,16 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.wai = 0x6a,
|
||||
.max_fifo_size = 682,
|
||||
.id = {
|
||||
[0] = ST_LSM6DSL_ID,
|
||||
[1] = ST_LSM6DSM_ID,
|
||||
[2] = ST_ISM330DLC_ID,
|
||||
{
|
||||
.hw_id = ST_LSM6DSL_ID,
|
||||
.name = ST_LSM6DSL_DEV_NAME,
|
||||
}, {
|
||||
.hw_id = ST_LSM6DSM_ID,
|
||||
.name = ST_LSM6DSM_DEV_NAME,
|
||||
}, {
|
||||
.hw_id = ST_ISM330DLC_ID,
|
||||
.name = ST_ISM330DLC_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.decimator = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
|
@ -267,8 +280,13 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.wai = 0x6c,
|
||||
.max_fifo_size = 512,
|
||||
.id = {
|
||||
[0] = ST_LSM6DSO_ID,
|
||||
[1] = ST_LSM6DSOX_ID,
|
||||
{
|
||||
.hw_id = ST_LSM6DSO_ID,
|
||||
.name = ST_LSM6DSO_DEV_NAME,
|
||||
}, {
|
||||
.hw_id = ST_LSM6DSOX_ID,
|
||||
.name = ST_LSM6DSOX_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.batch = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
|
@ -333,7 +351,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.wai = 0x6b,
|
||||
.max_fifo_size = 512,
|
||||
.id = {
|
||||
[0] = ST_ASM330LHH_ID,
|
||||
{
|
||||
.hw_id = ST_ASM330LHH_ID,
|
||||
.name = ST_ASM330LHH_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.batch = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
|
@ -372,7 +393,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.wai = 0x6b,
|
||||
.max_fifo_size = 512,
|
||||
.id = {
|
||||
[0] = ST_LSM6DSR_ID,
|
||||
{
|
||||
.hw_id = ST_LSM6DSR_ID,
|
||||
.name = ST_LSM6DSR_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.batch = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
|
@ -470,13 +494,14 @@ int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
|
||||
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
|
||||
const char **name)
|
||||
{
|
||||
int err, i, j, data;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
|
||||
for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) {
|
||||
if (id == st_lsm6dsx_sensor_settings[i].id[j])
|
||||
if (id == st_lsm6dsx_sensor_settings[i].id[j].hw_id)
|
||||
break;
|
||||
}
|
||||
if (j < ST_LSM6DSX_MAX_ID)
|
||||
|
@ -499,6 +524,7 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
*name = st_lsm6dsx_sensor_settings[i].id[j].name;
|
||||
hw->settings = &st_lsm6dsx_sensor_settings[i];
|
||||
|
||||
return 0;
|
||||
|
@ -1040,11 +1066,12 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
|||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
const char *name = NULL;
|
||||
int i, err;
|
||||
|
||||
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
|
||||
|
@ -1065,7 +1092,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
|||
hw->irq = irq;
|
||||
hw->regmap = regmap;
|
||||
|
||||
err = st_lsm6dsx_check_whoami(hw, hw_id);
|
||||
err = st_lsm6dsx_check_whoami(hw, hw_id, &name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -35,8 +35,7 @@ static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
|
|||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_lsm6dsx_probe(&client->dev, client->irq,
|
||||
hw_id, id->name, regmap);
|
||||
return st_lsm6dsx_probe(&client->dev, client->irq, hw_id, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
|
||||
|
|
|
@ -35,8 +35,7 @@ static int st_lsm6dsx_spi_probe(struct spi_device *spi)
|
|||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_lsm6dsx_probe(&spi->dev, spi->irq,
|
||||
hw_id, id->name, regmap);
|
||||
return st_lsm6dsx_probe(&spi->dev, spi->irq, hw_id, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
|
||||
|
|
|
@ -366,39 +366,25 @@ static void iio_device_unregister_debugfs(struct iio_dev *indio_dev)
|
|||
debugfs_remove_recursive(indio_dev->debugfs_dentry);
|
||||
}
|
||||
|
||||
static int iio_device_register_debugfs(struct iio_dev *indio_dev)
|
||||
static void iio_device_register_debugfs(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
if (indio_dev->info->debugfs_reg_access == NULL)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (!iio_debugfs_dentry)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
indio_dev->debugfs_dentry =
|
||||
debugfs_create_dir(dev_name(&indio_dev->dev),
|
||||
iio_debugfs_dentry);
|
||||
if (indio_dev->debugfs_dentry == NULL) {
|
||||
dev_warn(indio_dev->dev.parent,
|
||||
"Failed to create debugfs directory\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
d = debugfs_create_file("direct_reg_access", 0644,
|
||||
indio_dev->debugfs_dentry,
|
||||
indio_dev, &iio_debugfs_reg_fops);
|
||||
if (!d) {
|
||||
iio_device_unregister_debugfs(indio_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
debugfs_create_file("direct_reg_access", 0644,
|
||||
indio_dev->debugfs_dentry, indio_dev,
|
||||
&iio_debugfs_reg_fops);
|
||||
}
|
||||
#else
|
||||
static int iio_device_register_debugfs(struct iio_dev *indio_dev)
|
||||
static void iio_device_register_debugfs(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iio_device_unregister_debugfs(struct iio_dev *indio_dev)
|
||||
|
@ -1104,6 +1090,8 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
|
|||
char *avail_postfix;
|
||||
|
||||
for_each_set_bit(i, infomask, sizeof(*infomask) * 8) {
|
||||
if (i >= ARRAY_SIZE(iio_chan_info_postfix))
|
||||
return -EINVAL;
|
||||
avail_postfix = kasprintf(GFP_KERNEL,
|
||||
"%s_available",
|
||||
iio_chan_info_postfix[i]);
|
||||
|
@ -1669,12 +1657,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
|
|||
/* configure elements for the chrdev */
|
||||
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
|
||||
|
||||
ret = iio_device_register_debugfs(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Failed to register debugfs interfaces\n");
|
||||
return ret;
|
||||
}
|
||||
iio_device_register_debugfs(indio_dev);
|
||||
|
||||
ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
|
||||
if (ret) {
|
||||
|
|
|
@ -146,7 +146,7 @@ static int bh1780_probe(struct i2c_client *client,
|
|||
{
|
||||
int ret;
|
||||
struct bh1780_data *bh1780;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#define STK3310_CHIP_ID_VAL 0x13
|
||||
#define STK3311_CHIP_ID_VAL 0x1D
|
||||
#define STK3335_CHIP_ID_VAL 0x51
|
||||
#define STK3310_PSINT_EN 0x01
|
||||
#define STK3310_PS_MAX_VAL 0xFFFF
|
||||
|
||||
|
@ -451,7 +452,8 @@ static int stk3310_init(struct iio_dev *indio_dev)
|
|||
return ret;
|
||||
|
||||
if (chipid != STK3310_CHIP_ID_VAL &&
|
||||
chipid != STK3311_CHIP_ID_VAL) {
|
||||
chipid != STK3311_CHIP_ID_VAL &&
|
||||
chipid != STK3335_CHIP_ID_VAL) {
|
||||
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -663,6 +665,7 @@ static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume);
|
|||
static const struct i2c_device_id stk3310_i2c_id[] = {
|
||||
{"STK3310", 0},
|
||||
{"STK3311", 0},
|
||||
{"STK3335", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
|
||||
|
@ -670,6 +673,7 @@ MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
|
|||
static const struct acpi_device_id stk3310_acpi_id[] = {
|
||||
{"STK3310", 0},
|
||||
{"STK3311", 0},
|
||||
{"STK3335", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -53,6 +53,17 @@ config IIO_CROS_EC_BARO
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called cros_ec_baro.
|
||||
|
||||
config DPS310
|
||||
tristate "Infineon DPS310 pressure and temperature sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Support for the Infineon DPS310 digital barometric pressure sensor.
|
||||
It can be accessed over I2C bus.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called dps310.
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_BMP280) += bmp280.o
|
|||
bmp280-objs := bmp280-core.o bmp280-regmap.o
|
||||
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
|
||||
obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
|
||||
obj-$(CONFIG_DPS310) += dps310.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_HP03) += hp03.o
|
||||
|
|
|
@ -0,0 +1,827 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// Copyright IBM Corp 2019
|
||||
/*
|
||||
* The DPS310 is a barometric pressure and temperature sensor.
|
||||
* Currently only reading a single temperature is supported by
|
||||
* this driver.
|
||||
*
|
||||
* https://www.infineon.com/dgdl/?fileId=5546d462576f34750157750826c42242
|
||||
*
|
||||
* Temperature calculation:
|
||||
* c0 * 0.5 + c1 * T_raw / kT °C
|
||||
*
|
||||
* TODO:
|
||||
* - Optionally support the FIFO
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define DPS310_DEV_NAME "dps310"
|
||||
|
||||
#define DPS310_PRS_B0 0x00
|
||||
#define DPS310_PRS_B1 0x01
|
||||
#define DPS310_PRS_B2 0x02
|
||||
#define DPS310_TMP_B0 0x03
|
||||
#define DPS310_TMP_B1 0x04
|
||||
#define DPS310_TMP_B2 0x05
|
||||
#define DPS310_PRS_CFG 0x06
|
||||
#define DPS310_PRS_RATE_BITS GENMASK(6, 4)
|
||||
#define DPS310_PRS_PRC_BITS GENMASK(3, 0)
|
||||
#define DPS310_TMP_CFG 0x07
|
||||
#define DPS310_TMP_RATE_BITS GENMASK(6, 4)
|
||||
#define DPS310_TMP_PRC_BITS GENMASK(3, 0)
|
||||
#define DPS310_TMP_EXT BIT(7)
|
||||
#define DPS310_MEAS_CFG 0x08
|
||||
#define DPS310_MEAS_CTRL_BITS GENMASK(2, 0)
|
||||
#define DPS310_PRS_EN BIT(0)
|
||||
#define DPS310_TEMP_EN BIT(1)
|
||||
#define DPS310_BACKGROUND BIT(2)
|
||||
#define DPS310_PRS_RDY BIT(4)
|
||||
#define DPS310_TMP_RDY BIT(5)
|
||||
#define DPS310_SENSOR_RDY BIT(6)
|
||||
#define DPS310_COEF_RDY BIT(7)
|
||||
#define DPS310_CFG_REG 0x09
|
||||
#define DPS310_INT_HL BIT(7)
|
||||
#define DPS310_TMP_SHIFT_EN BIT(3)
|
||||
#define DPS310_PRS_SHIFT_EN BIT(4)
|
||||
#define DPS310_FIFO_EN BIT(5)
|
||||
#define DPS310_SPI_EN BIT(6)
|
||||
#define DPS310_RESET 0x0c
|
||||
#define DPS310_RESET_MAGIC 0x09
|
||||
#define DPS310_COEF_BASE 0x10
|
||||
|
||||
/* Make sure sleep time is <= 20ms for usleep_range */
|
||||
#define DPS310_POLL_SLEEP_US(t) min(20000, (t) / 8)
|
||||
/* Silently handle error in rate value here */
|
||||
#define DPS310_POLL_TIMEOUT_US(rc) ((rc) <= 0 ? 1000000 : 1000000 / (rc))
|
||||
|
||||
#define DPS310_PRS_BASE DPS310_PRS_B0
|
||||
#define DPS310_TMP_BASE DPS310_TMP_B0
|
||||
|
||||
/*
|
||||
* These values (defined in the spec) indicate how to scale the raw register
|
||||
* values for each level of precision available.
|
||||
*/
|
||||
static const int scale_factors[] = {
|
||||
524288,
|
||||
1572864,
|
||||
3670016,
|
||||
7864320,
|
||||
253952,
|
||||
516096,
|
||||
1040384,
|
||||
2088960,
|
||||
};
|
||||
|
||||
struct dps310_data {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct mutex lock; /* Lock for sequential HW access functions */
|
||||
|
||||
s32 c0, c1;
|
||||
s32 c00, c10, c20, c30, c01, c11, c21;
|
||||
s32 pressure_raw;
|
||||
s32 temp_raw;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec dps310_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
};
|
||||
|
||||
/* To be called after checking the COEF_RDY bit in MEAS_CFG */
|
||||
static int dps310_get_coefs(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
u8 coef[18];
|
||||
u32 c0, c1;
|
||||
u32 c00, c10, c20, c30, c01, c11, c21;
|
||||
|
||||
/* Read all sensor calibration coefficients from the COEF registers. */
|
||||
rc = regmap_bulk_read(data->regmap, DPS310_COEF_BASE, coef,
|
||||
sizeof(coef));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Calculate temperature calibration coefficients c0 and c1. The
|
||||
* numbers are 12-bit 2's complement numbers.
|
||||
*/
|
||||
c0 = (coef[0] << 4) | (coef[1] >> 4);
|
||||
data->c0 = sign_extend32(c0, 11);
|
||||
|
||||
c1 = ((coef[1] & GENMASK(3, 0)) << 8) | coef[2];
|
||||
data->c1 = sign_extend32(c1, 11);
|
||||
|
||||
/*
|
||||
* Calculate pressure calibration coefficients. c00 and c10 are 20 bit
|
||||
* 2's complement numbers, while the rest are 16 bit 2's complement
|
||||
* numbers.
|
||||
*/
|
||||
c00 = (coef[3] << 12) | (coef[4] << 4) | (coef[5] >> 4);
|
||||
data->c00 = sign_extend32(c00, 19);
|
||||
|
||||
c10 = ((coef[5] & GENMASK(3, 0)) << 16) | (coef[6] << 8) | coef[7];
|
||||
data->c10 = sign_extend32(c10, 19);
|
||||
|
||||
c01 = (coef[8] << 8) | coef[9];
|
||||
data->c01 = sign_extend32(c01, 15);
|
||||
|
||||
c11 = (coef[10] << 8) | coef[11];
|
||||
data->c11 = sign_extend32(c11, 15);
|
||||
|
||||
c20 = (coef[12] << 8) | coef[13];
|
||||
data->c20 = sign_extend32(c20, 15);
|
||||
|
||||
c21 = (coef[14] << 8) | coef[15];
|
||||
data->c21 = sign_extend32(c21, 15);
|
||||
|
||||
c30 = (coef[16] << 8) | coef[17];
|
||||
data->c30 = sign_extend32(c30, 15);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_get_pres_precision(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return BIT(val & GENMASK(2, 0));
|
||||
}
|
||||
|
||||
static int dps310_get_temp_precision(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Scale factor is bottom 4 bits of the register, but 1111 is
|
||||
* reserved so just grab bottom three
|
||||
*/
|
||||
return BIT(val & GENMASK(2, 0));
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
static int dps310_set_pres_precision(struct dps310_data *data, int val)
|
||||
{
|
||||
int rc;
|
||||
u8 shift_en;
|
||||
|
||||
if (val < 0 || val > 128)
|
||||
return -EINVAL;
|
||||
|
||||
shift_en = val >= 16 ? DPS310_PRS_SHIFT_EN : 0;
|
||||
rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
|
||||
DPS310_PRS_SHIFT_EN, shift_en);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return regmap_update_bits(data->regmap, DPS310_PRS_CFG,
|
||||
DPS310_PRS_PRC_BITS, ilog2(val));
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
static int dps310_set_temp_precision(struct dps310_data *data, int val)
|
||||
{
|
||||
int rc;
|
||||
u8 shift_en;
|
||||
|
||||
if (val < 0 || val > 128)
|
||||
return -EINVAL;
|
||||
|
||||
shift_en = val >= 16 ? DPS310_TMP_SHIFT_EN : 0;
|
||||
rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
|
||||
DPS310_TMP_SHIFT_EN, shift_en);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return regmap_update_bits(data->regmap, DPS310_TMP_CFG,
|
||||
DPS310_TMP_PRC_BITS, ilog2(val));
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
static int dps310_set_pres_samp_freq(struct dps310_data *data, int freq)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (freq < 0 || freq > 128)
|
||||
return -EINVAL;
|
||||
|
||||
val = ilog2(freq) << 4;
|
||||
|
||||
return regmap_update_bits(data->regmap, DPS310_PRS_CFG,
|
||||
DPS310_PRS_RATE_BITS, val);
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (freq < 0 || freq > 128)
|
||||
return -EINVAL;
|
||||
|
||||
val = ilog2(freq) << 4;
|
||||
|
||||
return regmap_update_bits(data->regmap, DPS310_TMP_CFG,
|
||||
DPS310_TMP_RATE_BITS, val);
|
||||
}
|
||||
|
||||
static int dps310_get_pres_samp_freq(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return BIT((val & DPS310_PRS_RATE_BITS) >> 4);
|
||||
}
|
||||
|
||||
static int dps310_get_temp_samp_freq(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return BIT((val & DPS310_TMP_RATE_BITS) >> 4);
|
||||
}
|
||||
|
||||
static int dps310_get_pres_k(struct dps310_data *data)
|
||||
{
|
||||
int rc = dps310_get_pres_precision(data);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return scale_factors[ilog2(rc)];
|
||||
}
|
||||
|
||||
static int dps310_get_temp_k(struct dps310_data *data)
|
||||
{
|
||||
int rc = dps310_get_temp_precision(data);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return scale_factors[ilog2(rc)];
|
||||
}
|
||||
|
||||
static int dps310_read_pres_raw(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int rate;
|
||||
int ready;
|
||||
int timeout;
|
||||
s32 raw;
|
||||
u8 val[3];
|
||||
|
||||
if (mutex_lock_interruptible(&data->lock))
|
||||
return -EINTR;
|
||||
|
||||
rate = dps310_get_pres_samp_freq(data);
|
||||
timeout = DPS310_POLL_TIMEOUT_US(rate);
|
||||
|
||||
/* Poll for sensor readiness; base the timeout upon the sample rate. */
|
||||
rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
|
||||
ready & DPS310_PRS_RDY,
|
||||
DPS310_POLL_SLEEP_US(timeout), timeout);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
rc = regmap_bulk_read(data->regmap, DPS310_PRS_BASE, val, sizeof(val));
|
||||
if (rc < 0)
|
||||
goto done;
|
||||
|
||||
raw = (val[0] << 16) | (val[1] << 8) | val[2];
|
||||
data->pressure_raw = sign_extend32(raw, 23);
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
static int dps310_read_temp_ready(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
u8 val[3];
|
||||
s32 raw;
|
||||
|
||||
rc = regmap_bulk_read(data->regmap, DPS310_TMP_BASE, val, sizeof(val));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
raw = (val[0] << 16) | (val[1] << 8) | val[2];
|
||||
data->temp_raw = sign_extend32(raw, 23);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_read_temp_raw(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int rate;
|
||||
int ready;
|
||||
int timeout;
|
||||
|
||||
if (mutex_lock_interruptible(&data->lock))
|
||||
return -EINTR;
|
||||
|
||||
rate = dps310_get_temp_samp_freq(data);
|
||||
timeout = DPS310_POLL_TIMEOUT_US(rate);
|
||||
|
||||
/* Poll for sensor readiness; base the timeout upon the sample rate. */
|
||||
rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
|
||||
ready & DPS310_TMP_RDY,
|
||||
DPS310_POLL_SLEEP_US(timeout), timeout);
|
||||
if (rc < 0)
|
||||
goto done;
|
||||
|
||||
rc = dps310_read_temp_ready(data);
|
||||
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case DPS310_PRS_CFG:
|
||||
case DPS310_TMP_CFG:
|
||||
case DPS310_MEAS_CFG:
|
||||
case DPS310_CFG_REG:
|
||||
case DPS310_RESET:
|
||||
/* No documentation available on the registers below */
|
||||
case 0x0e:
|
||||
case 0x0f:
|
||||
case 0x62:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool dps310_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case DPS310_PRS_B0:
|
||||
case DPS310_PRS_B1:
|
||||
case DPS310_PRS_B2:
|
||||
case DPS310_TMP_B0:
|
||||
case DPS310_TMP_B1:
|
||||
case DPS310_TMP_B2:
|
||||
case DPS310_MEAS_CFG:
|
||||
case 0x32: /* No documentation available on this register */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int dps310_write_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
int rc;
|
||||
struct dps310_data *data = iio_priv(iio);
|
||||
|
||||
if (mutex_lock_interruptible(&data->lock))
|
||||
return -EINTR;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
rc = dps310_set_pres_samp_freq(data, val);
|
||||
break;
|
||||
|
||||
case IIO_TEMP:
|
||||
rc = dps310_set_temp_samp_freq(data, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
rc = dps310_set_pres_precision(data, val);
|
||||
break;
|
||||
|
||||
case IIO_TEMP:
|
||||
rc = dps310_set_temp_precision(data, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dps310_calculate_pressure(struct dps310_data *data)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
int t_ready;
|
||||
int kpi = dps310_get_pres_k(data);
|
||||
int kti = dps310_get_temp_k(data);
|
||||
s64 rem = 0ULL;
|
||||
s64 pressure = 0ULL;
|
||||
s64 p;
|
||||
s64 t;
|
||||
s64 denoms[7];
|
||||
s64 nums[7];
|
||||
s64 rems[7];
|
||||
s64 kp;
|
||||
s64 kt;
|
||||
|
||||
if (kpi < 0)
|
||||
return kpi;
|
||||
|
||||
if (kti < 0)
|
||||
return kti;
|
||||
|
||||
kp = (s64)kpi;
|
||||
kt = (s64)kti;
|
||||
|
||||
/* Refresh temp if it's ready, otherwise just use the latest value */
|
||||
if (mutex_trylock(&data->lock)) {
|
||||
rc = regmap_read(data->regmap, DPS310_MEAS_CFG, &t_ready);
|
||||
if (rc >= 0 && t_ready & DPS310_TMP_RDY)
|
||||
dps310_read_temp_ready(data);
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
p = (s64)data->pressure_raw;
|
||||
t = (s64)data->temp_raw;
|
||||
|
||||
/* Section 4.9.1 of the DPS310 spec; algebra'd to avoid underflow */
|
||||
nums[0] = (s64)data->c00;
|
||||
denoms[0] = 1LL;
|
||||
nums[1] = p * (s64)data->c10;
|
||||
denoms[1] = kp;
|
||||
nums[2] = p * p * (s64)data->c20;
|
||||
denoms[2] = kp * kp;
|
||||
nums[3] = p * p * p * (s64)data->c30;
|
||||
denoms[3] = kp * kp * kp;
|
||||
nums[4] = t * (s64)data->c01;
|
||||
denoms[4] = kt;
|
||||
nums[5] = t * p * (s64)data->c11;
|
||||
denoms[5] = kp * kt;
|
||||
nums[6] = t * p * p * (s64)data->c21;
|
||||
denoms[6] = kp * kp * kt;
|
||||
|
||||
/* Kernel lacks a div64_s64_rem function; denoms are all positive */
|
||||
for (i = 0; i < 7; ++i) {
|
||||
u64 irem;
|
||||
|
||||
if (nums[i] < 0LL) {
|
||||
pressure -= div64_u64_rem(-nums[i], denoms[i], &irem);
|
||||
rems[i] = -irem;
|
||||
} else {
|
||||
pressure += div64_u64_rem(nums[i], denoms[i], &irem);
|
||||
rems[i] = (s64)irem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increase precision and calculate the remainder sum */
|
||||
for (i = 0; i < 7; ++i)
|
||||
rem += div64_s64((s64)rems[i] * 1000000000LL, denoms[i]);
|
||||
|
||||
pressure += div_s64(rem, 1000000000LL);
|
||||
if (pressure < 0LL)
|
||||
return -ERANGE;
|
||||
|
||||
return (int)min_t(s64, pressure, INT_MAX);
|
||||
}
|
||||
|
||||
static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rc = dps310_get_pres_samp_freq(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
rc = dps310_read_pres_raw(data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dps310_calculate_pressure(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
*val2 = 1000; /* Convert Pa to KPa per IIO ABI */
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
rc = dps310_get_pres_precision(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dps310_calculate_temp(struct dps310_data *data)
|
||||
{
|
||||
s64 c0;
|
||||
s64 t;
|
||||
int kt = dps310_get_temp_k(data);
|
||||
|
||||
if (kt < 0)
|
||||
return kt;
|
||||
|
||||
/* Obtain inverse-scaled offset */
|
||||
c0 = div_s64((s64)kt * (s64)data->c0, 2);
|
||||
|
||||
/* Add the offset to the unscaled temperature */
|
||||
t = c0 + ((s64)data->temp_raw * (s64)data->c1);
|
||||
|
||||
/* Convert to milliCelsius and scale the temperature */
|
||||
return (int)div_s64(t * 1000LL, kt);
|
||||
}
|
||||
|
||||
static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rc = dps310_get_temp_samp_freq(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
rc = dps310_read_temp_raw(data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dps310_calculate_temp(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
rc = dps310_get_temp_precision(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dps310_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dps310_data *data = iio_priv(iio);
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
return dps310_read_pressure(data, val, val2, mask);
|
||||
|
||||
case IIO_TEMP:
|
||||
return dps310_read_temp(data, val, val2, mask);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void dps310_reset(void *action_data)
|
||||
{
|
||||
struct dps310_data *data = action_data;
|
||||
|
||||
regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC);
|
||||
}
|
||||
|
||||
static const struct regmap_config dps310_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.writeable_reg = dps310_is_writeable_reg,
|
||||
.volatile_reg = dps310_is_volatile_reg,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = 0x62, /* No documentation available on this register */
|
||||
};
|
||||
|
||||
static const struct iio_info dps310_info = {
|
||||
.read_raw = dps310_read_raw,
|
||||
.write_raw = dps310_write_raw,
|
||||
};
|
||||
|
||||
/*
|
||||
* Some verions of chip will read temperatures in the ~60C range when
|
||||
* its actually ~20C. This is the manufacturer recommended workaround
|
||||
* to correct the issue. The registers used below are undocumented.
|
||||
*/
|
||||
static int dps310_temp_workaround(struct dps310_data *data)
|
||||
{
|
||||
int rc;
|
||||
int reg;
|
||||
|
||||
rc = regmap_read(data->regmap, 0x32, ®);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* If bit 1 is set then the device is okay, and the workaround does not
|
||||
* need to be applied
|
||||
*/
|
||||
if (reg & BIT(1))
|
||||
return 0;
|
||||
|
||||
rc = regmap_write(data->regmap, 0x0e, 0xA5);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = regmap_write(data->regmap, 0x0f, 0x96);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = regmap_write(data->regmap, 0x62, 0x02);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = regmap_write(data->regmap, 0x0e, 0x00);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return regmap_write(data->regmap, 0x0f, 0x00);
|
||||
}
|
||||
|
||||
static int dps310_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct dps310_data *data;
|
||||
struct iio_dev *iio;
|
||||
int rc, ready;
|
||||
|
||||
iio = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!iio)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(iio);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
iio->dev.parent = &client->dev;
|
||||
iio->name = id->name;
|
||||
iio->channels = dps310_channels;
|
||||
iio->num_channels = ARRAY_SIZE(dps310_channels);
|
||||
iio->info = &dps310_info;
|
||||
iio->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &dps310_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return PTR_ERR(data->regmap);
|
||||
|
||||
/* Register to run the device reset when the device is removed */
|
||||
rc = devm_add_action_or_reset(&client->dev, dps310_reset, data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Set up pressure sensor in single sample, one measurement per second
|
||||
* mode
|
||||
*/
|
||||
rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0);
|
||||
|
||||
/*
|
||||
* Set up external (MEMS) temperature sensor in single sample, one
|
||||
* measurement per second mode
|
||||
*/
|
||||
rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Temp and pressure shifts are disabled when PRC <= 8 */
|
||||
rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
|
||||
DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* MEAS_CFG doesn't update correctly unless first written with 0 */
|
||||
rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
|
||||
DPS310_MEAS_CTRL_BITS, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Turn on temperature and pressure measurement in the background */
|
||||
rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
|
||||
DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN |
|
||||
DPS310_TEMP_EN | DPS310_BACKGROUND);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Calibration coefficients required for reporting temperature.
|
||||
* They are available 40ms after the device has started
|
||||
*/
|
||||
rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
|
||||
ready & DPS310_COEF_RDY, 10000, 40000);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = dps310_get_coefs(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = dps310_temp_workaround(data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = devm_iio_device_register(&client->dev, iio);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
i2c_set_clientdata(client, iio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dps310_id[] = {
|
||||
{ DPS310_DEV_NAME, 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dps310_id);
|
||||
|
||||
static struct i2c_driver dps310_driver = {
|
||||
.driver = {
|
||||
.name = DPS310_DEV_NAME,
|
||||
},
|
||||
.probe = dps310_probe,
|
||||
.id_table = dps310_id,
|
||||
};
|
||||
module_i2c_driver(dps310_driver);
|
||||
|
||||
MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
|
||||
MODULE_DESCRIPTION("Infineon DPS310 pressure and temperature sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -10,6 +10,8 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
@ -262,9 +264,17 @@ static const struct spi_device_id maxim_thermocouple_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
|
||||
|
||||
static const struct of_device_id maxim_thermocouple_of_match[] = {
|
||||
{ .compatible = "maxim,max6675" },
|
||||
{ .compatible = "maxim,max31855" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, maxim_thermocouple_of_match);
|
||||
|
||||
static struct spi_driver maxim_thermocouple_driver = {
|
||||
.driver = {
|
||||
.name = MAXIM_THERMOCOUPLE_DRV_NAME,
|
||||
.of_match_table = maxim_thermocouple_of_match,
|
||||
},
|
||||
.probe = maxim_thermocouple_probe,
|
||||
.remove = maxim_thermocouple_remove,
|
||||
|
|
|
@ -18,24 +18,6 @@ config ION_SYSTEM_HEAP
|
|||
Choose this option to enable the Ion system heap. The system heap
|
||||
is backed by pages from the buddy allocator. If in doubt, say Y.
|
||||
|
||||
config ION_CARVEOUT_HEAP
|
||||
bool "Ion carveout heap support"
|
||||
depends on ION
|
||||
help
|
||||
Choose this option to enable carveout heaps with Ion. Carveout heaps
|
||||
are backed by memory reserved from the system. Allocation times are
|
||||
typically faster at the cost of memory not being used. Unless you
|
||||
know your system has these regions, you should say N here.
|
||||
|
||||
config ION_CHUNK_HEAP
|
||||
bool "Ion chunk heap support"
|
||||
depends on ION
|
||||
help
|
||||
Choose this option to enable chunk heaps with Ion. This heap is
|
||||
similar in function the carveout heap but memory is broken down
|
||||
into smaller chunk sizes, typically corresponding to a TLB size.
|
||||
Unless you know your system has these regions, you should say N here.
|
||||
|
||||
config ION_CMA_HEAP
|
||||
bool "Ion CMA heap support"
|
||||
depends on ION && DMA_CMA
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_ION) += ion.o ion_heap.o
|
||||
obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_system_heap.o ion_page_pool.o
|
||||
obj-$(CONFIG_ION_CARVEOUT_HEAP) += ion_carveout_heap.o
|
||||
obj-$(CONFIG_ION_CHUNK_HEAP) += ion_chunk_heap.o
|
||||
obj-$(CONFIG_ION_CMA_HEAP) += ion_cma_heap.o
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ION Memory Allocator carveout heap helper
|
||||
*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ion.h"
|
||||
|
||||
#define ION_CARVEOUT_ALLOCATE_FAIL -1
|
||||
|
||||
struct ion_carveout_heap {
|
||||
struct ion_heap heap;
|
||||
struct gen_pool *pool;
|
||||
};
|
||||
|
||||
static phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
|
||||
unsigned long size)
|
||||
{
|
||||
struct ion_carveout_heap *carveout_heap =
|
||||
container_of(heap, struct ion_carveout_heap, heap);
|
||||
unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);
|
||||
|
||||
if (!offset)
|
||||
return ION_CARVEOUT_ALLOCATE_FAIL;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static void ion_carveout_free(struct ion_heap *heap, phys_addr_t addr,
|
||||
unsigned long size)
|
||||
{
|
||||
struct ion_carveout_heap *carveout_heap =
|
||||
container_of(heap, struct ion_carveout_heap, heap);
|
||||
|
||||
if (addr == ION_CARVEOUT_ALLOCATE_FAIL)
|
||||
return;
|
||||
|
||||
gen_pool_free(carveout_heap->pool, addr, size);
|
||||
}
|
||||
|
||||
static int ion_carveout_heap_allocate(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct sg_table *table;
|
||||
phys_addr_t paddr;
|
||||
int ret;
|
||||
|
||||
table = kmalloc(sizeof(*table), GFP_KERNEL);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
ret = sg_alloc_table(table, 1, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
paddr = ion_carveout_allocate(heap, size);
|
||||
if (paddr == ION_CARVEOUT_ALLOCATE_FAIL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_table;
|
||||
}
|
||||
|
||||
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
|
||||
buffer->sg_table = table;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_table:
|
||||
sg_free_table(table);
|
||||
err_free:
|
||||
kfree(table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ion_carveout_heap_free(struct ion_buffer *buffer)
|
||||
{
|
||||
struct ion_heap *heap = buffer->heap;
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
struct page *page = sg_page(table->sgl);
|
||||
phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
|
||||
|
||||
ion_heap_buffer_zero(buffer);
|
||||
|
||||
ion_carveout_free(heap, paddr, buffer->size);
|
||||
sg_free_table(table);
|
||||
kfree(table);
|
||||
}
|
||||
|
||||
static struct ion_heap_ops carveout_heap_ops = {
|
||||
.allocate = ion_carveout_heap_allocate,
|
||||
.free = ion_carveout_heap_free,
|
||||
.map_user = ion_heap_map_user,
|
||||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
};
|
||||
|
||||
struct ion_heap *ion_carveout_heap_create(phys_addr_t base, size_t size)
|
||||
{
|
||||
struct ion_carveout_heap *carveout_heap;
|
||||
int ret;
|
||||
|
||||
struct page *page;
|
||||
|
||||
page = pfn_to_page(PFN_DOWN(base));
|
||||
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL);
|
||||
if (!carveout_heap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1);
|
||||
if (!carveout_heap->pool) {
|
||||
kfree(carveout_heap);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
gen_pool_add(carveout_heap->pool, base, size, -1);
|
||||
carveout_heap->heap.ops = &carveout_heap_ops;
|
||||
carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
|
||||
carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
|
||||
|
||||
return &carveout_heap->heap;
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ION memory allocator chunk heap helper
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ion.h"
|
||||
|
||||
struct ion_chunk_heap {
|
||||
struct ion_heap heap;
|
||||
struct gen_pool *pool;
|
||||
unsigned long chunk_size;
|
||||
unsigned long size;
|
||||
unsigned long allocated;
|
||||
};
|
||||
|
||||
static int ion_chunk_heap_allocate(struct ion_heap *heap,
|
||||
struct ion_buffer *buffer,
|
||||
unsigned long size,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct ion_chunk_heap *chunk_heap =
|
||||
container_of(heap, struct ion_chunk_heap, heap);
|
||||
struct sg_table *table;
|
||||
struct scatterlist *sg;
|
||||
int ret, i;
|
||||
unsigned long num_chunks;
|
||||
unsigned long allocated_size;
|
||||
|
||||
allocated_size = ALIGN(size, chunk_heap->chunk_size);
|
||||
num_chunks = allocated_size / chunk_heap->chunk_size;
|
||||
|
||||
if (allocated_size > chunk_heap->size - chunk_heap->allocated)
|
||||
return -ENOMEM;
|
||||
|
||||
table = kmalloc(sizeof(*table), GFP_KERNEL);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
ret = sg_alloc_table(table, num_chunks, GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sg = table->sgl;
|
||||
for (i = 0; i < num_chunks; i++) {
|
||||
unsigned long paddr = gen_pool_alloc(chunk_heap->pool,
|
||||
chunk_heap->chunk_size);
|
||||
if (!paddr)
|
||||
goto err;
|
||||
sg_set_page(sg, pfn_to_page(PFN_DOWN(paddr)),
|
||||
chunk_heap->chunk_size, 0);
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
|
||||
buffer->sg_table = table;
|
||||
chunk_heap->allocated += allocated_size;
|
||||
return 0;
|
||||
err:
|
||||
sg = table->sgl;
|
||||
for (i -= 1; i >= 0; i--) {
|
||||
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
|
||||
sg->length);
|
||||
sg = sg_next(sg);
|
||||
}
|
||||
sg_free_table(table);
|
||||
kfree(table);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void ion_chunk_heap_free(struct ion_buffer *buffer)
|
||||
{
|
||||
struct ion_heap *heap = buffer->heap;
|
||||
struct ion_chunk_heap *chunk_heap =
|
||||
container_of(heap, struct ion_chunk_heap, heap);
|
||||
struct sg_table *table = buffer->sg_table;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
unsigned long allocated_size;
|
||||
|
||||
allocated_size = ALIGN(buffer->size, chunk_heap->chunk_size);
|
||||
|
||||
ion_heap_buffer_zero(buffer);
|
||||
|
||||
for_each_sg(table->sgl, sg, table->nents, i) {
|
||||
gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
|
||||
sg->length);
|
||||
}
|
||||
chunk_heap->allocated -= allocated_size;
|
||||
sg_free_table(table);
|
||||
kfree(table);
|
||||
}
|
||||
|
||||
static struct ion_heap_ops chunk_heap_ops = {
|
||||
.allocate = ion_chunk_heap_allocate,
|
||||
.free = ion_chunk_heap_free,
|
||||
.map_user = ion_heap_map_user,
|
||||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
};
|
||||
|
||||
struct ion_heap *ion_chunk_heap_create(phys_addr_t base, size_t size, size_t chunk_size)
|
||||
{
|
||||
struct ion_chunk_heap *chunk_heap;
|
||||
int ret;
|
||||
struct page *page;
|
||||
|
||||
page = pfn_to_page(PFN_DOWN(base));
|
||||
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
chunk_heap = kzalloc(sizeof(*chunk_heap), GFP_KERNEL);
|
||||
if (!chunk_heap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
chunk_heap->chunk_size = chunk_size;
|
||||
chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) +
|
||||
PAGE_SHIFT, -1);
|
||||
if (!chunk_heap->pool) {
|
||||
ret = -ENOMEM;
|
||||
goto error_gen_pool_create;
|
||||
}
|
||||
chunk_heap->size = size;
|
||||
chunk_heap->allocated = 0;
|
||||
|
||||
gen_pool_add(chunk_heap->pool, base, size, -1);
|
||||
chunk_heap->heap.ops = &chunk_heap_ops;
|
||||
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
|
||||
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
|
||||
pr_debug("%s: base %pa size %zu\n", __func__, &base, size);
|
||||
|
||||
return &chunk_heap->heap;
|
||||
|
||||
error_gen_pool_create:
|
||||
kfree(chunk_heap);
|
||||
return ERR_PTR(ret);
|
||||
}
|
|
@ -27,18 +27,19 @@ static void comedi_buf_map_kref_release(struct kref *kref)
|
|||
unsigned int i;
|
||||
|
||||
if (bm->page_list) {
|
||||
for (i = 0; i < bm->n_pages; i++) {
|
||||
buf = &bm->page_list[i];
|
||||
clear_bit(PG_reserved,
|
||||
&(virt_to_page(buf->virt_addr)->flags));
|
||||
if (bm->dma_dir != DMA_NONE) {
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
dma_free_coherent(bm->dma_hw_dev,
|
||||
PAGE_SIZE,
|
||||
buf->virt_addr,
|
||||
buf->dma_addr);
|
||||
#endif
|
||||
} else {
|
||||
if (bm->dma_dir != DMA_NONE) {
|
||||
/*
|
||||
* DMA buffer was allocated as a single block.
|
||||
* Address is in page_list[0].
|
||||
*/
|
||||
buf = &bm->page_list[0];
|
||||
dma_free_coherent(bm->dma_hw_dev,
|
||||
PAGE_SIZE * bm->n_pages,
|
||||
buf->virt_addr, buf->dma_addr);
|
||||
} else {
|
||||
for (i = 0; i < bm->n_pages; i++) {
|
||||
buf = &bm->page_list[i];
|
||||
ClearPageReserved(virt_to_page(buf->virt_addr));
|
||||
free_page((unsigned long)buf->virt_addr);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +58,8 @@ static void __comedi_buf_free(struct comedi_device *dev,
|
|||
unsigned long flags;
|
||||
|
||||
if (async->prealloc_buf) {
|
||||
vunmap(async->prealloc_buf);
|
||||
if (s->async_dma_dir == DMA_NONE)
|
||||
vunmap(async->prealloc_buf);
|
||||
async->prealloc_buf = NULL;
|
||||
async->prealloc_bufsz = 0;
|
||||
}
|
||||
|
@ -69,6 +71,72 @@ static void __comedi_buf_free(struct comedi_device *dev,
|
|||
comedi_buf_map_put(bm);
|
||||
}
|
||||
|
||||
static struct comedi_buf_map *
|
||||
comedi_buf_map_alloc(struct comedi_device *dev, enum dma_data_direction dma_dir,
|
||||
unsigned int n_pages)
|
||||
{
|
||||
struct comedi_buf_map *bm;
|
||||
struct comedi_buf_page *buf;
|
||||
unsigned int i;
|
||||
|
||||
bm = kzalloc(sizeof(*bm), GFP_KERNEL);
|
||||
if (!bm)
|
||||
return NULL;
|
||||
|
||||
kref_init(&bm->refcount);
|
||||
bm->dma_dir = dma_dir;
|
||||
if (bm->dma_dir != DMA_NONE) {
|
||||
/* Need ref to hardware device to free buffer later. */
|
||||
bm->dma_hw_dev = get_device(dev->hw_dev);
|
||||
}
|
||||
|
||||
bm->page_list = vzalloc(sizeof(*buf) * n_pages);
|
||||
if (!bm->page_list)
|
||||
goto err;
|
||||
|
||||
if (bm->dma_dir != DMA_NONE) {
|
||||
void *virt_addr;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
/*
|
||||
* Currently, the DMA buffer needs to be allocated as a
|
||||
* single block so that it can be mmap()'ed.
|
||||
*/
|
||||
virt_addr = dma_alloc_coherent(bm->dma_hw_dev,
|
||||
PAGE_SIZE * n_pages, &dma_addr,
|
||||
GFP_KERNEL);
|
||||
if (!virt_addr)
|
||||
goto err;
|
||||
|
||||
for (i = 0; i < n_pages; i++) {
|
||||
buf = &bm->page_list[i];
|
||||
buf->virt_addr = virt_addr + (i << PAGE_SHIFT);
|
||||
buf->dma_addr = dma_addr + (i << PAGE_SHIFT);
|
||||
}
|
||||
|
||||
bm->n_pages = i;
|
||||
} else {
|
||||
for (i = 0; i < n_pages; i++) {
|
||||
buf = &bm->page_list[i];
|
||||
buf->virt_addr = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!buf->virt_addr)
|
||||
break;
|
||||
|
||||
SetPageReserved(virt_to_page(buf->virt_addr));
|
||||
}
|
||||
|
||||
bm->n_pages = i;
|
||||
if (i < n_pages)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return bm;
|
||||
|
||||
err:
|
||||
comedi_buf_map_put(bm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __comedi_buf_alloc(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
unsigned int n_pages)
|
||||
|
@ -86,57 +154,37 @@ static void __comedi_buf_alloc(struct comedi_device *dev,
|
|||
return;
|
||||
}
|
||||
|
||||
bm = kzalloc(sizeof(*async->buf_map), GFP_KERNEL);
|
||||
bm = comedi_buf_map_alloc(dev, s->async_dma_dir, n_pages);
|
||||
if (!bm)
|
||||
return;
|
||||
|
||||
kref_init(&bm->refcount);
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
async->buf_map = bm;
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
bm->dma_dir = s->async_dma_dir;
|
||||
if (bm->dma_dir != DMA_NONE)
|
||||
/* Need ref to hardware device to free buffer later. */
|
||||
bm->dma_hw_dev = get_device(dev->hw_dev);
|
||||
|
||||
bm->page_list = vzalloc(sizeof(*buf) * n_pages);
|
||||
if (bm->page_list)
|
||||
if (bm->dma_dir != DMA_NONE) {
|
||||
/*
|
||||
* DMA buffer was allocated as a single block.
|
||||
* Address is in page_list[0].
|
||||
*/
|
||||
buf = &bm->page_list[0];
|
||||
async->prealloc_buf = buf->virt_addr;
|
||||
} else {
|
||||
pages = vmalloc(sizeof(struct page *) * n_pages);
|
||||
if (!pages)
|
||||
return;
|
||||
|
||||
if (!pages)
|
||||
return;
|
||||
for (i = 0; i < n_pages; i++) {
|
||||
buf = &bm->page_list[i];
|
||||
pages[i] = virt_to_page(buf->virt_addr);
|
||||
}
|
||||
|
||||
for (i = 0; i < n_pages; i++) {
|
||||
buf = &bm->page_list[i];
|
||||
if (bm->dma_dir != DMA_NONE)
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
buf->virt_addr = dma_alloc_coherent(bm->dma_hw_dev,
|
||||
PAGE_SIZE,
|
||||
&buf->dma_addr,
|
||||
GFP_KERNEL |
|
||||
__GFP_COMP);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
else
|
||||
buf->virt_addr = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!buf->virt_addr)
|
||||
break;
|
||||
|
||||
set_bit(PG_reserved, &(virt_to_page(buf->virt_addr)->flags));
|
||||
|
||||
pages[i] = virt_to_page(buf->virt_addr);
|
||||
}
|
||||
spin_lock_irqsave(&s->spin_lock, flags);
|
||||
bm->n_pages = i;
|
||||
spin_unlock_irqrestore(&s->spin_lock, flags);
|
||||
|
||||
/* vmap the prealloc_buf if all the pages were allocated */
|
||||
if (i == n_pages)
|
||||
/* vmap the pages to prealloc_buf */
|
||||
async->prealloc_buf = vmap(pages, n_pages, VM_MAP,
|
||||
COMEDI_PAGE_PROTECTION);
|
||||
|
||||
vfree(pages);
|
||||
vfree(pages);
|
||||
}
|
||||
}
|
||||
|
||||
void comedi_buf_map_get(struct comedi_buf_map *bm)
|
||||
|
|
|
@ -2301,11 +2301,12 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
|
|||
struct comedi_subdevice *s;
|
||||
struct comedi_async *async;
|
||||
struct comedi_buf_map *bm = NULL;
|
||||
struct comedi_buf_page *buf;
|
||||
unsigned long start = vma->vm_start;
|
||||
unsigned long size;
|
||||
int n_pages;
|
||||
int i;
|
||||
int retval;
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
* 'trylock' avoids circular dependency with current->mm->mmap_sem
|
||||
|
@ -2361,24 +2362,36 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
|
|||
retval = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
for (i = 0; i < n_pages; ++i) {
|
||||
struct comedi_buf_page *buf = &bm->page_list[i];
|
||||
if (bm->dma_dir != DMA_NONE) {
|
||||
/*
|
||||
* DMA buffer was allocated as a single block.
|
||||
* Address is in page_list[0].
|
||||
*/
|
||||
buf = &bm->page_list[0];
|
||||
retval = dma_mmap_coherent(bm->dma_hw_dev, vma, buf->virt_addr,
|
||||
buf->dma_addr, n_pages * PAGE_SIZE);
|
||||
} else {
|
||||
for (i = 0; i < n_pages; ++i) {
|
||||
unsigned long pfn;
|
||||
|
||||
if (remap_pfn_range(vma, start,
|
||||
page_to_pfn(virt_to_page(buf->virt_addr)),
|
||||
PAGE_SIZE, PAGE_SHARED)) {
|
||||
retval = -EAGAIN;
|
||||
goto done;
|
||||
buf = &bm->page_list[i];
|
||||
pfn = page_to_pfn(virt_to_page(buf->virt_addr));
|
||||
retval = remap_pfn_range(vma, start, pfn, PAGE_SIZE,
|
||||
PAGE_SHARED);
|
||||
if (retval)
|
||||
break;
|
||||
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
|
||||
vma->vm_ops = &comedi_vm_ops;
|
||||
vma->vm_private_data = bm;
|
||||
if (retval == 0) {
|
||||
vma->vm_ops = &comedi_vm_ops;
|
||||
vma->vm_private_data = bm;
|
||||
|
||||
vma->vm_ops->open(vma);
|
||||
vma->vm_ops->open(vma);
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
up_read(&dev->attach_lock);
|
||||
comedi_buf_map_put(bm); /* put reference to buf map - okay if NULL */
|
||||
|
|
|
@ -46,18 +46,6 @@ static unsigned char clk_gat_sce(unsigned int which, unsigned int chan,
|
|||
((source & 030) << 3) | (source & 007);
|
||||
}
|
||||
|
||||
static unsigned char clk_sce(unsigned int which, unsigned int chan,
|
||||
unsigned int source)
|
||||
{
|
||||
return clk_gat_sce(which, chan, source);
|
||||
}
|
||||
|
||||
static unsigned char gat_sce(unsigned int which, unsigned int chan,
|
||||
unsigned int source)
|
||||
{
|
||||
return clk_gat_sce(which, chan, source);
|
||||
}
|
||||
|
||||
/*
|
||||
* Periods of the internal clock sources in nanoseconds.
|
||||
*/
|
||||
|
@ -489,7 +477,7 @@ static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
|
|||
unsigned int offset = dio200_subdev_8254_offset(dev, s);
|
||||
|
||||
dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
|
||||
gat_sce((offset >> 2) & 1, chan, src));
|
||||
clk_gat_sce((offset >> 2) & 1, chan, src));
|
||||
}
|
||||
|
||||
static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
|
||||
|
@ -500,7 +488,7 @@ static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
|
|||
unsigned int offset = dio200_subdev_8254_offset(dev, s);
|
||||
|
||||
dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
|
||||
clk_sce((offset >> 2) & 1, chan, src));
|
||||
clk_gat_sce((offset >> 2) & 1, chan, src));
|
||||
}
|
||||
|
||||
static int dio200_subdev_8254_config(struct comedi_device *dev,
|
||||
|
|
|
@ -2330,7 +2330,8 @@ static irqreturn_t pci230_interrupt(int irq, void *d)
|
|||
devpriv->intr_running = false;
|
||||
spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
|
||||
|
||||
comedi_handle_events(dev, s_ao);
|
||||
if (s_ao)
|
||||
comedi_handle_events(dev, s_ao);
|
||||
comedi_handle_events(dev, s_ai);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
|
@ -557,7 +557,8 @@ static irqreturn_t dt282x_interrupt(int irq, void *d)
|
|||
}
|
||||
#endif
|
||||
comedi_handle_events(dev, s);
|
||||
comedi_handle_events(dev, s_ao);
|
||||
if (s_ao)
|
||||
comedi_handle_events(dev, s_ao);
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
|
|
@ -558,7 +558,14 @@ void mite_prep_dma(struct mite_channel *mite_chan,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mite_prep_dma);
|
||||
|
||||
static struct mite_channel *__mite_request_channel(struct mite *mite,
|
||||
/**
|
||||
* mite_request_channel_in_range() - Request a MITE dma channel.
|
||||
* @mite: MITE device.
|
||||
* @ring: MITE dma ring.
|
||||
* @min_channel: minimum channel index to use.
|
||||
* @max_channel: maximum channel index to use.
|
||||
*/
|
||||
struct mite_channel *mite_request_channel_in_range(struct mite *mite,
|
||||
struct mite_ring *ring,
|
||||
unsigned int min_channel,
|
||||
unsigned int max_channel)
|
||||
|
@ -583,21 +590,6 @@ static struct mite_channel *__mite_request_channel(struct mite *mite,
|
|||
spin_unlock_irqrestore(&mite->lock, flags);
|
||||
return mite_chan;
|
||||
}
|
||||
|
||||
/**
|
||||
* mite_request_channel_in_range() - Request a MITE dma channel.
|
||||
* @mite: MITE device.
|
||||
* @ring: MITE dma ring.
|
||||
* @min_channel: minimum channel index to use.
|
||||
* @max_channel: maximum channel index to use.
|
||||
*/
|
||||
struct mite_channel *mite_request_channel_in_range(struct mite *mite,
|
||||
struct mite_ring *ring,
|
||||
unsigned int min_channel,
|
||||
unsigned int max_channel)
|
||||
{
|
||||
return __mite_request_channel(mite, ring, min_channel, max_channel);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mite_request_channel_in_range);
|
||||
|
||||
/**
|
||||
|
@ -608,7 +600,8 @@ EXPORT_SYMBOL_GPL(mite_request_channel_in_range);
|
|||
struct mite_channel *mite_request_channel(struct mite *mite,
|
||||
struct mite_ring *ring)
|
||||
{
|
||||
return __mite_request_channel(mite, ring, 0, mite->num_channels - 1);
|
||||
return mite_request_channel_in_range(mite, ring, 0,
|
||||
mite->num_channels - 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mite_request_channel);
|
||||
|
||||
|
|
|
@ -1226,7 +1226,7 @@ static int usbdux_pwm_period(struct comedi_device *dev,
|
|||
unsigned int period)
|
||||
{
|
||||
struct usbdux_private *devpriv = dev->private;
|
||||
int fx2delay = 255;
|
||||
int fx2delay;
|
||||
|
||||
if (period < MIN_PWM_PERIOD)
|
||||
return -EAGAIN;
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
EROFS_VERSION = "1.0pre1"
|
||||
|
||||
ccflags-y += -Wall -DEROFS_VERSION=\"$(EROFS_VERSION)\"
|
||||
ccflags-y += -DEROFS_VERSION=\"$(EROFS_VERSION)\"
|
||||
|
||||
obj-$(CONFIG_EROFS_FS) += erofs.o
|
||||
# staging requirement: to be self-contained in its own directory
|
||||
ccflags-y += -I $(srctree)/$(src)/include
|
||||
erofs-objs := super.o inode.o data.o namei.o dir.o utils.o
|
||||
erofs-$(CONFIG_EROFS_FS_XATTR) += xattr.o
|
||||
erofs-$(CONFIG_EROFS_FS_ZIP) += unzip_vle.o unzip_vle_lz4.o
|
||||
erofs-$(CONFIG_EROFS_FS_ZIP) += unzip_vle.o zmap.o decompressor.o
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* linux/drivers/staging/erofs/compress.h
|
||||
*
|
||||
* Copyright (C) 2019 HUAWEI, Inc.
|
||||
* http://www.huawei.com/
|
||||
* Created by Gao Xiang <gaoxiang25@huawei.com>
|
||||
*/
|
||||
#ifndef __EROFS_FS_COMPRESS_H
|
||||
#define __EROFS_FS_COMPRESS_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
enum {
|
||||
Z_EROFS_COMPRESSION_SHIFTED = Z_EROFS_COMPRESSION_MAX,
|
||||
Z_EROFS_COMPRESSION_RUNTIME_MAX
|
||||
};
|
||||
|
||||
struct z_erofs_decompress_req {
|
||||
struct super_block *sb;
|
||||
struct page **in, **out;
|
||||
|
||||
unsigned short pageofs_out;
|
||||
unsigned int inputsize, outputsize;
|
||||
|
||||
/* indicate the algorithm will be used for decompression */
|
||||
unsigned int alg;
|
||||
bool inplace_io, partial_decoding;
|
||||
};
|
||||
|
||||
/*
|
||||
* - 0x5A110C8D ('sallocated', Z_EROFS_MAPPING_STAGING) -
|
||||
* used to mark temporary allocated pages from other
|
||||
* file/cached pages and NULL mapping pages.
|
||||
*/
|
||||
#define Z_EROFS_MAPPING_STAGING ((void *)0x5A110C8D)
|
||||
|
||||
/* check if a page is marked as staging */
|
||||
static inline bool z_erofs_page_is_staging(struct page *page)
|
||||
{
|
||||
return page->mapping == Z_EROFS_MAPPING_STAGING;
|
||||
}
|
||||
|
||||
static inline bool z_erofs_put_stagingpage(struct list_head *pagepool,
|
||||
struct page *page)
|
||||
{
|
||||
if (!z_erofs_page_is_staging(page))
|
||||
return false;
|
||||
|
||||
/* staging pages should not be used by others at the same time */
|
||||
if (page_ref_count(page) > 1)
|
||||
put_page(page);
|
||||
else
|
||||
list_add(&page->lru, pagepool);
|
||||
return true;
|
||||
}
|
||||
|
||||
int z_erofs_decompress(struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool);
|
||||
|
||||
#endif
|
||||
|
|
@ -124,7 +124,7 @@ static int erofs_map_blocks_flatmode(struct inode *inode,
|
|||
trace_erofs_map_blocks_flatmode_enter(inode, map, flags);
|
||||
|
||||
nblocks = DIV_ROUND_UP(inode->i_size, PAGE_SIZE);
|
||||
lastblk = nblocks - is_inode_layout_inline(inode);
|
||||
lastblk = nblocks - is_inode_flat_inline(inode);
|
||||
|
||||
if (unlikely(offset >= inode->i_size)) {
|
||||
/* leave out-of-bound access unmapped */
|
||||
|
@ -139,7 +139,7 @@ static int erofs_map_blocks_flatmode(struct inode *inode,
|
|||
if (offset < blknr_to_addr(lastblk)) {
|
||||
map->m_pa = blknr_to_addr(vi->raw_blkaddr) + map->m_la;
|
||||
map->m_plen = blknr_to_addr(lastblk) - offset;
|
||||
} else if (is_inode_layout_inline(inode)) {
|
||||
} else if (is_inode_flat_inline(inode)) {
|
||||
/* 2 - inode inline B: inode, [xattrs], inline last blk... */
|
||||
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
|
||||
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* linux/drivers/staging/erofs/decompressor.c
|
||||
*
|
||||
* Copyright (C) 2019 HUAWEI, Inc.
|
||||
* http://www.huawei.com/
|
||||
* Created by Gao Xiang <gaoxiang25@huawei.com>
|
||||
*/
|
||||
#include "compress.h"
|
||||
#include <linux/lz4.h>
|
||||
|
||||
#ifndef LZ4_DISTANCE_MAX /* history window size */
|
||||
#define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
|
||||
#endif
|
||||
|
||||
#define LZ4_MAX_DISTANCE_PAGES (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1)
|
||||
#ifndef LZ4_DECOMPRESS_INPLACE_MARGIN
|
||||
#define LZ4_DECOMPRESS_INPLACE_MARGIN(srcsize) (((srcsize) >> 8) + 32)
|
||||
#endif
|
||||
|
||||
struct z_erofs_decompressor {
|
||||
/*
|
||||
* if destpages have sparsed pages, fill them with bounce pages.
|
||||
* it also check whether destpages indicate continuous physical memory.
|
||||
*/
|
||||
int (*prepare_destpages)(struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool);
|
||||
int (*decompress)(struct z_erofs_decompress_req *rq, u8 *out);
|
||||
char *name;
|
||||
};
|
||||
|
||||
static int lz4_prepare_destpages(struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool)
|
||||
{
|
||||
const unsigned int nr =
|
||||
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
|
||||
struct page *availables[LZ4_MAX_DISTANCE_PAGES] = { NULL };
|
||||
unsigned long bounced[DIV_ROUND_UP(LZ4_MAX_DISTANCE_PAGES,
|
||||
BITS_PER_LONG)] = { 0 };
|
||||
void *kaddr = NULL;
|
||||
unsigned int i, j, top;
|
||||
|
||||
top = 0;
|
||||
for (i = j = 0; i < nr; ++i, ++j) {
|
||||
struct page *const page = rq->out[i];
|
||||
struct page *victim;
|
||||
|
||||
if (j >= LZ4_MAX_DISTANCE_PAGES)
|
||||
j = 0;
|
||||
|
||||
/* 'valid' bounced can only be tested after a complete round */
|
||||
if (test_bit(j, bounced)) {
|
||||
DBG_BUGON(i < LZ4_MAX_DISTANCE_PAGES);
|
||||
DBG_BUGON(top >= LZ4_MAX_DISTANCE_PAGES);
|
||||
availables[top++] = rq->out[i - LZ4_MAX_DISTANCE_PAGES];
|
||||
}
|
||||
|
||||
if (page) {
|
||||
__clear_bit(j, bounced);
|
||||
if (kaddr) {
|
||||
if (kaddr + PAGE_SIZE == page_address(page))
|
||||
kaddr += PAGE_SIZE;
|
||||
else
|
||||
kaddr = NULL;
|
||||
} else if (!i) {
|
||||
kaddr = page_address(page);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
kaddr = NULL;
|
||||
__set_bit(j, bounced);
|
||||
|
||||
if (top) {
|
||||
victim = availables[--top];
|
||||
get_page(victim);
|
||||
} else {
|
||||
if (!list_empty(pagepool)) {
|
||||
victim = lru_to_page(pagepool);
|
||||
list_del(&victim->lru);
|
||||
DBG_BUGON(page_ref_count(victim) != 1);
|
||||
} else {
|
||||
victim = alloc_pages(GFP_KERNEL, 0);
|
||||
if (!victim)
|
||||
return -ENOMEM;
|
||||
}
|
||||
victim->mapping = Z_EROFS_MAPPING_STAGING;
|
||||
}
|
||||
rq->out[i] = victim;
|
||||
}
|
||||
return kaddr ? 1 : 0;
|
||||
}
|
||||
|
||||
static void *generic_copy_inplace_data(struct z_erofs_decompress_req *rq,
|
||||
u8 *src, unsigned int pageofs_in)
|
||||
{
|
||||
/*
|
||||
* if in-place decompression is ongoing, those decompressed
|
||||
* pages should be copied in order to avoid being overlapped.
|
||||
*/
|
||||
struct page **in = rq->in;
|
||||
u8 *const tmp = erofs_get_pcpubuf(0);
|
||||
u8 *tmpp = tmp;
|
||||
unsigned int inlen = rq->inputsize - pageofs_in;
|
||||
unsigned int count = min_t(uint, inlen, PAGE_SIZE - pageofs_in);
|
||||
|
||||
while (tmpp < tmp + inlen) {
|
||||
if (!src)
|
||||
src = kmap_atomic(*in);
|
||||
memcpy(tmpp, src + pageofs_in, count);
|
||||
kunmap_atomic(src);
|
||||
src = NULL;
|
||||
tmpp += count;
|
||||
pageofs_in = 0;
|
||||
count = PAGE_SIZE;
|
||||
++in;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out)
|
||||
{
|
||||
unsigned int inputmargin, inlen;
|
||||
u8 *src;
|
||||
bool copied, support_0padding;
|
||||
int ret;
|
||||
|
||||
if (rq->inputsize > PAGE_SIZE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
src = kmap_atomic(*rq->in);
|
||||
inputmargin = 0;
|
||||
support_0padding = false;
|
||||
|
||||
/* decompression inplace is only safe when 0padding is enabled */
|
||||
if (EROFS_SB(rq->sb)->requirements & EROFS_REQUIREMENT_LZ4_0PADDING) {
|
||||
support_0padding = true;
|
||||
|
||||
while (!src[inputmargin & ~PAGE_MASK])
|
||||
if (!(++inputmargin & ~PAGE_MASK))
|
||||
break;
|
||||
|
||||
if (inputmargin >= rq->inputsize) {
|
||||
kunmap_atomic(src);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
copied = false;
|
||||
inlen = rq->inputsize - inputmargin;
|
||||
if (rq->inplace_io) {
|
||||
const uint oend = (rq->pageofs_out +
|
||||
rq->outputsize) & ~PAGE_MASK;
|
||||
const uint nr = PAGE_ALIGN(rq->pageofs_out +
|
||||
rq->outputsize) >> PAGE_SHIFT;
|
||||
|
||||
if (rq->partial_decoding || !support_0padding ||
|
||||
rq->out[nr - 1] != rq->in[0] ||
|
||||
rq->inputsize - oend <
|
||||
LZ4_DECOMPRESS_INPLACE_MARGIN(inlen)) {
|
||||
src = generic_copy_inplace_data(rq, src, inputmargin);
|
||||
inputmargin = 0;
|
||||
copied = true;
|
||||
}
|
||||
}
|
||||
|
||||
ret = LZ4_decompress_safe_partial(src + inputmargin, out,
|
||||
inlen, rq->outputsize,
|
||||
rq->outputsize);
|
||||
if (ret < 0) {
|
||||
errln("%s, failed to decompress, in[%p, %u, %u] out[%p, %u]",
|
||||
__func__, src + inputmargin, inlen, inputmargin,
|
||||
out, rq->outputsize);
|
||||
WARN_ON(1);
|
||||
print_hex_dump(KERN_DEBUG, "[ in]: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, src + inputmargin, inlen, true);
|
||||
print_hex_dump(KERN_DEBUG, "[out]: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, out, rq->outputsize, true);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
if (copied)
|
||||
erofs_put_pcpubuf(src);
|
||||
else
|
||||
kunmap_atomic(src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct z_erofs_decompressor decompressors[] = {
|
||||
[Z_EROFS_COMPRESSION_SHIFTED] = {
|
||||
.name = "shifted"
|
||||
},
|
||||
[Z_EROFS_COMPRESSION_LZ4] = {
|
||||
.prepare_destpages = lz4_prepare_destpages,
|
||||
.decompress = lz4_decompress,
|
||||
.name = "lz4"
|
||||
},
|
||||
};
|
||||
|
||||
static void copy_from_pcpubuf(struct page **out, const char *dst,
|
||||
unsigned short pageofs_out,
|
||||
unsigned int outputsize)
|
||||
{
|
||||
const char *end = dst + outputsize;
|
||||
const unsigned int righthalf = PAGE_SIZE - pageofs_out;
|
||||
const char *cur = dst - pageofs_out;
|
||||
|
||||
while (cur < end) {
|
||||
struct page *const page = *out++;
|
||||
|
||||
if (page) {
|
||||
char *buf = kmap_atomic(page);
|
||||
|
||||
if (cur >= dst) {
|
||||
memcpy(buf, cur, min_t(uint, PAGE_SIZE,
|
||||
end - cur));
|
||||
} else {
|
||||
memcpy(buf + pageofs_out, cur + pageofs_out,
|
||||
min_t(uint, righthalf, end - cur));
|
||||
}
|
||||
kunmap_atomic(buf);
|
||||
}
|
||||
cur += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static int decompress_generic(struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool)
|
||||
{
|
||||
const unsigned int nrpages_out =
|
||||
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
|
||||
const struct z_erofs_decompressor *alg = decompressors + rq->alg;
|
||||
unsigned int dst_maptype;
|
||||
void *dst;
|
||||
int ret;
|
||||
|
||||
if (nrpages_out == 1 && !rq->inplace_io) {
|
||||
DBG_BUGON(!*rq->out);
|
||||
dst = kmap_atomic(*rq->out);
|
||||
dst_maptype = 0;
|
||||
goto dstmap_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the case of small output size (especially much less
|
||||
* than PAGE_SIZE), memcpy the decompressed data rather than
|
||||
* compressed data is preferred.
|
||||
*/
|
||||
if (rq->outputsize <= PAGE_SIZE * 7 / 8) {
|
||||
dst = erofs_get_pcpubuf(0);
|
||||
if (IS_ERR(dst))
|
||||
return PTR_ERR(dst);
|
||||
|
||||
rq->inplace_io = false;
|
||||
ret = alg->decompress(rq, dst);
|
||||
if (!ret)
|
||||
copy_from_pcpubuf(rq->out, dst, rq->pageofs_out,
|
||||
rq->outputsize);
|
||||
|
||||
erofs_put_pcpubuf(dst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = alg->prepare_destpages(rq, pagepool);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
} else if (ret) {
|
||||
dst = page_address(*rq->out);
|
||||
dst_maptype = 1;
|
||||
goto dstmap_out;
|
||||
}
|
||||
|
||||
dst = erofs_vmap(rq->out, nrpages_out);
|
||||
if (!dst)
|
||||
return -ENOMEM;
|
||||
dst_maptype = 2;
|
||||
|
||||
dstmap_out:
|
||||
ret = alg->decompress(rq, dst + rq->pageofs_out);
|
||||
|
||||
if (!dst_maptype)
|
||||
kunmap_atomic(dst);
|
||||
else if (dst_maptype == 2)
|
||||
erofs_vunmap(dst, nrpages_out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int shifted_decompress(const struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool)
|
||||
{
|
||||
const unsigned int nrpages_out =
|
||||
PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
|
||||
const unsigned int righthalf = PAGE_SIZE - rq->pageofs_out;
|
||||
unsigned char *src, *dst;
|
||||
|
||||
if (nrpages_out > 2) {
|
||||
DBG_BUGON(1);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rq->out[0] == *rq->in) {
|
||||
DBG_BUGON(nrpages_out != 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
src = kmap_atomic(*rq->in);
|
||||
if (!rq->out[0]) {
|
||||
dst = NULL;
|
||||
} else {
|
||||
dst = kmap_atomic(rq->out[0]);
|
||||
memcpy(dst + rq->pageofs_out, src, righthalf);
|
||||
}
|
||||
|
||||
if (rq->out[1] == *rq->in) {
|
||||
memmove(src, src + righthalf, rq->pageofs_out);
|
||||
} else if (nrpages_out == 2) {
|
||||
if (dst)
|
||||
kunmap_atomic(dst);
|
||||
DBG_BUGON(!rq->out[1]);
|
||||
dst = kmap_atomic(rq->out[1]);
|
||||
memcpy(dst, src + righthalf, rq->pageofs_out);
|
||||
}
|
||||
if (dst)
|
||||
kunmap_atomic(dst);
|
||||
kunmap_atomic(src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int z_erofs_decompress(struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool)
|
||||
{
|
||||
if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED)
|
||||
return shifted_decompress(rq, pagepool);
|
||||
return decompress_generic(rq, pagepool);
|
||||
}
|
||||
|
|
@ -42,10 +42,9 @@ static int erofs_fill_dentries(struct dir_context *ctx,
|
|||
void *dentry_blk, unsigned int *ofs,
|
||||
unsigned int nameoff, unsigned int maxsize)
|
||||
{
|
||||
struct erofs_dirent *de = dentry_blk;
|
||||
struct erofs_dirent *de = dentry_blk + *ofs;
|
||||
const struct erofs_dirent *end = dentry_blk + nameoff;
|
||||
|
||||
de = dentry_blk + *ofs;
|
||||
while (de < end) {
|
||||
const char *de_name;
|
||||
unsigned int de_namelen;
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
* Any bits that aren't in EROFS_ALL_REQUIREMENTS should be
|
||||
* incompatible with this kernel version.
|
||||
*/
|
||||
#define EROFS_ALL_REQUIREMENTS 0
|
||||
#define EROFS_REQUIREMENT_LZ4_0PADDING 0x00000001
|
||||
#define EROFS_ALL_REQUIREMENTS EROFS_REQUIREMENT_LZ4_0PADDING
|
||||
|
||||
struct erofs_super_block {
|
||||
/* 0 */__le32 magic; /* in the little endian */
|
||||
|
@ -49,19 +50,29 @@ struct erofs_super_block {
|
|||
* erofs inode data mapping:
|
||||
* 0 - inode plain without inline data A:
|
||||
* inode, [xattrs], ... | ... | no-holed data
|
||||
* 1 - inode VLE compression B:
|
||||
* 1 - inode VLE compression B (legacy):
|
||||
* inode, [xattrs], extents ... | ...
|
||||
* 2 - inode plain with inline data C:
|
||||
* inode, [xattrs], last_inline_data, ... | ... | no-holed data
|
||||
* 3~7 - reserved
|
||||
* 3 - inode compression D:
|
||||
* inode, [xattrs], map_header, extents ... | ...
|
||||
* 4~7 - reserved
|
||||
*/
|
||||
enum {
|
||||
EROFS_INODE_LAYOUT_PLAIN,
|
||||
EROFS_INODE_LAYOUT_COMPRESSION,
|
||||
EROFS_INODE_LAYOUT_INLINE,
|
||||
EROFS_INODE_FLAT_PLAIN,
|
||||
EROFS_INODE_FLAT_COMPRESSION_LEGACY,
|
||||
EROFS_INODE_FLAT_INLINE,
|
||||
EROFS_INODE_FLAT_COMPRESSION,
|
||||
EROFS_INODE_LAYOUT_MAX
|
||||
};
|
||||
|
||||
static bool erofs_inode_is_data_compressed(unsigned int datamode)
|
||||
{
|
||||
if (datamode == EROFS_INODE_FLAT_COMPRESSION)
|
||||
return true;
|
||||
return datamode == EROFS_INODE_FLAT_COMPRESSION_LEGACY;
|
||||
}
|
||||
|
||||
/* bit definitions of inode i_advise */
|
||||
#define EROFS_I_VERSION_BITS 1
|
||||
#define EROFS_I_DATA_MAPPING_BITS 3
|
||||
|
@ -176,11 +187,39 @@ struct erofs_xattr_entry {
|
|||
sizeof(struct erofs_xattr_entry) + \
|
||||
(entry)->e_name_len + le16_to_cpu((entry)->e_value_size))
|
||||
|
||||
/* have to be aligned with 8 bytes on disk */
|
||||
struct erofs_extent_header {
|
||||
__le32 eh_checksum;
|
||||
__le32 eh_reserved[3];
|
||||
} __packed;
|
||||
/* available compression algorithm types */
|
||||
enum {
|
||||
Z_EROFS_COMPRESSION_LZ4,
|
||||
Z_EROFS_COMPRESSION_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* bit 0 : COMPACTED_2B indexes (0 - off; 1 - on)
|
||||
* e.g. for 4k logical cluster size, 4B if compacted 2B is off;
|
||||
* (4B) + 2B + (4B) if compacted 2B is on.
|
||||
*/
|
||||
#define Z_EROFS_ADVISE_COMPACTED_2B_BIT 0
|
||||
|
||||
#define Z_EROFS_ADVISE_COMPACTED_2B (1 << Z_EROFS_ADVISE_COMPACTED_2B_BIT)
|
||||
|
||||
struct z_erofs_map_header {
|
||||
__le32 h_reserved1;
|
||||
__le16 h_advise;
|
||||
/*
|
||||
* bit 0-3 : algorithm type of head 1 (logical cluster type 01);
|
||||
* bit 4-7 : algorithm type of head 2 (logical cluster type 11).
|
||||
*/
|
||||
__u8 h_algorithmtype;
|
||||
/*
|
||||
* bit 0-2 : logical cluster bits - 12, e.g. 0 for 4096;
|
||||
* bit 3-4 : (physical - logical) cluster bits of head 1:
|
||||
* For example, if logical clustersize = 4096, 1 for 8192.
|
||||
* bit 5-7 : (physical - logical) cluster bits of head 2.
|
||||
*/
|
||||
__u8 h_clusterbits;
|
||||
};
|
||||
|
||||
#define Z_EROFS_VLE_LEGACY_HEADER_PADDING 8
|
||||
|
||||
/*
|
||||
* Z_EROFS Variable-sized Logical Extent cluster type:
|
||||
|
@ -236,8 +275,9 @@ struct z_erofs_vle_decompressed_index {
|
|||
} di_u __packed; /* 8 bytes */
|
||||
} __packed;
|
||||
|
||||
#define Z_EROFS_VLE_EXTENT_ALIGN(size) round_up(size, \
|
||||
sizeof(struct z_erofs_vle_decompressed_index))
|
||||
#define Z_EROFS_VLE_LEGACY_INDEX_ALIGN(size) \
|
||||
(round_up(size, sizeof(struct z_erofs_vle_decompressed_index)) + \
|
||||
sizeof(struct z_erofs_map_header) + Z_EROFS_VLE_LEGACY_HEADER_PADDING)
|
||||
|
||||
/* dirent sorts in alphabet order, thus we can do binary search */
|
||||
struct erofs_dirent {
|
||||
|
@ -270,7 +310,7 @@ static inline void erofs_check_ondisk_layout_definitions(void)
|
|||
BUILD_BUG_ON(sizeof(struct erofs_inode_v2) != 64);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_xattr_ibody_header) != 12);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_xattr_entry) != 4);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_extent_header) != 16);
|
||||
BUILD_BUG_ON(sizeof(struct z_erofs_map_header) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct z_erofs_vle_decompressed_index) != 8);
|
||||
BUILD_BUG_ON(sizeof(struct erofs_dirent) != 12);
|
||||
|
||||
|
|
|
@ -20,12 +20,13 @@ static int read_inode(struct inode *inode, void *data)
|
|||
struct erofs_vnode *vi = EROFS_V(inode);
|
||||
struct erofs_inode_v1 *v1 = data;
|
||||
const unsigned int advise = le16_to_cpu(v1->i_advise);
|
||||
erofs_blk_t nblks = 0;
|
||||
|
||||
vi->data_mapping_mode = __inode_data_mapping(advise);
|
||||
vi->datamode = __inode_data_mapping(advise);
|
||||
|
||||
if (unlikely(vi->data_mapping_mode >= EROFS_INODE_LAYOUT_MAX)) {
|
||||
errln("unknown data mapping mode %u of nid %llu",
|
||||
vi->data_mapping_mode, vi->nid);
|
||||
if (unlikely(vi->datamode >= EROFS_INODE_LAYOUT_MAX)) {
|
||||
errln("unsupported data mapping %u of nid %llu",
|
||||
vi->datamode, vi->nid);
|
||||
DBG_BUGON(1);
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -60,6 +61,10 @@ static int read_inode(struct inode *inode, void *data)
|
|||
le32_to_cpu(v2->i_ctime_nsec);
|
||||
|
||||
inode->i_size = le64_to_cpu(v2->i_size);
|
||||
|
||||
/* total blocks for compressed files */
|
||||
if (is_inode_layout_compression(inode))
|
||||
nblks = le32_to_cpu(v2->i_u.compressed_blocks);
|
||||
} else if (__inode_version(advise) == EROFS_INODE_LAYOUT_V1) {
|
||||
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
|
||||
|
||||
|
@ -90,6 +95,8 @@ static int read_inode(struct inode *inode, void *data)
|
|||
sbi->build_time_nsec;
|
||||
|
||||
inode->i_size = le32_to_cpu(v1->i_size);
|
||||
if (is_inode_layout_compression(inode))
|
||||
nblks = le32_to_cpu(v1->i_u.compressed_blocks);
|
||||
} else {
|
||||
errln("unsupported on-disk inode version %u of nid %llu",
|
||||
__inode_version(advise), vi->nid);
|
||||
|
@ -97,8 +104,11 @@ static int read_inode(struct inode *inode, void *data)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* measure inode.i_blocks as the generic filesystem */
|
||||
inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
|
||||
if (!nblks)
|
||||
/* measure inode.i_blocks as generic filesystems */
|
||||
inode->i_blocks = roundup(inode->i_size, EROFS_BLKSIZ) >> 9;
|
||||
else
|
||||
inode->i_blocks = nblks << LOG_SECTORS_PER_BLOCK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -117,12 +127,9 @@ static int fill_inline_data(struct inode *inode, void *data,
|
|||
{
|
||||
struct erofs_vnode *vi = EROFS_V(inode);
|
||||
struct erofs_sb_info *sbi = EROFS_I_SB(inode);
|
||||
int mode = vi->data_mapping_mode;
|
||||
|
||||
DBG_BUGON(mode >= EROFS_INODE_LAYOUT_MAX);
|
||||
|
||||
/* should be inode inline C */
|
||||
if (mode != EROFS_INODE_LAYOUT_INLINE)
|
||||
if (!is_inode_flat_inline(inode))
|
||||
return 0;
|
||||
|
||||
/* fast symlink (following ext4) */
|
||||
|
@ -148,7 +155,7 @@ static int fill_inline_data(struct inode *inode, void *data,
|
|||
inode->i_link = lnk;
|
||||
set_inode_fast_symlink(inode);
|
||||
}
|
||||
return -EAGAIN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_inode(struct inode *inode, int isdir)
|
||||
|
@ -197,25 +204,21 @@ static int fill_inode(struct inode *inode, int isdir)
|
|||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
inode->i_op = &erofs_generic_iops;
|
||||
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
||||
goto out_unlock;
|
||||
} else {
|
||||
err = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (is_inode_layout_compression(inode)) {
|
||||
#ifdef CONFIG_EROFS_FS_ZIP
|
||||
inode->i_mapping->a_ops =
|
||||
&z_erofs_vle_normalaccess_aops;
|
||||
#else
|
||||
err = -ENOTSUPP;
|
||||
#endif
|
||||
err = z_erofs_fill_inode(inode);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
inode->i_mapping->a_ops = &erofs_raw_access_aops;
|
||||
|
||||
/* fill last page if inline data is available */
|
||||
fill_inline_data(inode, data, ofs);
|
||||
err = fill_inline_data(inode, data, ofs);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
|
@ -285,7 +288,24 @@ struct inode *erofs_iget(struct super_block *sb,
|
|||
return inode;
|
||||
}
|
||||
|
||||
int erofs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags)
|
||||
{
|
||||
struct inode *const inode = d_inode(path->dentry);
|
||||
|
||||
if (is_inode_layout_compression(inode))
|
||||
stat->attributes |= STATX_ATTR_COMPRESSED;
|
||||
|
||||
stat->attributes |= STATX_ATTR_IMMUTABLE;
|
||||
stat->attributes_mask |= (STATX_ATTR_COMPRESSED |
|
||||
STATX_ATTR_IMMUTABLE);
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct inode_operations erofs_generic_iops = {
|
||||
.getattr = erofs_getattr,
|
||||
#ifdef CONFIG_EROFS_FS_XATTR
|
||||
.listxattr = erofs_listxattr,
|
||||
#endif
|
||||
|
@ -294,6 +314,7 @@ const struct inode_operations erofs_generic_iops = {
|
|||
|
||||
const struct inode_operations erofs_symlink_iops = {
|
||||
.get_link = page_get_link,
|
||||
.getattr = erofs_getattr,
|
||||
#ifdef CONFIG_EROFS_FS_XATTR
|
||||
.listxattr = erofs_listxattr,
|
||||
#endif
|
||||
|
@ -302,6 +323,7 @@ const struct inode_operations erofs_symlink_iops = {
|
|||
|
||||
const struct inode_operations erofs_fast_symlink_iops = {
|
||||
.get_link = simple_get_link,
|
||||
.getattr = erofs_getattr,
|
||||
#ifdef CONFIG_EROFS_FS_XATTR
|
||||
.listxattr = erofs_listxattr,
|
||||
#endif
|
||||
|
|
|
@ -321,6 +321,10 @@ static inline void z_erofs_exit_zip_subsystem(void) {}
|
|||
|
||||
/* page count of a compressed cluster */
|
||||
#define erofs_clusterpages(sbi) ((1 << (sbi)->clusterbits) / PAGE_SIZE)
|
||||
|
||||
#define EROFS_PCPUBUF_NR_PAGES Z_EROFS_CLUSTER_MAX_PAGES
|
||||
#else
|
||||
#define EROFS_PCPUBUF_NR_PAGES 0
|
||||
#endif
|
||||
|
||||
typedef u64 erofs_off_t;
|
||||
|
@ -339,9 +343,11 @@ static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid)
|
|||
|
||||
/* atomic flag definitions */
|
||||
#define EROFS_V_EA_INITED_BIT 0
|
||||
#define EROFS_V_Z_INITED_BIT 1
|
||||
|
||||
/* bitlock definitions (arranged in reverse order) */
|
||||
#define EROFS_V_BL_XATTR_BIT (BITS_PER_LONG - 1)
|
||||
#define EROFS_V_BL_Z_BIT (BITS_PER_LONG - 2)
|
||||
|
||||
struct erofs_vnode {
|
||||
erofs_nid_t nid;
|
||||
|
@ -349,16 +355,24 @@ struct erofs_vnode {
|
|||
/* atomic flags (including bitlocks) */
|
||||
unsigned long flags;
|
||||
|
||||
unsigned char data_mapping_mode;
|
||||
/* inline size in bytes */
|
||||
unsigned char datamode;
|
||||
unsigned char inode_isize;
|
||||
unsigned short xattr_isize;
|
||||
|
||||
unsigned xattr_shared_count;
|
||||
unsigned *xattr_shared_xattrs;
|
||||
|
||||
erofs_blk_t raw_blkaddr;
|
||||
|
||||
union {
|
||||
erofs_blk_t raw_blkaddr;
|
||||
#ifdef CONFIG_EROFS_FS_ZIP
|
||||
struct {
|
||||
unsigned short z_advise;
|
||||
unsigned char z_algorithmtype[2];
|
||||
unsigned char z_logical_clusterbits;
|
||||
unsigned char z_physical_clusterbits[2];
|
||||
};
|
||||
#endif
|
||||
};
|
||||
/* the corresponding vfs inode */
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
@ -383,20 +397,14 @@ static inline unsigned long inode_datablocks(struct inode *inode)
|
|||
return DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ);
|
||||
}
|
||||
|
||||
static inline bool is_inode_layout_plain(struct inode *inode)
|
||||
{
|
||||
return EROFS_V(inode)->data_mapping_mode == EROFS_INODE_LAYOUT_PLAIN;
|
||||
}
|
||||
|
||||
static inline bool is_inode_layout_compression(struct inode *inode)
|
||||
{
|
||||
return EROFS_V(inode)->data_mapping_mode ==
|
||||
EROFS_INODE_LAYOUT_COMPRESSION;
|
||||
return erofs_inode_is_data_compressed(EROFS_V(inode)->datamode);
|
||||
}
|
||||
|
||||
static inline bool is_inode_layout_inline(struct inode *inode)
|
||||
static inline bool is_inode_flat_inline(struct inode *inode)
|
||||
{
|
||||
return EROFS_V(inode)->data_mapping_mode == EROFS_INODE_LAYOUT_INLINE;
|
||||
return EROFS_V(inode)->datamode == EROFS_INODE_FLAT_INLINE;
|
||||
}
|
||||
|
||||
extern const struct super_operations erofs_sops;
|
||||
|
@ -433,6 +441,7 @@ extern const struct address_space_operations z_erofs_vle_normalaccess_aops;
|
|||
*/
|
||||
enum {
|
||||
BH_Zipped = BH_PrivateStart,
|
||||
BH_FullMapped,
|
||||
};
|
||||
|
||||
/* Has a disk mapping */
|
||||
|
@ -441,6 +450,8 @@ enum {
|
|||
#define EROFS_MAP_META (1 << BH_Meta)
|
||||
/* The extent has been compressed */
|
||||
#define EROFS_MAP_ZIPPED (1 << BH_Zipped)
|
||||
/* The length of extent is full */
|
||||
#define EROFS_MAP_FULL_MAPPED (1 << BH_FullMapped)
|
||||
|
||||
struct erofs_map_blocks {
|
||||
erofs_off_t m_pa, m_la;
|
||||
|
@ -454,11 +465,14 @@ struct erofs_map_blocks {
|
|||
/* Flags used by erofs_map_blocks() */
|
||||
#define EROFS_GET_BLOCKS_RAW 0x0001
|
||||
|
||||
/* zmap.c */
|
||||
#ifdef CONFIG_EROFS_FS_ZIP
|
||||
int z_erofs_fill_inode(struct inode *inode);
|
||||
int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
struct erofs_map_blocks *map,
|
||||
int flags);
|
||||
#else
|
||||
static inline int z_erofs_fill_inode(struct inode *inode) { return -ENOTSUPP; }
|
||||
static inline int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
struct erofs_map_blocks *map,
|
||||
int flags)
|
||||
|
@ -558,6 +572,8 @@ static inline bool is_inode_fast_symlink(struct inode *inode)
|
|||
}
|
||||
|
||||
struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid, bool dir);
|
||||
int erofs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags);
|
||||
|
||||
/* namei.c */
|
||||
extern const struct inode_operations erofs_dir_iops;
|
||||
|
@ -599,6 +615,22 @@ static inline void erofs_vunmap(const void *mem, unsigned int count)
|
|||
extern struct shrinker erofs_shrinker_info;
|
||||
|
||||
struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp);
|
||||
|
||||
#if (EROFS_PCPUBUF_NR_PAGES > 0)
|
||||
void *erofs_get_pcpubuf(unsigned int pagenr);
|
||||
#define erofs_put_pcpubuf(buf) do { \
|
||||
(void)&(buf); \
|
||||
preempt_enable(); \
|
||||
} while (0)
|
||||
#else
|
||||
static inline void *erofs_get_pcpubuf(unsigned int pagenr)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
#define erofs_put_pcpubuf(buf) do {} while (0)
|
||||
#endif
|
||||
|
||||
void erofs_register_super(struct super_block *sb);
|
||||
void erofs_unregister_super(struct super_block *sb);
|
||||
|
||||
|
|
|
@ -247,6 +247,7 @@ static struct dentry *erofs_lookup(struct inode *dir,
|
|||
|
||||
const struct inode_operations erofs_dir_iops = {
|
||||
.lookup = erofs_lookup,
|
||||
.getattr = erofs_getattr,
|
||||
#ifdef CONFIG_EROFS_FS_XATTR
|
||||
.listxattr = erofs_listxattr,
|
||||
#endif
|
||||
|
|
|
@ -383,7 +383,7 @@ static int erofs_read_super(struct super_block *sb,
|
|||
goto err;
|
||||
}
|
||||
|
||||
sbi = kzalloc(sizeof(struct erofs_sb_info), GFP_KERNEL);
|
||||
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
|
||||
if (unlikely(!sbi)) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
|
|
|
@ -69,10 +69,7 @@ z_erofs_pagevec_ctor_next_page(struct z_erofs_pagevec_ctor *ctor,
|
|||
if (tags == Z_EROFS_PAGE_TYPE_EXCLUSIVE)
|
||||
return tagptr_unfold_ptr(t);
|
||||
}
|
||||
|
||||
if (unlikely(nr >= ctor->nr))
|
||||
BUG();
|
||||
|
||||
DBG_BUGON(nr >= ctor->nr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* distribution for more details.
|
||||
*/
|
||||
#include "unzip_vle.h"
|
||||
#include "compress.h"
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include <trace/events/erofs.h>
|
||||
|
@ -329,7 +330,7 @@ try_to_claim_workgroup(struct z_erofs_vle_workgroup *grp,
|
|||
z_erofs_vle_owned_workgrp_t *owned_head,
|
||||
bool *hosted)
|
||||
{
|
||||
DBG_BUGON(*hosted == true);
|
||||
DBG_BUGON(*hosted);
|
||||
|
||||
/* let's claim these following types of workgroup */
|
||||
retry:
|
||||
|
@ -468,6 +469,9 @@ z_erofs_vle_work_register(const struct z_erofs_vle_work_finder *f,
|
|||
Z_EROFS_VLE_WORKGRP_FMT_LZ4 :
|
||||
Z_EROFS_VLE_WORKGRP_FMT_PLAIN);
|
||||
|
||||
if (map->m_flags & EROFS_MAP_FULL_MAPPED)
|
||||
grp->flags |= Z_EROFS_VLE_WORKGRP_FULL_LENGTH;
|
||||
|
||||
/* new workgrps have been claimed as type 1 */
|
||||
WRITE_ONCE(grp->next, *f->owned_head);
|
||||
/* primary and followed work for all new workgrps */
|
||||
|
@ -552,8 +556,7 @@ static int z_erofs_vle_work_iter_begin(struct z_erofs_vle_work_builder *builder,
|
|||
if (IS_ERR(work))
|
||||
return PTR_ERR(work);
|
||||
got_it:
|
||||
z_erofs_pagevec_ctor_init(&builder->vector,
|
||||
Z_EROFS_VLE_INLINE_PAGEVECS,
|
||||
z_erofs_pagevec_ctor_init(&builder->vector, Z_EROFS_NR_INLINE_PAGEVECS,
|
||||
work->pagevec, work->vcnt);
|
||||
|
||||
if (builder->role >= Z_EROFS_VLE_WORK_PRIMARY) {
|
||||
|
@ -856,7 +859,7 @@ static inline void z_erofs_vle_read_endio(struct bio *bio)
|
|||
DBG_BUGON(PageUptodate(page));
|
||||
DBG_BUGON(!page->mapping);
|
||||
|
||||
if (unlikely(!sbi && !z_erofs_is_stagingpage(page))) {
|
||||
if (unlikely(!sbi && !z_erofs_page_is_staging(page))) {
|
||||
sbi = EROFS_SB(page->mapping->host->i_sb);
|
||||
|
||||
if (time_to_inject(sbi, FAULT_READ_IO)) {
|
||||
|
@ -897,12 +900,12 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
unsigned int sparsemem_pages = 0;
|
||||
struct page *pages_onstack[Z_EROFS_VLE_VMAP_ONSTACK_PAGES];
|
||||
struct page **pages, **compressed_pages, *page;
|
||||
unsigned int i, llen;
|
||||
unsigned int algorithm;
|
||||
unsigned int i, outputsize;
|
||||
|
||||
enum z_erofs_page_type page_type;
|
||||
bool overlapped;
|
||||
bool overlapped, partial;
|
||||
struct z_erofs_vle_work *work;
|
||||
void *vout;
|
||||
int err;
|
||||
|
||||
might_sleep();
|
||||
|
@ -936,7 +939,7 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
for (i = 0; i < nr_pages; ++i)
|
||||
pages[i] = NULL;
|
||||
|
||||
z_erofs_pagevec_ctor_init(&ctor, Z_EROFS_VLE_INLINE_PAGEVECS,
|
||||
z_erofs_pagevec_ctor_init(&ctor, Z_EROFS_NR_INLINE_PAGEVECS,
|
||||
work->pagevec, 0);
|
||||
|
||||
for (i = 0; i < work->vcnt; ++i) {
|
||||
|
@ -948,7 +951,7 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
DBG_BUGON(!page);
|
||||
DBG_BUGON(!page->mapping);
|
||||
|
||||
if (z_erofs_gather_if_stagingpage(page_pool, page))
|
||||
if (z_erofs_put_stagingpage(page_pool, page))
|
||||
continue;
|
||||
|
||||
if (page_type == Z_EROFS_VLE_PAGE_TYPE_HEAD)
|
||||
|
@ -978,7 +981,7 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
DBG_BUGON(!page);
|
||||
DBG_BUGON(!page->mapping);
|
||||
|
||||
if (!z_erofs_is_stagingpage(page)) {
|
||||
if (!z_erofs_page_is_staging(page)) {
|
||||
if (erofs_page_is_managed(sbi, page)) {
|
||||
if (unlikely(!PageUptodate(page)))
|
||||
err = -EIO;
|
||||
|
@ -1009,43 +1012,30 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
if (unlikely(err))
|
||||
goto out;
|
||||
|
||||
llen = (nr_pages << PAGE_SHIFT) - work->pageofs;
|
||||
|
||||
if (z_erofs_vle_workgrp_fmt(grp) == Z_EROFS_VLE_WORKGRP_FMT_PLAIN) {
|
||||
err = z_erofs_vle_plain_copy(compressed_pages, clusterpages,
|
||||
pages, nr_pages, work->pageofs);
|
||||
goto out;
|
||||
if (nr_pages << PAGE_SHIFT >= work->pageofs + grp->llen) {
|
||||
outputsize = grp->llen;
|
||||
partial = !(grp->flags & Z_EROFS_VLE_WORKGRP_FULL_LENGTH);
|
||||
} else {
|
||||
outputsize = (nr_pages << PAGE_SHIFT) - work->pageofs;
|
||||
partial = true;
|
||||
}
|
||||
|
||||
if (llen > grp->llen)
|
||||
llen = grp->llen;
|
||||
if (z_erofs_vle_workgrp_fmt(grp) == Z_EROFS_VLE_WORKGRP_FMT_PLAIN)
|
||||
algorithm = Z_EROFS_COMPRESSION_SHIFTED;
|
||||
else
|
||||
algorithm = Z_EROFS_COMPRESSION_LZ4;
|
||||
|
||||
err = z_erofs_vle_unzip_fast_percpu(compressed_pages, clusterpages,
|
||||
pages, llen, work->pageofs);
|
||||
if (err != -ENOTSUPP)
|
||||
goto out;
|
||||
|
||||
if (sparsemem_pages >= nr_pages)
|
||||
goto skip_allocpage;
|
||||
|
||||
for (i = 0; i < nr_pages; ++i) {
|
||||
if (pages[i])
|
||||
continue;
|
||||
|
||||
pages[i] = __stagingpage_alloc(page_pool, GFP_NOFS);
|
||||
}
|
||||
|
||||
skip_allocpage:
|
||||
vout = erofs_vmap(pages, nr_pages);
|
||||
if (!vout) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = z_erofs_vle_unzip_vmap(compressed_pages, clusterpages, vout,
|
||||
llen, work->pageofs, overlapped);
|
||||
|
||||
erofs_vunmap(vout, nr_pages);
|
||||
err = z_erofs_decompress(&(struct z_erofs_decompress_req) {
|
||||
.sb = sb,
|
||||
.in = compressed_pages,
|
||||
.out = pages,
|
||||
.pageofs_out = work->pageofs,
|
||||
.inputsize = PAGE_SIZE,
|
||||
.outputsize = outputsize,
|
||||
.alg = algorithm,
|
||||
.inplace_io = overlapped,
|
||||
.partial_decoding = partial
|
||||
}, page_pool);
|
||||
|
||||
out:
|
||||
/* must handle all compressed pages before endding pages */
|
||||
|
@ -1056,7 +1046,7 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
continue;
|
||||
|
||||
/* recycle all individual staging pages */
|
||||
(void)z_erofs_gather_if_stagingpage(page_pool, page);
|
||||
(void)z_erofs_put_stagingpage(page_pool, page);
|
||||
|
||||
WRITE_ONCE(compressed_pages[i], NULL);
|
||||
}
|
||||
|
@ -1069,7 +1059,7 @@ static int z_erofs_vle_unzip(struct super_block *sb,
|
|||
DBG_BUGON(!page->mapping);
|
||||
|
||||
/* recycle all individual staging pages */
|
||||
if (z_erofs_gather_if_stagingpage(page_pool, page))
|
||||
if (z_erofs_put_stagingpage(page_pool, page))
|
||||
continue;
|
||||
|
||||
if (unlikely(err < 0))
|
||||
|
@ -1273,8 +1263,7 @@ jobqueue_init(struct super_block *sb,
|
|||
goto out;
|
||||
}
|
||||
|
||||
iosb = kvzalloc(sizeof(struct z_erofs_vle_unzip_io_sb),
|
||||
GFP_KERNEL | __GFP_NOFAIL);
|
||||
iosb = kvzalloc(sizeof(*iosb), GFP_KERNEL | __GFP_NOFAIL);
|
||||
DBG_BUGON(!iosb);
|
||||
|
||||
/* initialize fields in the allocated descriptor */
|
||||
|
@ -1600,289 +1589,3 @@ const struct address_space_operations z_erofs_vle_normalaccess_aops = {
|
|||
.readpages = z_erofs_vle_normalaccess_readpages,
|
||||
};
|
||||
|
||||
/*
|
||||
* Variable-sized Logical Extent (Fixed Physical Cluster) Compression Mode
|
||||
* ---
|
||||
* VLE compression mode attempts to compress a number of logical data into
|
||||
* a physical cluster with a fixed size.
|
||||
* VLE compression mode uses "struct z_erofs_vle_decompressed_index".
|
||||
*/
|
||||
#define __vle_cluster_advise(x, bit, bits) \
|
||||
((le16_to_cpu(x) >> (bit)) & ((1 << (bits)) - 1))
|
||||
|
||||
#define __vle_cluster_type(advise) __vle_cluster_advise(advise, \
|
||||
Z_EROFS_VLE_DI_CLUSTER_TYPE_BIT, Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS)
|
||||
|
||||
#define vle_cluster_type(di) \
|
||||
__vle_cluster_type((di)->di_advise)
|
||||
|
||||
static int
|
||||
vle_decompressed_index_clusterofs(unsigned int *clusterofs,
|
||||
unsigned int clustersize,
|
||||
struct z_erofs_vle_decompressed_index *di)
|
||||
{
|
||||
switch (vle_cluster_type(di)) {
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:
|
||||
*clusterofs = clustersize;
|
||||
break;
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:
|
||||
*clusterofs = le16_to_cpu(di->di_clusterofs);
|
||||
break;
|
||||
default:
|
||||
DBG_BUGON(1);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline erofs_blk_t
|
||||
vle_extent_blkaddr(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct erofs_sb_info *sbi = EROFS_I_SB(inode);
|
||||
struct erofs_vnode *vi = EROFS_V(inode);
|
||||
|
||||
unsigned int ofs = Z_EROFS_VLE_EXTENT_ALIGN(vi->inode_isize +
|
||||
vi->xattr_isize) + sizeof(struct erofs_extent_header) +
|
||||
index * sizeof(struct z_erofs_vle_decompressed_index);
|
||||
|
||||
return erofs_blknr(iloc(sbi, vi->nid) + ofs);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
vle_extent_blkoff(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct erofs_sb_info *sbi = EROFS_I_SB(inode);
|
||||
struct erofs_vnode *vi = EROFS_V(inode);
|
||||
|
||||
unsigned int ofs = Z_EROFS_VLE_EXTENT_ALIGN(vi->inode_isize +
|
||||
vi->xattr_isize) + sizeof(struct erofs_extent_header) +
|
||||
index * sizeof(struct z_erofs_vle_decompressed_index);
|
||||
|
||||
return erofs_blkoff(iloc(sbi, vi->nid) + ofs);
|
||||
}
|
||||
|
||||
struct vle_map_blocks_iter_ctx {
|
||||
struct inode *inode;
|
||||
struct super_block *sb;
|
||||
unsigned int clusterbits;
|
||||
|
||||
struct page **mpage_ret;
|
||||
void **kaddr_ret;
|
||||
};
|
||||
|
||||
static int
|
||||
vle_get_logical_extent_head(const struct vle_map_blocks_iter_ctx *ctx,
|
||||
unsigned int lcn, /* logical cluster number */
|
||||
unsigned long long *ofs,
|
||||
erofs_blk_t *pblk,
|
||||
unsigned int *flags)
|
||||
{
|
||||
const unsigned int clustersize = 1 << ctx->clusterbits;
|
||||
const erofs_blk_t mblk = vle_extent_blkaddr(ctx->inode, lcn);
|
||||
struct page *mpage = *ctx->mpage_ret; /* extent metapage */
|
||||
|
||||
struct z_erofs_vle_decompressed_index *di;
|
||||
unsigned int cluster_type, delta0;
|
||||
|
||||
if (mpage->index != mblk) {
|
||||
kunmap_atomic(*ctx->kaddr_ret);
|
||||
unlock_page(mpage);
|
||||
put_page(mpage);
|
||||
|
||||
mpage = erofs_get_meta_page(ctx->sb, mblk, false);
|
||||
if (IS_ERR(mpage)) {
|
||||
*ctx->mpage_ret = NULL;
|
||||
return PTR_ERR(mpage);
|
||||
}
|
||||
*ctx->mpage_ret = mpage;
|
||||
*ctx->kaddr_ret = kmap_atomic(mpage);
|
||||
}
|
||||
|
||||
di = *ctx->kaddr_ret + vle_extent_blkoff(ctx->inode, lcn);
|
||||
|
||||
cluster_type = vle_cluster_type(di);
|
||||
switch (cluster_type) {
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:
|
||||
delta0 = le16_to_cpu(di->di_u.delta[0]);
|
||||
if (unlikely(!delta0 || delta0 > lcn)) {
|
||||
errln("invalid NONHEAD dl0 %u at lcn %u of nid %llu",
|
||||
delta0, lcn, EROFS_V(ctx->inode)->nid);
|
||||
DBG_BUGON(1);
|
||||
return -EIO;
|
||||
}
|
||||
return vle_get_logical_extent_head(ctx,
|
||||
lcn - delta0, ofs, pblk, flags);
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:
|
||||
*flags ^= EROFS_MAP_ZIPPED;
|
||||
/* fallthrough */
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:
|
||||
/* clustersize should be a power of two */
|
||||
*ofs = ((u64)lcn << ctx->clusterbits) +
|
||||
(le16_to_cpu(di->di_clusterofs) & (clustersize - 1));
|
||||
*pblk = le32_to_cpu(di->di_u.blkaddr);
|
||||
break;
|
||||
default:
|
||||
errln("unknown cluster type %u at lcn %u of nid %llu",
|
||||
cluster_type, lcn, EROFS_V(ctx->inode)->nid);
|
||||
DBG_BUGON(1);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int z_erofs_map_blocks_iter(struct inode *inode,
|
||||
struct erofs_map_blocks *map,
|
||||
int flags)
|
||||
{
|
||||
void *kaddr;
|
||||
const struct vle_map_blocks_iter_ctx ctx = {
|
||||
.inode = inode,
|
||||
.sb = inode->i_sb,
|
||||
.clusterbits = EROFS_I_SB(inode)->clusterbits,
|
||||
.mpage_ret = &map->mpage,
|
||||
.kaddr_ret = &kaddr
|
||||
};
|
||||
const unsigned int clustersize = 1 << ctx.clusterbits;
|
||||
/* if both m_(l,p)len are 0, regularize l_lblk, l_lofs, etc... */
|
||||
const bool initial = !map->m_llen;
|
||||
|
||||
/* logicial extent (start, end) offset */
|
||||
unsigned long long ofs, end;
|
||||
unsigned int lcn;
|
||||
u32 ofs_rem;
|
||||
|
||||
/* initialize `pblk' to keep gcc from printing foolish warnings */
|
||||
erofs_blk_t mblk, pblk = 0;
|
||||
struct page *mpage = map->mpage;
|
||||
struct z_erofs_vle_decompressed_index *di;
|
||||
unsigned int cluster_type, logical_cluster_ofs;
|
||||
int err = 0;
|
||||
|
||||
trace_z_erofs_map_blocks_iter_enter(inode, map, flags);
|
||||
|
||||
/* when trying to read beyond EOF, leave it unmapped */
|
||||
if (unlikely(map->m_la >= inode->i_size)) {
|
||||
DBG_BUGON(!initial);
|
||||
map->m_llen = map->m_la + 1 - inode->i_size;
|
||||
map->m_la = inode->i_size;
|
||||
map->m_flags = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
debugln("%s, m_la %llu m_llen %llu --- start", __func__,
|
||||
map->m_la, map->m_llen);
|
||||
|
||||
ofs = map->m_la + map->m_llen;
|
||||
|
||||
/* clustersize should be power of two */
|
||||
lcn = ofs >> ctx.clusterbits;
|
||||
ofs_rem = ofs & (clustersize - 1);
|
||||
|
||||
mblk = vle_extent_blkaddr(inode, lcn);
|
||||
|
||||
if (!mpage || mpage->index != mblk) {
|
||||
if (mpage)
|
||||
put_page(mpage);
|
||||
|
||||
mpage = erofs_get_meta_page(ctx.sb, mblk, false);
|
||||
if (IS_ERR(mpage)) {
|
||||
err = PTR_ERR(mpage);
|
||||
goto out;
|
||||
}
|
||||
map->mpage = mpage;
|
||||
} else {
|
||||
lock_page(mpage);
|
||||
DBG_BUGON(!PageUptodate(mpage));
|
||||
}
|
||||
|
||||
kaddr = kmap_atomic(mpage);
|
||||
di = kaddr + vle_extent_blkoff(inode, lcn);
|
||||
|
||||
debugln("%s, lcn %u mblk %u e_blkoff %u", __func__, lcn,
|
||||
mblk, vle_extent_blkoff(inode, lcn));
|
||||
|
||||
err = vle_decompressed_index_clusterofs(&logical_cluster_ofs,
|
||||
clustersize, di);
|
||||
if (unlikely(err))
|
||||
goto unmap_out;
|
||||
|
||||
if (!initial) {
|
||||
/* [walking mode] 'map' has been already initialized */
|
||||
map->m_llen += logical_cluster_ofs;
|
||||
goto unmap_out;
|
||||
}
|
||||
|
||||
/* by default, compressed */
|
||||
map->m_flags |= EROFS_MAP_ZIPPED;
|
||||
|
||||
end = ((u64)lcn + 1) * clustersize;
|
||||
|
||||
cluster_type = vle_cluster_type(di);
|
||||
|
||||
switch (cluster_type) {
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN:
|
||||
if (ofs_rem >= logical_cluster_ofs)
|
||||
map->m_flags ^= EROFS_MAP_ZIPPED;
|
||||
/* fallthrough */
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_HEAD:
|
||||
if (ofs_rem == logical_cluster_ofs) {
|
||||
pblk = le32_to_cpu(di->di_u.blkaddr);
|
||||
goto exact_hitted;
|
||||
}
|
||||
|
||||
if (ofs_rem > logical_cluster_ofs) {
|
||||
ofs = (u64)lcn * clustersize | logical_cluster_ofs;
|
||||
pblk = le32_to_cpu(di->di_u.blkaddr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* logical cluster number should be >= 1 */
|
||||
if (unlikely(!lcn)) {
|
||||
errln("invalid logical cluster 0 at nid %llu",
|
||||
EROFS_V(inode)->nid);
|
||||
err = -EIO;
|
||||
goto unmap_out;
|
||||
}
|
||||
end = ((u64)lcn-- * clustersize) | logical_cluster_ofs;
|
||||
/* fallthrough */
|
||||
case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD:
|
||||
/* get the correspoinding first chunk */
|
||||
err = vle_get_logical_extent_head(&ctx, lcn, &ofs,
|
||||
&pblk, &map->m_flags);
|
||||
mpage = map->mpage;
|
||||
|
||||
if (unlikely(err)) {
|
||||
if (mpage)
|
||||
goto unmap_out;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errln("unknown cluster type %u at offset %llu of nid %llu",
|
||||
cluster_type, ofs, EROFS_V(inode)->nid);
|
||||
err = -EIO;
|
||||
goto unmap_out;
|
||||
}
|
||||
|
||||
map->m_la = ofs;
|
||||
exact_hitted:
|
||||
map->m_llen = end - ofs;
|
||||
map->m_plen = clustersize;
|
||||
map->m_pa = blknr_to_addr(pblk);
|
||||
map->m_flags |= EROFS_MAP_MAPPED;
|
||||
unmap_out:
|
||||
kunmap_atomic(kaddr);
|
||||
unlock_page(mpage);
|
||||
out:
|
||||
debugln("%s, m_la %llu m_pa %llu m_llen %llu m_plen %llu m_flags 0%o",
|
||||
__func__, map->m_la, map->m_pa,
|
||||
map->m_llen, map->m_plen, map->m_flags);
|
||||
|
||||
trace_z_erofs_map_blocks_iter_exit(inode, map, flags, err);
|
||||
|
||||
/* aggressively BUG_ON iff CONFIG_EROFS_FS_DEBUG is on */
|
||||
DBG_BUGON(err < 0 && err != -ENOMEM);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,25 +16,7 @@
|
|||
#include "internal.h"
|
||||
#include "unzip_pagevec.h"
|
||||
|
||||
/*
|
||||
* - 0x5A110C8D ('sallocated', Z_EROFS_MAPPING_STAGING) -
|
||||
* used for temporary allocated pages (via erofs_allocpage),
|
||||
* in order to seperate those from NULL mapping (eg. truncated pages)
|
||||
*/
|
||||
#define Z_EROFS_MAPPING_STAGING ((void *)0x5A110C8D)
|
||||
|
||||
#define z_erofs_is_stagingpage(page) \
|
||||
((page)->mapping == Z_EROFS_MAPPING_STAGING)
|
||||
|
||||
static inline bool z_erofs_gather_if_stagingpage(struct list_head *page_pool,
|
||||
struct page *page)
|
||||
{
|
||||
if (z_erofs_is_stagingpage(page)) {
|
||||
list_add(&page->lru, page_pool);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#define Z_EROFS_NR_INLINE_PAGEVECS 3
|
||||
|
||||
/*
|
||||
* Structure fields follow one of the following exclusion rules.
|
||||
|
@ -44,8 +26,6 @@ static inline bool z_erofs_gather_if_stagingpage(struct list_head *page_pool,
|
|||
*
|
||||
*/
|
||||
|
||||
#define Z_EROFS_VLE_INLINE_PAGEVECS 3
|
||||
|
||||
struct z_erofs_vle_work {
|
||||
struct mutex lock;
|
||||
|
||||
|
@ -58,7 +38,7 @@ struct z_erofs_vle_work {
|
|||
|
||||
union {
|
||||
/* L: pagevec */
|
||||
erofs_vtptr_t pagevec[Z_EROFS_VLE_INLINE_PAGEVECS];
|
||||
erofs_vtptr_t pagevec[Z_EROFS_NR_INLINE_PAGEVECS];
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
};
|
||||
|
@ -66,6 +46,7 @@ struct z_erofs_vle_work {
|
|||
#define Z_EROFS_VLE_WORKGRP_FMT_PLAIN 0
|
||||
#define Z_EROFS_VLE_WORKGRP_FMT_LZ4 1
|
||||
#define Z_EROFS_VLE_WORKGRP_FMT_MASK 1
|
||||
#define Z_EROFS_VLE_WORKGRP_FULL_LENGTH 2
|
||||
|
||||
typedef void *z_erofs_vle_owned_workgrp_t;
|
||||
|
||||
|
@ -147,7 +128,7 @@ static inline unsigned z_erofs_onlinepage_index(struct page *page)
|
|||
{
|
||||
union z_erofs_onlinepage_converter u;
|
||||
|
||||
BUG_ON(!PagePrivate(page));
|
||||
DBG_BUGON(!PagePrivate(page));
|
||||
u.v = &page_private(page);
|
||||
|
||||
return atomic_read(u.o) >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT;
|
||||
|
@ -179,7 +160,7 @@ static inline void z_erofs_onlinepage_fixup(struct page *page,
|
|||
if (!index)
|
||||
return;
|
||||
|
||||
BUG_ON(id != index);
|
||||
DBG_BUGON(id != index);
|
||||
}
|
||||
|
||||
v = (index << Z_EROFS_ONLINEPAGE_INDEX_SHIFT) |
|
||||
|
@ -193,7 +174,7 @@ static inline void z_erofs_onlinepage_endio(struct page *page)
|
|||
union z_erofs_onlinepage_converter u;
|
||||
unsigned v;
|
||||
|
||||
BUG_ON(!PagePrivate(page));
|
||||
DBG_BUGON(!PagePrivate(page));
|
||||
u.v = &page_private(page);
|
||||
|
||||
v = atomic_dec_return(u.o);
|
||||
|
@ -211,18 +192,5 @@ static inline void z_erofs_onlinepage_endio(struct page *page)
|
|||
min_t(unsigned int, THREAD_SIZE / 8 / sizeof(struct page *), 96U)
|
||||
#define Z_EROFS_VLE_VMAP_GLOBAL_PAGES 2048
|
||||
|
||||
/* unzip_vle_lz4.c */
|
||||
int z_erofs_vle_plain_copy(struct page **compressed_pages,
|
||||
unsigned int clusterpages, struct page **pages,
|
||||
unsigned int nr_pages, unsigned short pageofs);
|
||||
int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages,
|
||||
unsigned int clusterpages,
|
||||
struct page **pages, unsigned int outlen,
|
||||
unsigned short pageofs);
|
||||
int z_erofs_vle_unzip_vmap(struct page **compressed_pages,
|
||||
unsigned int clusterpages,
|
||||
void *vaddr, unsigned int llen,
|
||||
unsigned short pageofs, bool overlapped);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* linux/drivers/staging/erofs/unzip_vle_lz4.c
|
||||
*
|
||||
* Copyright (C) 2018 HUAWEI, Inc.
|
||||
* http://www.huawei.com/
|
||||
* Created by Gao Xiang <gaoxiang25@huawei.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of the Linux
|
||||
* distribution for more details.
|
||||
*/
|
||||
#include "unzip_vle.h"
|
||||
#include <linux/lz4.h>
|
||||
|
||||
static int z_erofs_unzip_lz4(void *in, void *out, size_t inlen, size_t outlen)
|
||||
{
|
||||
int ret = LZ4_decompress_safe_partial(in, out, inlen, outlen, outlen);
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* LZ4_decompress_safe_partial will return an error code
|
||||
* (< 0) if decompression failed
|
||||
*/
|
||||
errln("%s, failed to decompress, in[%p, %zu] outlen[%p, %zu]",
|
||||
__func__, in, inlen, out, outlen);
|
||||
WARN_ON(1);
|
||||
print_hex_dump(KERN_DEBUG, "raw data [in]: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, in, inlen, true);
|
||||
print_hex_dump(KERN_DEBUG, "raw data [out]: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, out, outlen, true);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if Z_EROFS_CLUSTER_MAX_PAGES > Z_EROFS_VLE_INLINE_PAGEVECS
|
||||
#define EROFS_PERCPU_NR_PAGES Z_EROFS_CLUSTER_MAX_PAGES
|
||||
#else
|
||||
#define EROFS_PERCPU_NR_PAGES Z_EROFS_VLE_INLINE_PAGEVECS
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
char data[PAGE_SIZE * EROFS_PERCPU_NR_PAGES];
|
||||
} erofs_pcpubuf[NR_CPUS];
|
||||
|
||||
int z_erofs_vle_plain_copy(struct page **compressed_pages,
|
||||
unsigned int clusterpages,
|
||||
struct page **pages,
|
||||
unsigned int nr_pages,
|
||||
unsigned short pageofs)
|
||||
{
|
||||
unsigned int i, j;
|
||||
void *src = NULL;
|
||||
const unsigned int righthalf = PAGE_SIZE - pageofs;
|
||||
char *percpu_data;
|
||||
bool mirrored[Z_EROFS_CLUSTER_MAX_PAGES] = { 0 };
|
||||
|
||||
preempt_disable();
|
||||
percpu_data = erofs_pcpubuf[smp_processor_id()].data;
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < nr_pages; j = i++) {
|
||||
struct page *page = pages[i];
|
||||
void *dst;
|
||||
|
||||
if (!page) {
|
||||
if (src) {
|
||||
if (!mirrored[j])
|
||||
kunmap_atomic(src);
|
||||
src = NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
dst = kmap_atomic(page);
|
||||
|
||||
for (; j < clusterpages; ++j) {
|
||||
if (compressed_pages[j] != page)
|
||||
continue;
|
||||
|
||||
DBG_BUGON(mirrored[j]);
|
||||
memcpy(percpu_data + j * PAGE_SIZE, dst, PAGE_SIZE);
|
||||
mirrored[j] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
if (!src)
|
||||
src = mirrored[i - 1] ?
|
||||
percpu_data + (i - 1) * PAGE_SIZE :
|
||||
kmap_atomic(compressed_pages[i - 1]);
|
||||
|
||||
memcpy(dst, src + righthalf, pageofs);
|
||||
|
||||
if (!mirrored[i - 1])
|
||||
kunmap_atomic(src);
|
||||
|
||||
if (unlikely(i >= clusterpages)) {
|
||||
kunmap_atomic(dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!righthalf) {
|
||||
src = NULL;
|
||||
} else {
|
||||
src = mirrored[i] ? percpu_data + i * PAGE_SIZE :
|
||||
kmap_atomic(compressed_pages[i]);
|
||||
|
||||
memcpy(dst + pageofs, src, righthalf);
|
||||
}
|
||||
|
||||
kunmap_atomic(dst);
|
||||
}
|
||||
|
||||
if (src && !mirrored[j])
|
||||
kunmap_atomic(src);
|
||||
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages,
|
||||
unsigned int clusterpages,
|
||||
struct page **pages,
|
||||
unsigned int outlen,
|
||||
unsigned short pageofs)
|
||||
{
|
||||
void *vin, *vout;
|
||||
unsigned int nr_pages, i, j;
|
||||
int ret;
|
||||
|
||||
if (outlen + pageofs > EROFS_PERCPU_NR_PAGES * PAGE_SIZE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
nr_pages = DIV_ROUND_UP(outlen + pageofs, PAGE_SIZE);
|
||||
|
||||
if (clusterpages == 1) {
|
||||
vin = kmap_atomic(compressed_pages[0]);
|
||||
} else {
|
||||
vin = erofs_vmap(compressed_pages, clusterpages);
|
||||
if (!vin)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
preempt_disable();
|
||||
vout = erofs_pcpubuf[smp_processor_id()].data;
|
||||
|
||||
ret = z_erofs_unzip_lz4(vin, vout + pageofs,
|
||||
clusterpages * PAGE_SIZE, outlen);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = 0;
|
||||
|
||||
for (i = 0; i < nr_pages; ++i) {
|
||||
j = min((unsigned int)PAGE_SIZE - pageofs, outlen);
|
||||
|
||||
if (pages[i]) {
|
||||
if (clusterpages == 1 &&
|
||||
pages[i] == compressed_pages[0]) {
|
||||
memcpy(vin + pageofs, vout + pageofs, j);
|
||||
} else {
|
||||
void *dst = kmap_atomic(pages[i]);
|
||||
|
||||
memcpy(dst + pageofs, vout + pageofs, j);
|
||||
kunmap_atomic(dst);
|
||||
}
|
||||
}
|
||||
vout += PAGE_SIZE;
|
||||
outlen -= j;
|
||||
pageofs = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
preempt_enable();
|
||||
|
||||
if (clusterpages == 1)
|
||||
kunmap_atomic(vin);
|
||||
else
|
||||
erofs_vunmap(vin, clusterpages);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int z_erofs_vle_unzip_vmap(struct page **compressed_pages,
|
||||
unsigned int clusterpages,
|
||||
void *vout,
|
||||
unsigned int llen,
|
||||
unsigned short pageofs,
|
||||
bool overlapped)
|
||||
{
|
||||
void *vin;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (overlapped) {
|
||||
preempt_disable();
|
||||
vin = erofs_pcpubuf[smp_processor_id()].data;
|
||||
|
||||
for (i = 0; i < clusterpages; ++i) {
|
||||
void *t = kmap_atomic(compressed_pages[i]);
|
||||
|
||||
memcpy(vin + PAGE_SIZE * i, t, PAGE_SIZE);
|
||||
kunmap_atomic(t);
|
||||
}
|
||||
} else if (clusterpages == 1) {
|
||||
vin = kmap_atomic(compressed_pages[0]);
|
||||
} else {
|
||||
vin = erofs_vmap(compressed_pages, clusterpages);
|
||||
}
|
||||
|
||||
ret = z_erofs_unzip_lz4(vin, vout + pageofs,
|
||||
clusterpages * PAGE_SIZE, llen);
|
||||
if (ret > 0)
|
||||
ret = 0;
|
||||
|
||||
if (!overlapped) {
|
||||
if (clusterpages == 1)
|
||||
kunmap_atomic(vin);
|
||||
else
|
||||
erofs_vunmap(vin, clusterpages);
|
||||
} else {
|
||||
preempt_enable();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -27,6 +27,18 @@ struct page *erofs_allocpage(struct list_head *pool, gfp_t gfp)
|
|||
return page;
|
||||
}
|
||||
|
||||
#if (EROFS_PCPUBUF_NR_PAGES > 0)
|
||||
static struct {
|
||||
u8 data[PAGE_SIZE * EROFS_PCPUBUF_NR_PAGES];
|
||||
} ____cacheline_aligned_in_smp erofs_pcpubuf[NR_CPUS];
|
||||
|
||||
void *erofs_get_pcpubuf(unsigned int pagenr)
|
||||
{
|
||||
preempt_disable();
|
||||
return &erofs_pcpubuf[smp_processor_id()].data[pagenr * PAGE_SIZE];
|
||||
}
|
||||
#endif
|
||||
|
||||
/* global shrink count (for all mounted EROFS instances) */
|
||||
static atomic_long_t erofs_global_shrink_cnt;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue