1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

Char/Misc/IIO driver updates for 6.14-rc1

Here is the "big" set of char/misc/iio and other smaller driver
 subsystem updates for 6.14-rc1.  Loads of different things in here this
 development cycle, highlights are:
   - ntsync "driver" to handle Windows locking types enabling Wine to
     work much better on many workloads (i.e. games).  The driver
     framework was in 6.13, but now it's enabled and fully working
     properly.  Should make many SteamOS users happy.  Even comes with
     tests!
   - Large IIO driver updates and bugfixes
   - FPGA driver updates
   - Coresight driver updates
   - MHI driver updates
   - PPS driver updatesa
   - const bin_attribute reworking for many drivers
   - binder driver updates
   - smaller driver updates and fixes
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ5fGOQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynatACeLlbkhUT544Va1eOL2TkjfcGxrZUAoJ3ymGC0
 y0N7/+fWL6aS+b4sEilv
 =TU0D
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull Char/Misc/IIO driver updates from Greg KH:
 "Here is the "big" set of char/misc/iio and other smaller driver
  subsystem updates for 6.14-rc1. Loads of different things in here this
  development cycle, highlights are:

   - ntsync "driver" to handle Windows locking types enabling Wine to
     work much better on many workloads (i.e. games). The driver
     framework was in 6.13, but now it's enabled and fully working
     properly. Should make many SteamOS users happy. Even comes with
     tests!

   - Large IIO driver updates and bugfixes

   - FPGA driver updates

   - Coresight driver updates

   - MHI driver updates

   - PPS driver updatesa

   - const bin_attribute reworking for many drivers

   - binder driver updates

   - smaller driver updates and fixes

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (311 commits)
  ntsync: Fix reference leaks in the remaining create ioctls.
  spmi: hisi-spmi-controller: Drop duplicated OF node assignment in spmi_controller_probe()
  spmi: Set fwnode for spmi devices
  ntsync: fix a file reference leak in drivers/misc/ntsync.c
  scripts/tags.sh: Don't tag usages of DECLARE_BITMAP
  dt-bindings: interconnect: qcom,msm8998-bwmon: Add SM8750 CPU BWMONs
  dt-bindings: interconnect: OSM L3: Document sm8650 OSM L3 compatible
  dt-bindings: interconnect: qcom-bwmon: Document QCS615 bwmon compatibles
  interconnect: sm8750: Add missing const to static qcom_icc_desc
  memstick: core: fix kernel-doc notation
  intel_th: core: fix kernel-doc warnings
  binder: log transaction code on failure
  iio: dac: ad3552r-hs: clear reset status flag
  iio: dac: ad3552r-common: fix ad3541/2r ranges
  iio: chemical: bme680: Fix uninitialized variable in __bme680_read_raw()
  misc: fastrpc: Fix copy buffer page size
  misc: fastrpc: Fix registered buffer page address
  misc: fastrpc: Deregister device nodes properly in error scenarios
  nvmem: core: improve range check for nvmem_cell_write()
  nvmem: qcom-spmi-sdam: Set size in struct nvmem_config
  ...
This commit is contained in:
Linus Torvalds 2025-01-27 16:51:51 -08:00
commit 13845bdc86
300 changed files with 12442 additions and 4550 deletions

View file

@ -0,0 +1,15 @@
What: /sys/bus/coresight/devices/dummy_source<N>/enable_source
Date: Dec 2024
KernelVersion: 6.14
Contact: Mao Jinlong <quic_jinlmao@quicinc.com>
Description: (RW) Enable/disable tracing of dummy source. A sink should be activated
before enabling the source. The path of coresight components linking
the source to the sink is configured and managed automatically by the
coresight framework.
What: /sys/bus/coresight/devices/dummy_source<N>/traceid
Date: Dec 2024
KernelVersion: 6.14
Contact: Mao Jinlong <quic_jinlmao@quicinc.com>
Description: (R) Show the trace ID that will appear in the trace stream
coming from this trace entity.

View file

@ -168,18 +168,6 @@ Description:
is required is a consistent labeling. Units after application
of scale and offset are millivolts.
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw
KernelVersion: 3.17
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no bias removal etc.) current measurement from
channel Y. In special cases where the channel does not
correspond to externally available input one of the named
versions may be used. The number must always be specified and
unique to allow association with event codes. Units after
application of scale and offset are milliamps.
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_raw
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
@ -227,7 +215,7 @@ Description:
same scaling as _raw.
What: /sys/bus/iio/devices/iio:deviceX/in_temp_raw
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_temp_x_raw
What: /sys/bus/iio/devices/iio:deviceX/in_temp_y_raw
What: /sys/bus/iio/devices/iio:deviceX/in_temp_ambient_raw
@ -416,11 +404,11 @@ Contact: linux-iio@vger.kernel.org
Description:
Scaled humidity measurement in milli percent.
What: /sys/bus/iio/devices/iio:deviceX/in_X_mean_raw
What: /sys/bus/iio/devices/iio:deviceX/in_Y_mean_raw
KernelVersion: 3.5
Contact: linux-iio@vger.kernel.org
Description:
Averaged raw measurement from channel X. The number of values
Averaged raw measurement from channel Y. The number of values
used for averaging is device specific. The converting rules for
normal raw values also applies to the averaged raw values.
@ -448,7 +436,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset
What: /sys/bus/iio/devices/iio:deviceX/in_magn_offset
What: /sys/bus/iio/devices/iio:deviceX/in_rot_offset
What: /sys/bus/iio/devices/iio:deviceX/in_angl_offset
What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceX_offset
What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceY_offset
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -508,6 +496,9 @@ What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_x_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_y_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_z_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
@ -660,10 +651,10 @@ What: /sys/.../iio:deviceX/in_magn_scale_available
What: /sys/.../iio:deviceX/in_illuminance_scale_available
What: /sys/.../iio:deviceX/in_intensity_scale_available
What: /sys/.../iio:deviceX/in_proximity_scale_available
What: /sys/.../iio:deviceX/in_voltageX_scale_available
What: /sys/.../iio:deviceX/in_voltageY_scale_available
What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
What: /sys/.../iio:deviceX/out_voltageX_scale_available
What: /sys/.../iio:deviceX/out_altvoltageX_scale_available
What: /sys/.../iio:deviceX/out_voltageY_scale_available
What: /sys/.../iio:deviceX/out_altvoltageY_scale_available
What: /sys/.../iio:deviceX/in_capacitance_scale_available
What: /sys/.../iio:deviceX/in_pressure_scale_available
What: /sys/.../iio:deviceX/in_pressureY_scale_available
@ -681,6 +672,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_hardwaregain
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_hardwaregain
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_hardwaregain
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_clear_hardwaregain
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_hardwaregain
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -1562,7 +1554,7 @@ Description:
This attribute is used to read the amount of quadrature error
present in the device at a given time.
What: /sys/.../iio:deviceX/in_accelX_power_mode
What: /sys/.../iio:deviceX/in_accelY_power_mode
KernelVersion: 3.11
Contact: linux-iio@vger.kernel.org
Description:
@ -1633,6 +1625,10 @@ What: /sys/.../iio:deviceX/in_intensityY_uv_raw
What: /sys/.../iio:deviceX/in_intensityY_uva_raw
What: /sys/.../iio:deviceX/in_intensityY_uvb_raw
What: /sys/.../iio:deviceX/in_intensityY_duv_raw
What: /sys/.../iio:deviceX/in_intensity_red_raw
What: /sys/.../iio:deviceX/in_intensity_green_raw
What: /sys/.../iio:deviceX/in_intensity_blue_raw
What: /sys/.../iio:deviceX/in_intensity_clear_raw
KernelVersion: 3.4
Contact: linux-iio@vger.kernel.org
Description:
@ -1691,16 +1687,19 @@ Description:
Raw value of rotation from true/magnetic north measured with
or without compensation from tilt sensors.
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_q_raw
KernelVersion: 3.18
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_raw
KernelVersion: 3.17
Contact: linux-iio@vger.kernel.org
Description:
Raw current measurement from channel X. Units are in milliamps
Raw current measurement from channel Y. Units are in milliamps
after application of scale and offset. If no offset or scale is
present, output should be considered as processed with the
unit in milliamps.
unit in milliamps. In special cases where the channel does not
correspond to externally available input one of the named
versions may be used.
Channels with 'i' and 'q' modifiers always exist in pairs and both
channels refer to the same signal. The 'i' channel contains the in-phase
@ -1864,9 +1863,9 @@ Description:
hardware fifo watermark level.
What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_temp_object_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_object_calibemissivity
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_object_calibemissivity
KernelVersion: 4.1
Contact: linux-iio@vger.kernel.org
Description:
@ -1887,17 +1886,17 @@ Description:
is considered as one sample for <type>[_name]_sampling_frequency.
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationY_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_ethanol_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_ethanol_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationY_ethanol_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_h2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_h2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationY_h2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_o2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_o2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationY_o2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationY_voc_raw
KernelVersion: 4.3
Contact: linux-iio@vger.kernel.org
Description:
@ -1905,9 +1904,9 @@ Description:
after application of scale and offset are percents.
What: /sys/bus/iio/devices/iio:deviceX/in_resistance_raw
What: /sys/bus/iio/devices/iio:deviceX/in_resistanceX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_resistanceY_raw
What: /sys/bus/iio/devices/iio:deviceX/out_resistance_raw
What: /sys/bus/iio/devices/iio:deviceX/out_resistanceX_raw
What: /sys/bus/iio/devices/iio:deviceX/out_resistanceY_raw
KernelVersion: 4.3
Contact: linux-iio@vger.kernel.org
Description:
@ -2096,7 +2095,7 @@ Description:
One of the following thermocouple types: B, E, J, K, N, R, S, T.
What: /sys/bus/iio/devices/iio:deviceX/in_temp_object_calibambient
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_object_calibambient
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_object_calibambient
KernelVersion: 5.10
Contact: linux-iio@vger.kernel.org
Description:
@ -2172,9 +2171,9 @@ Description:
- a range specified as "[min step max]"
What: /sys/bus/iio/devices/iio:deviceX/in_voltageX_sampling_frequency
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sampling_frequency
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_sampling_frequency
What: /sys/bus/iio/devices/iio:deviceX/in_currentZ_sampling_frequency
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_sampling_frequency
KernelVersion: 5.20
Contact: linux-iio@vger.kernel.org
Description:

View file

@ -0,0 +1,23 @@
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sys_calibration
KernelVersion: 5.5
Contact: linux-iio@vger.kernel.org
Description:
This attribute, if available, initiates the system calibration procedure. This is done on a
single channel at a time. Write '1' to start the calibration.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sys_calibration_mode_available
KernelVersion: 5.5
Contact: linux-iio@vger.kernel.org
Description:
This attribute, if available, returns a list with the possible calibration modes.
There are two available options:
"zero_scale" - calibrate to zero scale
"full_scale" - calibrate to full scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sys_calibration_mode
KernelVersion: 5.5
Contact: linux-iio@vger.kernel.org
Description:
This attribute, if available, sets up the calibration mode used in the system calibration
procedure. Reading returns the current calibration mode.
Writing sets the system calibration mode.

View file

@ -19,33 +19,9 @@ Description:
the bridge can be disconnected (when it is not being used
using the bridge_switch_en attribute.
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Initiates the system calibration procedure. This is done on a
single channel at a time. Write '1' to start the calibration.
What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Measure voltage from AIN2 pin connected to AIN(+)
and AIN(-) shorted.
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode_available
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Reading returns a list with the possible calibration modes.
There are two available options:
"zero_scale" - calibrate to zero scale
"full_scale" - calibrate to full scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Sets up the calibration mode used in the system calibration
procedure. Reading returns the current calibration mode.
Writing sets the system calibration mode.

View file

@ -0,0 +1,43 @@
What: /sys/class/pps-gen/
Date: February 2025
KernelVersion: 6.13
Contact: Rodolfo Giometti <giometti@enneenne.com>
Description:
The /sys/class/pps-gen/ directory contains files and
directories that provide a unified interface to the PPS
generators.
What: /sys/class/pps-gen/pps-genX/
Date: February 2025
KernelVersion: 6.13
Contact: Rodolfo Giometti <giometti@enneenne.com>
Description:
The /sys/class/pps-gen/pps-genX/ directory is related to X-th
PPS generator in the system. Each directory contain files to
manage and control its PPS generator.
What: /sys/class/pps-gen/pps-genX/enable
Date: February 2025
KernelVersion: 6.13
Contact: Rodolfo Giometti <giometti@enneenne.com>
Description:
This write-only file enables or disables generation of the
PPS signal.
What: /sys/class/pps-gen/pps-genX/system
Date: February 2025
KernelVersion: 6.13
Contact: Rodolfo Giometti <giometti@enneenne.com>
Description:
This read-only file returns "1" if the generator takes the
timing from the system clock, while it returns "0" if not
(i.e. from a peripheral device clock).
What: /sys/class/pps-gen/pps-genX/time
Date: February 2025
KernelVersion: 6.13
Contact: Rodolfo Giometti <giometti@enneenne.com>
Description:
This read-only file contains the current time stored into the
generator clock as two integers representing the current time
seconds and nanoseconds.

View file

@ -38,6 +38,12 @@ properties:
enum:
- arm,coresight-dummy-source
arm,static-trace-id:
description: If dummy source needs static id support, use this to set trace id.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 111
out-ports:
$ref: /schemas/graph.yaml#/properties/ports

View file

@ -45,7 +45,22 @@ properties:
patternProperties:
'^port@[01]$':
description: Output connections to CoreSight Trace bus
$ref: /schemas/graph.yaml#/properties/port
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
properties:
endpoint:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
unevaluatedProperties: false
properties:
filter-source:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle to the coresight trace source device matching the
hard coded filtering for this port
remote-endpoint: true
required:
- compatible
@ -72,6 +87,7 @@ examples:
reg = <0>;
replicator_out_port0: endpoint {
remote-endpoint = <&etb_in_port>;
filter-source = <&tpdm_video>;
};
};
@ -79,6 +95,7 @@ examples:
reg = <1>;
replicator_out_port1: endpoint {
remote-endpoint = <&tpiu_in_port>;
filter-source = <&tpdm_mdss>;
};
};
};

View file

@ -37,10 +37,17 @@ properties:
interrupts:
maxItems: 1
interrupt-names:
items:
- enum: [INT1, INT2]
dependencies:
interrupts: [ interrupt-names ]
interrupt-names: [ interrupts ]
required:
- compatible
- reg
- interrupts
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
@ -61,6 +68,7 @@ examples:
reg = <0x2a>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT1";
};
};
- |
@ -79,5 +87,6 @@ examples:
spi-cpha;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT2";
};
};

View file

@ -4,23 +4,26 @@
$id: http://devicetree.org/schemas/iio/accel/kionix,kx022a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM/Kionix KX022A, KX132-1211 and KX132ACR-LBZ Accelerometers
title: ROHM/Kionix KX022A, KX132/134-1211 and KX132/134ACR-LBZ Accelerometers
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
KX022A, KX132ACR-LBZ and KX132-1211 are 3-axis accelerometers supporting
+/- 2G, 4G, 8G and 16G ranges, variable output data-rates and a
hardware-fifo buffering. These accelerometers can be accessed either
via I2C or SPI.
+/- 2G, 4G, 8G and 16G ranges. The KX134ACR-LBZ and KX134-1211 support
+/- 8G, 16G, 32G and 64G. All the sensors also have variable output
data-rates and a hardware-fifo buffering. These accelerometers can be
accessed either via I2C or SPI.
properties:
compatible:
enum:
- kionix,kx022a
- kionix,kx132-1211
- kionix,kx134-1211
- rohm,kx132acr-lbz
- rohm,kx134acr-lbz
reg:
maxItems: 1

View file

@ -14,12 +14,20 @@ description: |
SPI and I2C interface.
https://www.nxp.com/docs/en/data-sheet/FXLS8962AF.pdf
https://www.nxp.com/docs/en/data-sheet/FXLS8964AF.pdf
https://www.nxp.com/docs/en/data-sheet/FXLS8967AF.pdf
https://www.nxp.com/docs/en/data-sheet/FXLS8974CF.pdf
properties:
compatible:
enum:
- nxp,fxls8962af
- nxp,fxls8964af
oneOf:
- enum:
- nxp,fxls8962af
- nxp,fxls8964af
- items:
- enum:
- nxp,fxls8967af
- nxp,fxls8974cf
- const: nxp,fxls8962af
reg:
maxItems: 1
@ -38,6 +46,11 @@ properties:
drive-open-drain:
type: boolean
wakeup-source:
$ref: /schemas/types.yaml#/definitions/flag
description:
Enable wake on accelerometer event
required:
- compatible
- reg
@ -61,6 +74,7 @@ examples:
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "INT1";
wakeup-source;
};
};
- |

View file

@ -19,49 +19,82 @@ description: |
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4020-4021-4022.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4001.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4003.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7685.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7686.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7687.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7688.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7690.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7691.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7693.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7942.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7946.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7980.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7982.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7983.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7984.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7988-1_7988-5.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
oneOf:
- const: adi,ad4000
- enum:
- adi,ad4000
- adi,ad4001
- adi,ad4002
- adi,ad4003
- adi,ad4020
- adi,adaq4001
- adi,adaq4003
- adi,ad7687
- adi,ad7691
- adi,ad7942
- adi,ad7946
- adi,ad7983
- items:
- enum:
- adi,ad4004
- adi,ad4008
- const: adi,ad4000
- const: adi,ad4001
- items:
- enum:
- adi,ad4005
- const: adi,ad4001
- const: adi,ad4002
- items:
- enum:
- adi,ad4006
- adi,ad4010
- const: adi,ad4002
- const: adi,ad4003
- items:
- enum:
- adi,ad4007
- adi,ad4011
- const: adi,ad4003
- const: adi,ad4020
- items:
- enum:
- adi,ad4021
- adi,ad4022
- const: adi,ad4020
- const: adi,adaq4001
- const: adi,adaq4003
- items:
- enum:
- adi,ad7685
- adi,ad7686
- adi,ad7980
- adi,ad7988-1
- adi,ad7988-5
- const: adi,ad7983
- items:
- enum:
- adi,ad7688
- adi,ad7693
- const: adi,ad7687
- items:
- enum:
- adi,ad7690
- adi,ad7982
- adi,ad7984
- const: adi,ad7691
reg:
maxItems: 1
@ -133,6 +166,22 @@ required:
- ref-supply
allOf:
# Single-channel PulSAR devices have SDI either tied to VIO, GND, or host CS.
- if:
properties:
compatible:
contains:
enum:
- adi,ad7687
- adi,ad7691
- adi,ad7942
- adi,ad7946
- adi,ad7983
then:
properties:
adi,sdi-pin:
enum: [ high, low, cs ]
default: cs
# The configuration register can only be accessed if SDI is connected to MOSI
- if:
required:

View file

@ -134,8 +134,9 @@ patternProperties:
description:
Describes the common mode channel for single channels. 0xFF is REFGND
and OxFE is COM. Macros are available for these values in
dt-bindings/iio/adi,ad4695.h. Values 1 to 15 correspond to INx inputs.
Only odd numbered INx inputs can be used as common mode channels.
dt-bindings/iio/adc/adi,ad4695.h. Values 1 to 15 correspond to INx
inputs. Only odd numbered INx inputs can be used as common mode
channels.
enum: [1, 3, 5, 7, 9, 11, 13, 15, 0xFE, 0xFF]
default: 0xFF
@ -209,7 +210,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/iio/adi,ad4695.h>
#include <dt-bindings/iio/adc/adi,ad4695.h>
spi {
#address-cells = <1>;

View file

@ -37,6 +37,17 @@ properties:
description: IRQ line for the ADC
maxItems: 1
rdy-gpios:
description:
GPIO reading the R̅D̅Y̅ line. Having such a GPIO is technically optional but
highly recommended because DOUT/R̅D̅Y̅ toggles during SPI transfers (in its
DOUT aka MISO role) and so usually triggers a spurious interrupt. The
distinction between such a spurious event and a real one can only be done
by reading such a GPIO. (There is a register telling the same
information, but accessing that one needs a SPI transfer which then
triggers another interrupt event.)
maxItems: 1
'#address-cells':
const: 1
@ -111,6 +122,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
@ -121,6 +133,7 @@ examples:
spi-max-frequency = <5000000>;
interrupts = <25 2>;
interrupt-parent = <&gpio>;
rdy-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
refin1-supply = <&adc_vref>;
clocks = <&ad7124_mclk>;
clock-names = "mclk";

View file

@ -135,6 +135,17 @@ properties:
'#clock-cells':
const: 0
rdy-gpios:
description:
GPIO reading the R̅D̅Y̅ line. Having such a GPIO is technically optional but
highly recommended because DOUT/R̅D̅Y̅ toggles during SPI transfers (in its
DOUT aka MISO role) and so usually triggers a spurious interrupt. The
distinction between such a spurious event and a real one can only be done
by reading such a GPIO. (There is a register telling the same
information, but accessing that one needs a SPI transfer which then
triggers another interrupt event.)
maxItems: 1
patternProperties:
"^channel@[0-9a-f]$":
type: object
@ -443,6 +454,7 @@ examples:
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "rdy";
interrupt-parent = <&gpio>;
rdy-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
spi-max-frequency = <5000000>;
gpio-controller;
#gpio-cells = <2>;

View file

@ -106,6 +106,17 @@ properties:
description: see Documentation/devicetree/bindings/iio/adc/adc.yaml
type: boolean
rdy-gpios:
description:
GPIO reading the R̅D̅Y̅ line. Having such a GPIO is technically optional but
highly recommended because DOUT/R̅D̅Y̅ toggles during SPI transfers (in its
DOUT aka MISO role) and so usually triggers a spurious interrupt. The
distinction between such a spurious event and a real one can only be done
by reading such a GPIO. (There is a register telling the same
information, but accessing that one needs a SPI transfer which then
triggers another interrupt event.)
maxItems: 1
patternProperties:
"^channel@[0-9a-f]+$":
type: object
@ -181,6 +192,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
@ -195,6 +207,7 @@ examples:
clock-names = "mclk";
interrupts = <25 0x2>;
interrupt-parent = <&gpio>;
rdy-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
aincom-supply = <&aincom>;
dvdd-supply = <&dvdd>;
avdd-supply = <&avdd>;
@ -207,6 +220,7 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
@ -224,6 +238,7 @@ examples:
#clock-cells = <0>;
interrupts = <25 0x2>;
interrupt-parent = <&gpio>;
rdy-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
aincom-supply = <&aincom>;
dvdd-supply = <&dvdd>;
avdd-supply = <&avdd>;

View file

@ -63,6 +63,17 @@ properties:
marked GPIO_ACTIVE_LOW.
maxItems: 1
rdy-gpios:
description:
GPIO reading the R̅D̅Y̅ line. Having such a GPIO is technically optional but
highly recommended because DOUT/R̅D̅Y̅ toggles during SPI transfers (in its
DOUT aka MISO role) and so usually triggers a spurious interrupt. The
distinction between such a spurious event and a real one can only be done
by reading such a GPIO. (There is a register telling the same
information, but accessing that one needs a SPI transfer which then
triggers another interrupt event.)
maxItems: 1
required:
- compatible
- reg

View file

@ -17,12 +17,15 @@ description: |
properties:
compatible:
items:
- enum:
- renesas,r9a07g043-adc # RZ/G2UL and RZ/Five
- renesas,r9a07g044-adc # RZ/G2L
- renesas,r9a07g054-adc # RZ/V2L
- const: renesas,rzg2l-adc
oneOf:
- items:
- enum:
- renesas,r9a07g043-adc # RZ/G2UL and RZ/Five
- renesas,r9a07g044-adc # RZ/G2L
- renesas,r9a07g054-adc # RZ/V2L
- const: renesas,rzg2l-adc
- items:
- const: renesas,r9a08g045-adc # RZ/G3S
reg:
maxItems: 1
@ -57,6 +60,9 @@ properties:
'#size-cells':
const: 0
"#io-channel-cells":
const: 1
required:
- compatible
- reg
@ -68,7 +74,7 @@ required:
- reset-names
patternProperties:
"^channel@[0-7]$":
"^channel@[0-8]$":
$ref: adc.yaml
type: object
description: |
@ -78,6 +84,8 @@ patternProperties:
reg:
description: |
The channel number.
minimum: 0
maximum: 8
required:
- reg
@ -92,18 +100,25 @@ allOf:
const: renesas,r9a07g043-adc
then:
patternProperties:
"^channel@[2-7]$": false
"^channel@[2-8]$": false
"^channel@[0-1]$":
properties:
reg:
minimum: 0
maximum: 1
else:
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a07g044-adc
- renesas,r9a07g054-adc
then:
patternProperties:
"^channel@[8]$": false
"^channel@[0-7]$":
properties:
reg:
minimum: 0
maximum: 7
additionalProperties: false

View file

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/chemical/bosch,bme680.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Bosch BME680 Gas sensor
maintainers:
- Vasileios Amoiridis <vassilisamir@gmail.com>
description: >
BME680 is a gas sensor which combines relative humidity, barometric pressure,
ambient temperature and gas (VOC - Volatile Organic Compounds) measurements.
https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme680-ds001.pdf
properties:
compatible:
const: bosch,bme680
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
required:
- compatible
- reg
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
co2-sensor@77 {
compatible = "bosch,bme680";
reg = <0x77>;
vddio-supply = <&vddio>;
vdd-supply = <&vdd>;
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
co2-sensor@0 {
compatible = "bosch,bme680";
reg = <0>;
spi-max-frequency = <500000>;
vddio-supply = <&vddio>;
vdd-supply = <&vdd>;
};
};

View file

@ -91,7 +91,7 @@ examples:
vrefn-supply = <&dac_vrefn>;
reset-gpios = <&gpio_bd 16 GPIO_ACTIVE_LOW>;
clear-gpios = <&gpio_bd 17 GPIO_ACTIVE_LOW>;
ldac-gpios = <&gpio_bd 18 GPIO_ACTIVE_HIGH>;
ldac-gpios = <&gpio_bd 18 GPIO_ACTIVE_LOW>;
};
};
...

View file

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2024 ROHM Semiconductor.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/rohm,bd79703.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD79703 DAC device driver
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
The ROHM BD79703 is a 6 channel, 8-bit DAC.
Datasheet can be found here:
https://fscdn.rohm.com/en/products/databook/datasheet/ic/data_converter/dac/bd79702fv-lb_bd79703fv-lb-e.pdf
properties:
compatible:
const: rohm,bd79703
reg:
maxItems: 1
spi-max-frequency:
maximum: 30000000
vfs-supply:
description:
The regulator to use as a full scale voltage. The voltage should be between 2.7V .. VCC
vcc-supply:
description:
The regulator supplying the operating voltage. Should be between 2.7V ... 5.5V
required:
- compatible
- reg
- spi-max-frequency
- vfs-supply
- vcc-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
compatible = "rohm,bd79703";
reg = <0>;
spi-max-frequency = <30000000>;
vcc-supply = <&vcc>;
vfs-supply = <&vref>;
};
};
...

View file

@ -11,24 +11,30 @@ maintainers:
properties:
compatible:
enum:
- adi,adis16375
- adi,adis16480
- adi,adis16485
- adi,adis16488
- adi,adis16490
- adi,adis16495-1
- adi,adis16495-2
- adi,adis16495-3
- adi,adis16497-1
- adi,adis16497-2
- adi,adis16497-3
- adi,adis16545-1
- adi,adis16545-2
- adi,adis16545-3
- adi,adis16547-1
- adi,adis16547-2
- adi,adis16547-3
oneOf:
- enum:
- adi,adis16375
- adi,adis16480
- adi,adis16485
- adi,adis16486
- adi,adis16488
- adi,adis16489
- adi,adis16490
- adi,adis16495-1
- adi,adis16495-2
- adi,adis16495-3
- adi,adis16497-1
- adi,adis16497-2
- adi,adis16497-3
- adi,adis16545-1
- adi,adis16545-2
- adi,adis16545-3
- adi,adis16547-1
- adi,adis16547-2
- adi,adis16547-3
- items:
- const: adi,adis16487
- const: adi,adis16485
reg:
maxItems: 1

View file

@ -37,6 +37,7 @@ properties:
to "INT2" if INT2 pin should be used instead
drive-open-drain:
type: boolean
description: |
set if the specified interrupt pin should be configured as
open drain. If not set, defaults to push-pull.

View file

@ -41,6 +41,7 @@ properties:
- INT2
drive-open-drain:
type: boolean
description:
set if the specified interrupt pins should be configured as
open drain. If not set, defaults to push-pull.

View file

@ -38,6 +38,7 @@ properties:
- INT2
drive-open-drain:
type: boolean
description:
set if the specified interrupt pin should be configured as
open drain. If not set, defaults to push-pull.

View file

@ -16,6 +16,7 @@ properties:
compatible:
oneOf:
- enum:
- invensense,iam20380
- invensense,iam20680
- invensense,icm20608
- invensense,icm20609

View file

@ -1,49 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bu27008.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BU27008 color sensor
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description:
The ROHM BU27008 is a sensor with 5 photodiodes (red, green, blue, clear
and IR) with four configurable channels. Red and green being always
available and two out of the rest three (blue, clear, IR) can be
selected to be simultaneously measured. Typical application is adjusting
LCD backlight of TVs, mobile phones and tablet PCs.
properties:
compatible:
const: rohm,bu27008
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@38 {
compatible = "rohm,bu27008";
reg = <0x38>;
};
};
...

View file

@ -1,50 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/rohm,bu27010.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BU27010 color sensor
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
The ROHM BU27010 is a sensor with 6 photodiodes (red, green, blue, clear,
IR and flickering detection) with five configurable channels. Red, green
and flickering detection being always available and two out of the rest
three (blue, clear, IR) can be selected to be simultaneously measured.
Typical application is adjusting LCD/OLED backlight of TVs, mobile phones
and tablet PCs.
properties:
compatible:
const: rohm,bu27010
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@38 {
compatible = "rohm,bu27010";
reg = <0x38>;
vdd-supply = <&vdd>;
};
};

View file

@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/ti,opt4060.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments OPT4060 RGBW Color Sensor
maintainers:
- Per-Daniel Olsson <perdaniel.olsson@axis.com>
description:
Texas Instrument RGBW high resolution color sensor over I2C.
https://www.ti.com/lit/gpn/opt4060
properties:
compatible:
enum:
- ti,opt4060
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@44 {
compatible = "ti,opt4060";
reg = <0x44>;
vdd-supply = <&vdd_reg>;
interrupt-parent = <&gpio5>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
};
};
...

View file

@ -55,12 +55,16 @@ properties:
If not set, defaults to push-pull configuration.
type: boolean
spi-max-frequency:
maximum: 10000000
required:
- compatible
- vddd-supply
- vdda-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
properties:
compatible:
@ -73,6 +77,16 @@ allOf:
then:
properties:
interrupts: false
- if:
properties:
compatible:
contains:
enum:
- bosch,bmp085
- bosch,bmp180
then:
properties:
spi-max-frequency: false
additionalProperties: false
@ -93,3 +107,18 @@ examples:
vdda-supply = <&bar>;
};
};
- |
# include <dt-bindings/gpio/gpio.h>
# include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
pressure@0 {
compatible = "bosch,bmp280";
reg = <0>;
spi-max-frequency = <10000000>;
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
vddd-supply = <&foo>;
vdda-supply = <&bar>;
};
};

View file

@ -38,6 +38,7 @@ properties:
- qcom,sm8250-cpu-bwmon
- qcom,sm8550-cpu-bwmon
- qcom,sm8650-cpu-bwmon
- qcom,sm8750-cpu-bwmon
- qcom,x1e80100-cpu-bwmon
- const: qcom,sdm845-bwmon # BWMON v4, unified register space
- items:

View file

@ -33,6 +33,7 @@ properties:
- qcom,sm6375-cpucp-l3
- qcom,sm8250-epss-l3
- qcom,sm8350-epss-l3
- qcom,sm8650-epss-l3
- const: qcom,epss-l3
reg:

View file

@ -20,12 +20,14 @@ properties:
- qcom,apq8064-qfprom
- qcom,apq8084-qfprom
- qcom,ipq5332-qfprom
- qcom,ipq5424-qfprom
- qcom,ipq6018-qfprom
- qcom,ipq8064-qfprom
- qcom,ipq8074-qfprom
- qcom,ipq9574-qfprom
- qcom,msm8226-qfprom
- qcom,msm8916-qfprom
- qcom,msm8917-qfprom
- qcom,msm8974-qfprom
- qcom,msm8976-qfprom
- qcom,msm8996-qfprom
@ -33,6 +35,7 @@ properties:
- qcom,qcm2290-qfprom
- qcom,qcs404-qfprom
- qcom,qcs615-qfprom
- qcom,qcs8300-qfprom
- qcom,sc7180-qfprom
- qcom,sc7280-qfprom
- qcom,sc8280xp-qfprom

View file

@ -16,6 +16,7 @@ properties:
compatible:
items:
- enum:
- mobileye,eyeq5-bootloader-config
- raspberrypi,bootloader-config
- raspberrypi,bootloader-public-key
- const: nvmem-rmem

View file

@ -55,8 +55,6 @@ properties:
- atmel,atsha204a
# BPA-RS600: Power Supply
- blutek,bpa-rs600
# Bosch Sensortec pressure, temperature, humididty and VOC sensor
- bosch,bme680
# CM32181: Ambient Light Sensor
- capella,cm32181
# CM3232: Ambient Light Sensor

View file

@ -25,6 +25,8 @@ properties:
reg:
maxItems: 1
vcc-supply: true
required:
- compatible
- reg

View file

@ -0,0 +1,255 @@
=======================
Extcon Device Subsystem
=======================
Overview
========
The Extcon (External Connector) subsystem provides a unified framework for
managing external connectors in Linux systems. It allows drivers to report
the state of external connectors and provides a standardized interface for
userspace to query and monitor these states.
Extcon is particularly useful in modern devices with multiple connectivity
options, such as smartphones, tablets, and laptops. It helps manage various
types of connectors, including:
1. USB connectors (e.g., USB-C, micro-USB)
2. Charging ports (e.g., fast charging, wireless charging)
3. Audio jacks (e.g., 3.5mm headphone jack)
4. Video outputs (e.g., HDMI, DisplayPort)
5. Docking stations
Real-world examples:
1. Smartphone USB-C port:
A single USB-C port on a smartphone can serve multiple functions. Extcon
can manage the different states of this port, such as:
- USB data connection
- Charging (various types like fast charging, USB Power Delivery)
- Audio output (USB-C headphones)
- Video output (USB-C to HDMI adapter)
2. Laptop docking station:
When a laptop is connected to a docking station, multiple connections are
made simultaneously. Extcon can handle the state changes for:
- Power delivery
- External displays
- USB hub connections
- Ethernet connectivity
3. Wireless charging pad:
Extcon can manage the state of a wireless charging connection, allowing
the system to respond appropriately when a device is placed on or removed
from the charging pad.
4. Smart TV HDMI ports:
In a smart TV, Extcon can manage multiple HDMI ports, detecting when
devices are connected or disconnected, and potentially identifying the
type of device (e.g., gaming console, set-top box, Blu-ray player).
The Extcon framework simplifies the development of drivers for these complex
scenarios by providing a standardized way to report and query connector
states, handle mutually exclusive connections, and manage connector
properties. This allows for more robust and flexible handling of external
connections in modern devices.
Key Components
==============
extcon_dev
----------
The core structure representing an Extcon device::
struct extcon_dev {
const char *name;
const unsigned int *supported_cable;
const u32 *mutually_exclusive;
/* Internal data */
struct device dev;
unsigned int id;
struct raw_notifier_head nh_all;
struct raw_notifier_head *nh;
struct list_head entry;
int max_supported;
spinlock_t lock;
u32 state;
/* Sysfs related */
struct device_type extcon_dev_type;
struct extcon_cable *cables;
struct attribute_group attr_g_muex;
struct attribute **attrs_muex;
struct device_attribute *d_attrs_muex;
};
Key fields:
- ``name``: Name of the Extcon device
- ``supported_cable``: Array of supported cable types
- ``mutually_exclusive``: Array defining mutually exclusive cable types
This field is crucial for enforcing hardware constraints. It's an array of
32-bit unsigned integers, where each element represents a set of mutually
exclusive cable types. The array should be terminated with a 0.
For example:
::
static const u32 mutually_exclusive[] = {
BIT(0) | BIT(1), /* Cable 0 and 1 are mutually exclusive */
BIT(2) | BIT(3) | BIT(4), /* Cables 2, 3, and 4 are mutually exclusive */
0 /* Terminator */
};
In this example, cables 0 and 1 cannot be connected simultaneously, and
cables 2, 3, and 4 are also mutually exclusive. This is useful for
scenarios like a single port that can either be USB or HDMI, but not both
at the same time.
The Extcon core uses this information to prevent invalid combinations of
cable states, ensuring that the reported states are always consistent
with the hardware capabilities.
- ``state``: Current state of the device (bitmap of connected cables)
extcon_cable
------------
Represents an individual cable managed by an Extcon device::
struct extcon_cable {
struct extcon_dev *edev;
int cable_index;
struct attribute_group attr_g;
struct device_attribute attr_name;
struct device_attribute attr_state;
struct attribute *attrs[3];
union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
DECLARE_BITMAP(usb_bits, EXTCON_PROP_USB_CNT);
DECLARE_BITMAP(chg_bits, EXTCON_PROP_CHG_CNT);
DECLARE_BITMAP(jack_bits, EXTCON_PROP_JACK_CNT);
DECLARE_BITMAP(disp_bits, EXTCON_PROP_DISP_CNT);
};
Core Functions
==============
.. kernel-doc:: drivers/extcon/extcon.c
:identifiers: extcon_get_state
.. kernel-doc:: drivers/extcon/extcon.c
:identifiers: extcon_set_state
.. kernel-doc:: drivers/extcon/extcon.c
:identifiers: extcon_set_state_sync
.. kernel-doc:: drivers/extcon/extcon.c
:identifiers: extcon_get_property
Sysfs Interface
===============
Extcon devices expose the following sysfs attributes:
- ``name``: Name of the Extcon device
- ``state``: Current state of all supported cables
- ``cable.N/name``: Name of the Nth supported cable
- ``cable.N/state``: State of the Nth supported cable
Usage Example
-------------
.. code-block:: c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/extcon.h>
struct my_extcon_data {
struct extcon_dev *edev;
struct device *dev;
};
static const unsigned int my_extcon_cable[] = {
EXTCON_USB,
EXTCON_USB_HOST,
EXTCON_NONE,
};
static int my_extcon_probe(struct platform_device *pdev)
{
struct my_extcon_data *data;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = &pdev->dev;
/* Initialize extcon device */
data->edev = devm_extcon_dev_allocate(data->dev, my_extcon_cable);
if (IS_ERR(data->edev)) {
dev_err(data->dev, "Failed to allocate extcon device\n");
return PTR_ERR(data->edev);
}
/* Register extcon device */
ret = devm_extcon_dev_register(data->dev, data->edev);
if (ret < 0) {
dev_err(data->dev, "Failed to register extcon device\n");
return ret;
}
platform_set_drvdata(pdev, data);
/* Example: Set initial state */
extcon_set_state_sync(data->edev, EXTCON_USB, true);
dev_info(data->dev, "My extcon driver probed successfully\n");
return 0;
}
static int my_extcon_remove(struct platform_device *pdev)
{
struct my_extcon_data *data = platform_get_drvdata(pdev);
/* Example: Clear state before removal */
extcon_set_state_sync(data->edev, EXTCON_USB, false);
dev_info(data->dev, "My extcon driver removed\n");
return 0;
}
static const struct of_device_id my_extcon_of_match[] = {
{ .compatible = "my,extcon-device", },
{ },
};
MODULE_DEVICE_TABLE(of, my_extcon_of_match);
static struct platform_driver my_extcon_driver = {
.driver = {
.name = "my-extcon-driver",
.of_match_table = my_extcon_of_match,
},
.probe = my_extcon_probe,
.remove = my_extcon_remove,
};
module_platform_driver(my_extcon_driver);
This example demonstrates:
---------------------------
- Defining supported cable types (USB and USB Host in this case).
- Allocating and registering an extcon device.
- Setting an initial state for a cable (USB connected in this example).
- Clearing the state when the driver is removed.

View file

@ -86,6 +86,7 @@ Subsystem-specific APIs
dmaengine/index
dpll
edac
extcon
firmware/index
fpga/index
frame-buffer

View file

@ -202,6 +202,46 @@ Sometimes one needs to be able not only to catch PPS signals but to produce
them also. For example, running a distributed simulation, which requires
computers' clock to be synchronized very tightly.
To do so the class pps-gen has been added. PPS generators can be
registered in the kernel by defining a struct pps_gen_source_info as
follows::
static struct pps_gen_source_info pps_gen_dummy_info = {
.name = "dummy",
.use_system_clock = true,
.get_time = pps_gen_dummy_get_time,
.enable = pps_gen_dummy_enable,
};
Where the use_system_clock states if the generator uses the system
clock to generate its pulses, or they are from a peripheral device
clock. Method get_time() is used to query the time stored into the
generator clock, while the method enable() is used to enable or
disable the PPS pulse generation.
Then calling the function pps_gen_register_source() in your
initialization routine as follows creates a new generator in the
system::
pps_gen = pps_gen_register_source(&pps_gen_dummy_info);
Generators SYSFS support
------------------------
If the SYSFS filesystem is enabled in the kernel it provides a new class::
$ ls /sys/class/pps-gen/
pps-gen0/ pps-gen1/ pps-gen2/
Every directory is the ID of a PPS generator defined in the system and
inside of it you find several files::
$ ls -F /sys/class/pps-gen/pps-gen0/
dev enable name power/ subsystem@ system time uevent
To enable the PPS signal generation you can use the command below::
$ echo 1 > /sys/class/pps-gen/pps-gen0/enable
Parallel port generator
------------------------

View file

@ -101,7 +101,7 @@ The macro comes from:
.. code-block::
#include <dt-bindings/iio/adi,ad4695.h>
#include <dt-bindings/iio/adc/adi,ad4695.h>
Pairing two INx pins
^^^^^^^^^^^^^^^^^^^^

View file

@ -12,7 +12,10 @@ This driver supports Analog Device's IMUs on SPI bus.
* `ADIS16375 <https://www.analog.com/ADIS16375>`_
* `ADIS16480 <https://www.analog.com/ADIS16480>`_
* `ADIS16485 <https://www.analog.com/ADIS16485>`_
* `ADIS16486 <https://www.analog.com/ADIS16486>`_
* `ADIS16487 <https://www.analog.com/ADIS16487>`_
* `ADIS16488 <https://www.analog.com/ADIS16488>`_
* `ADIS16489 <https://www.analog.com/ADIS16489>`_
* `ADIS16490 <https://www.analog.com/ADIS16490>`_
* `ADIS16495 <https://www.analog.com/ADIS16495>`_
* `ADIS16497 <https://www.analog.com/ADIS16497>`_

View file

@ -29,3 +29,4 @@ Industrial I/O Kernel Drivers
adxl380
bno055
ep93xx_adc
opt4060

View file

@ -0,0 +1,61 @@
==============================
OPT4060 driver
==============================
1. Overview
=============================
This driver supports the Texas Instrument RGBW high resolution color sensor over
I2C.
https://www.ti.com/lit/gpn/opt4060
The driver supports:
- Raw values for red, green, blue and clear.
- Illuminance values.
- Scaled color values for red, green and blue.
- IIO events for thresholds.
- IIO triggered buffer using both its own data ready trigger and triggers from
other drivers.
2. Illuminance calculation
=============================
Illuminance is calculated using the wide spectrum green channel.
lux = GREEN_RAW x 2.15e-3
The value is accessed from:
/sys/bus/iio/devices/iio:deviceX/in_illuminance_input
See section 8.4.5.2 in the data sheet for additional details.
3. Color scale values
=============================
The sensor has different sensitivity for the different color components and
compensating factors are exposed from the driver.
The values are accessed from:
/sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale
/sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale
/sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale
A userspace application can multiply the raw values with the scale values so
that for a particular test light source, typically white, the measurement
intensity is the same across the different color channels. This is calculated
in the following way:
R = RED_RAW x SCALE_RED(2.4)
G = GREEN_RAW x SCALE_GREEN(1.0)
B = BLUE_RAW x SCALE_BLUE(1.3)
The data sheet suggests using the scaled values to normalize the scaled R, G
and B values. This is useful to get a value for the ratio between colors
independent of light intensity. A userspace application can do this in the
following way:
R_NORMALIZED = R / (R + G + B)
G_NORMALIZED = G / (R + G + B)
B_NORMALIZED = B / (R + G + B)
See section 8.4.5.2 in the data sheet for additional details.

View file

@ -64,6 +64,7 @@ Everything else
vduse
futex2
perf_ring_buffer
ntsync
.. only:: subproject and html

View file

@ -283,6 +283,7 @@ Code Seq# Include File Comments
'p' 80-9F linux/ppdev.h user-space parport
<mailto:tim@cyberelk.net>
'p' A1-A5 linux/pps.h LinuxPPS
'p' B1-B3 linux/pps_gen.h LinuxPPS
<mailto:giometti@linux.it>
'q' 00-1F linux/serio.h
'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK

View file

@ -0,0 +1,385 @@
===================================
NT synchronization primitive driver
===================================
This page documents the user-space API for the ntsync driver.
ntsync is a support driver for emulation of NT synchronization
primitives by user-space NT emulators. It exists because implementation
in user-space, using existing tools, cannot match Windows performance
while offering accurate semantics. It is implemented entirely in
software, and does not drive any hardware device.
This interface is meant as a compatibility tool only, and should not
be used for general synchronization. Instead use generic, versatile
interfaces such as futex(2) and poll(2).
Synchronization primitives
==========================
The ntsync driver exposes three types of synchronization primitives:
semaphores, mutexes, and events.
A semaphore holds a single volatile 32-bit counter, and a static 32-bit
integer denoting the maximum value. It is considered signaled (that is,
can be acquired without contention, or will wake up a waiting thread)
when the counter is nonzero. The counter is decremented by one when a
wait is satisfied. Both the initial and maximum count are established
when the semaphore is created.
A mutex holds a volatile 32-bit recursion count, and a volatile 32-bit
identifier denoting its owner. A mutex is considered signaled when its
owner is zero (indicating that it is not owned). The recursion count is
incremented when a wait is satisfied, and ownership is set to the given
identifier.
A mutex also holds an internal flag denoting whether its previous owner
has died; such a mutex is said to be abandoned. Owner death is not
tracked automatically based on thread death, but rather must be
communicated using ``NTSYNC_IOC_MUTEX_KILL``. An abandoned mutex is
inherently considered unowned.
Except for the "unowned" semantics of zero, the actual value of the
owner identifier is not interpreted by the ntsync driver at all. The
intended use is to store a thread identifier; however, the ntsync
driver does not actually validate that a calling thread provides
consistent or unique identifiers.
An event is similar to a semaphore with a maximum count of one. It holds
a volatile boolean state denoting whether it is signaled or not. There
are two types of events, auto-reset and manual-reset. An auto-reset
event is designaled when a wait is satisfied; a manual-reset event is
not. The event type is specified when the event is created.
Unless specified otherwise, all operations on an object are atomic and
totally ordered with respect to other operations on the same object.
Objects are represented by files. When all file descriptors to an
object are closed, that object is deleted.
Char device
===========
The ntsync driver creates a single char device /dev/ntsync. Each file
description opened on the device represents a unique instance intended
to back an individual NT virtual machine. Objects created by one ntsync
instance may only be used with other objects created by the same
instance.
ioctl reference
===============
All operations on the device are done through ioctls. There are four
structures used in ioctl calls::
struct ntsync_sem_args {
__u32 count;
__u32 max;
};
struct ntsync_mutex_args {
__u32 owner;
__u32 count;
};
struct ntsync_event_args {
__u32 signaled;
__u32 manual;
};
struct ntsync_wait_args {
__u64 timeout;
__u64 objs;
__u32 count;
__u32 owner;
__u32 index;
__u32 alert;
__u32 flags;
__u32 pad;
};
Depending on the ioctl, members of the structure may be used as input,
output, or not at all.
The ioctls on the device file are as follows:
.. c:macro:: NTSYNC_IOC_CREATE_SEM
Create a semaphore object. Takes a pointer to struct
:c:type:`ntsync_sem_args`, which is used as follows:
.. list-table::
* - ``count``
- Initial count of the semaphore.
* - ``max``
- Maximum count of the semaphore.
Fails with ``EINVAL`` if ``count`` is greater than ``max``.
On success, returns a file descriptor the created semaphore.
.. c:macro:: NTSYNC_IOC_CREATE_MUTEX
Create a mutex object. Takes a pointer to struct
:c:type:`ntsync_mutex_args`, which is used as follows:
.. list-table::
* - ``count``
- Initial recursion count of the mutex.
* - ``owner``
- Initial owner of the mutex.
If ``owner`` is nonzero and ``count`` is zero, or if ``owner`` is
zero and ``count`` is nonzero, the function fails with ``EINVAL``.
On success, returns a file descriptor the created mutex.
.. c:macro:: NTSYNC_IOC_CREATE_EVENT
Create an event object. Takes a pointer to struct
:c:type:`ntsync_event_args`, which is used as follows:
.. list-table::
* - ``signaled``
- If nonzero, the event is initially signaled, otherwise
nonsignaled.
* - ``manual``
- If nonzero, the event is a manual-reset event, otherwise
auto-reset.
On success, returns a file descriptor the created event.
The ioctls on the individual objects are as follows:
.. c:macro:: NTSYNC_IOC_SEM_POST
Post to a semaphore object. Takes a pointer to a 32-bit integer,
which on input holds the count to be added to the semaphore, and on
output contains its previous count.
If adding to the semaphore's current count would raise the latter
past the semaphore's maximum count, the ioctl fails with
``EOVERFLOW`` and the semaphore is not affected. If raising the
semaphore's count causes it to become signaled, eligible threads
waiting on this semaphore will be woken and the semaphore's count
decremented appropriately.
.. c:macro:: NTSYNC_IOC_MUTEX_UNLOCK
Release a mutex object. Takes a pointer to struct
:c:type:`ntsync_mutex_args`, which is used as follows:
.. list-table::
* - ``owner``
- Specifies the owner trying to release this mutex.
* - ``count``
- On output, contains the previous recursion count.
If ``owner`` is zero, the ioctl fails with ``EINVAL``. If ``owner``
is not the current owner of the mutex, the ioctl fails with
``EPERM``.
The mutex's count will be decremented by one. If decrementing the
mutex's count causes it to become zero, the mutex is marked as
unowned and signaled, and eligible threads waiting on it will be
woken as appropriate.
.. c:macro:: NTSYNC_IOC_SET_EVENT
Signal an event object. Takes a pointer to a 32-bit integer, which on
output contains the previous state of the event.
Eligible threads will be woken, and auto-reset events will be
designaled appropriately.
.. c:macro:: NTSYNC_IOC_RESET_EVENT
Designal an event object. Takes a pointer to a 32-bit integer, which
on output contains the previous state of the event.
.. c:macro:: NTSYNC_IOC_PULSE_EVENT
Wake threads waiting on an event object while leaving it in an
unsignaled state. Takes a pointer to a 32-bit integer, which on
output contains the previous state of the event.
A pulse operation can be thought of as a set followed by a reset,
performed as a single atomic operation. If two threads are waiting on
an auto-reset event which is pulsed, only one will be woken. If two
threads are waiting a manual-reset event which is pulsed, both will
be woken. However, in both cases, the event will be unsignaled
afterwards, and a simultaneous read operation will always report the
event as unsignaled.
.. c:macro:: NTSYNC_IOC_READ_SEM
Read the current state of a semaphore object. Takes a pointer to
struct :c:type:`ntsync_sem_args`, which is used as follows:
.. list-table::
* - ``count``
- On output, contains the current count of the semaphore.
* - ``max``
- On output, contains the maximum count of the semaphore.
.. c:macro:: NTSYNC_IOC_READ_MUTEX
Read the current state of a mutex object. Takes a pointer to struct
:c:type:`ntsync_mutex_args`, which is used as follows:
.. list-table::
* - ``owner``
- On output, contains the current owner of the mutex, or zero
if the mutex is not currently owned.
* - ``count``
- On output, contains the current recursion count of the mutex.
If the mutex is marked as abandoned, the function fails with
``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
zero.
.. c:macro:: NTSYNC_IOC_READ_EVENT
Read the current state of an event object. Takes a pointer to struct
:c:type:`ntsync_event_args`, which is used as follows:
.. list-table::
* - ``signaled``
- On output, contains the current state of the event.
* - ``manual``
- On output, contains 1 if the event is a manual-reset event,
and 0 otherwise.
.. c:macro:: NTSYNC_IOC_KILL_OWNER
Mark a mutex as unowned and abandoned if it is owned by the given
owner. Takes an input-only pointer to a 32-bit integer denoting the
owner. If the owner is zero, the ioctl fails with ``EINVAL``. If the
owner does not own the mutex, the function fails with ``EPERM``.
Eligible threads waiting on the mutex will be woken as appropriate
(and such waits will fail with ``EOWNERDEAD``, as described below).
.. c:macro:: NTSYNC_IOC_WAIT_ANY
Poll on any of a list of objects, atomically acquiring at most one.
Takes a pointer to struct :c:type:`ntsync_wait_args`, which is
used as follows:
.. list-table::
* - ``timeout``
- Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME``
is set, the timeout is measured against the REALTIME clock;
otherwise it is measured against the MONOTONIC clock. If the
timeout is equal to or earlier than the current time, the
function returns immediately without sleeping. If ``timeout``
is U64_MAX, the function will sleep until an object is
signaled, and will not fail with ``ETIMEDOUT``.
* - ``objs``
- Pointer to an array of ``count`` file descriptors
(specified as an integer so that the structure has the same
size regardless of architecture). If any object is
invalid, the function fails with ``EINVAL``.
* - ``count``
- Number of objects specified in the ``objs`` array.
If greater than ``NTSYNC_MAX_WAIT_COUNT``, the function fails
with ``EINVAL``.
* - ``owner``
- Mutex owner identifier. If any object in ``objs`` is a mutex,
the ioctl will attempt to acquire that mutex on behalf of
``owner``. If ``owner`` is zero, the ioctl fails with
``EINVAL``.
* - ``index``
- On success, contains the index (into ``objs``) of the object
which was signaled. If ``alert`` was signaled instead,
this contains ``count``.
* - ``alert``
- Optional event object file descriptor. If nonzero, this
specifies an "alert" event object which, if signaled, will
terminate the wait. If nonzero, the identifier must point to a
valid event.
* - ``flags``
- Zero or more flags. Currently the only flag is
``NTSYNC_WAIT_REALTIME``, which causes the timeout to be
measured against the REALTIME clock instead of MONOTONIC.
* - ``pad``
- Unused, must be set to zero.
This function attempts to acquire one of the given objects. If unable
to do so, it sleeps until an object becomes signaled, subsequently
acquiring it, or the timeout expires. In the latter case the ioctl
fails with ``ETIMEDOUT``. The function only acquires one object, even
if multiple objects are signaled.
A semaphore is considered to be signaled if its count is nonzero, and
is acquired by decrementing its count by one. A mutex is considered
to be signaled if it is unowned or if its owner matches the ``owner``
argument, and is acquired by incrementing its recursion count by one
and setting its owner to the ``owner`` argument. An auto-reset event
is acquired by designaling it; a manual-reset event is not affected
by acquisition.
Acquisition is atomic and totally ordered with respect to other
operations on the same object. If two wait operations (with different
``owner`` identifiers) are queued on the same mutex, only one is
signaled. If two wait operations are queued on the same semaphore,
and a value of one is posted to it, only one is signaled.
If an abandoned mutex is acquired, the ioctl fails with
``EOWNERDEAD``. Although this is a failure return, the function may
otherwise be considered successful. The mutex is marked as owned by
the given owner (with a recursion count of 1) and as no longer
abandoned, and ``index`` is still set to the index of the mutex.
The ``alert`` argument is an "extra" event which can terminate the
wait, independently of all other objects.
It is valid to pass the same object more than once, including by
passing the same event in the ``objs`` array and in ``alert``. If a
wakeup occurs due to that object being signaled, ``index`` is set to
the lowest index corresponding to that object.
The function may fail with ``EINTR`` if a signal is received.
.. c:macro:: NTSYNC_IOC_WAIT_ALL
Poll on a list of objects, atomically acquiring all of them. Takes a
pointer to struct :c:type:`ntsync_wait_args`, which is used
identically to ``NTSYNC_IOC_WAIT_ANY``, except that ``index`` is
always filled with zero on success if not woken via alert.
This function attempts to simultaneously acquire all of the given
objects. If unable to do so, it sleeps until all objects become
simultaneously signaled, subsequently acquiring them, or the timeout
expires. In the latter case the ioctl fails with ``ETIMEDOUT`` and no
objects are modified.
Objects may become signaled and subsequently designaled (through
acquisition by other threads) while this thread is sleeping. Only
once all objects are simultaneously signaled does the ioctl acquire
them and return. The entire acquisition is atomic and totally ordered
with respect to other operations on any of the given objects.
If an abandoned mutex is acquired, the ioctl fails with
``EOWNERDEAD``. Similarly to ``NTSYNC_IOC_WAIT_ANY``, all objects are
nevertheless marked as acquired. Note that if multiple mutex objects
are specified, there is no way to know which were marked as
abandoned.
As with "any" waits, the ``alert`` argument is an "extra" event which
can terminate the wait. Critically, however, an "all" wait will
succeed if all members in ``objs`` are signaled, *or* if ``alert`` is
signaled. In the latter case ``index`` will be set to ``count``. As
with "any" waits, if both conditions are filled, the former takes
priority, and objects in ``objs`` will be acquired.
Unlike ``NTSYNC_IOC_WAIT_ANY``, it is not valid to pass the same
object more than once, nor is it valid to pass the same object in
``objs`` and in ``alert``. If this is attempted, the function fails
with ``EINVAL``.

View file

@ -1318,7 +1318,7 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4695.yaml
F: Documentation/iio/ad4695.rst
F: drivers/iio/adc/ad4695.c
F: include/dt-bindings/iio/adi,ad4695.h
F: include/dt-bindings/iio/adc/adi,ad4695.h
ANALOG DEVICES INC AD7091R DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
@ -8710,6 +8710,7 @@ L: linux-kernel@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git
F: Documentation/devicetree/bindings/extcon/
F: Documentation/driver-api/extcon.rst
F: Documentation/firmware-guide/acpi/extcon-intel-int3496.rst
F: drivers/extcon/
F: include/linux/extcon.h
@ -16833,6 +16834,15 @@ T: git https://github.com/Paragon-Software-Group/linux-ntfs3.git
F: Documentation/filesystems/ntfs3.rst
F: fs/ntfs3/
NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER
M: Elizabeth Figura <zfigura@codeweavers.com>
L: wine-devel@winehq.org
S: Supported
F: Documentation/userspace-api/ntsync.rst
F: drivers/misc/ntsync.c
F: include/uapi/linux/ntsync.h
F: tools/testing/selftests/drivers/ntsync/
NUBUS SUBSYSTEM
M: Finn Thain <fthain@linux-m68k.org>
L: linux-m68k@lists.linux-m68k.org
@ -16914,6 +16924,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/srini/nvmem.git
F: Documentation/ABI/stable/sysfs-bus-nvmem
F: Documentation/devicetree/bindings/nvmem/
F: drivers/nvmem/
F: include/dt-bindings/nvmem/
F: include/linux/nvmem-consumer.h
F: include/linux/nvmem-provider.h
@ -18834,11 +18845,13 @@ L: linuxpps@ml.enneenne.com (subscribers-only)
S: Maintained
W: http://wiki.enneenne.com/index.php/LinuxPPS_support
F: Documentation/ABI/testing/sysfs-pps
F: Documentation/ABI/testing/sysfs-pps-gen
F: Documentation/devicetree/bindings/pps/pps-gpio.yaml
F: Documentation/driver-api/pps.rst
F: drivers/pps/
F: include/linux/pps*.h
F: include/uapi/linux/pps.h
F: include/uapi/linux/pps_gen.h
PRESSURE STALL INFORMATION (PSI)
M: Johannes Weiner <hannes@cmpxchg.org>
@ -20429,6 +20442,11 @@ L: linux-serial@vger.kernel.org
S: Odd Fixes
F: drivers/tty/serial/rp2.*
ROHM BD79703 DAC
M: Matti Vaittinen <mazziesaccount@gmail.com>
S: Supported
F: drivers/iio/dac/rohm-bd79703.c
ROHM BD99954 CHARGER IC
M: Matti Vaittinen <mazziesaccount@gmail.com>
S: Supported
@ -20457,7 +20475,6 @@ ROHM BU270xx LIGHT SENSOR DRIVERs
M: Matti Vaittinen <mazziesaccount@gmail.com>
L: linux-iio@vger.kernel.org
S: Supported
F: drivers/iio/light/rohm-bu27008.c
F: drivers/iio/light/rohm-bu27034.c
ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
@ -21389,6 +21406,7 @@ M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/slimbus/
F: Documentation/driver-api/slimbus.rst
F: drivers/slimbus/
F: include/linux/slimbus.h

View file

@ -49,6 +49,28 @@
mhm_reserved_0: the-mhm-reserved-0@0 {
reg = <0x8 0x00000000 0x0 0x0000800>;
};
nvram@461fe00 {
compatible = "mobileye,eyeq5-bootloader-config", "nvmem-rmem";
reg = <0x0 0x0461fe00 0x0 0x200>;
#address-cells = <1>;
#size-cells = <1>;
no-map;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
eth0_mac: mac@7c {
reg = <0x7c 0x6>;
};
eth1_mac: mac@82 {
reg = <0x82 0x6>;
};
};
};
};
aliases {

View file

@ -1971,7 +1971,7 @@ static bool binder_validate_fixup(struct binder_proc *proc,
* struct binder_task_work_cb - for deferred close
*
* @twork: callback_head for task work
* @fd: fd to close
* @file: file to close
*
* Structure to pass task work to be handled after
* returning from binder_ioctl() via task_work_add().
@ -3800,13 +3800,13 @@ err_invalid_target_handle:
}
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
"%d:%d transaction %s to %d:%d failed %d/%d/%d, size %lld-%lld line %d\n",
"%d:%d transaction %s to %d:%d failed %d/%d/%d, code %u size %lld-%lld line %d\n",
proc->pid, thread->pid, reply ? "reply" :
(tr->flags & TF_ONE_WAY ? "async" : "call"),
target_proc ? target_proc->pid : 0,
target_thread ? target_thread->pid : 0,
t_debug_id, return_error, return_error_param,
(u64)tr->data_size, (u64)tr->offsets_size,
tr->code, (u64)tr->data_size, (u64)tr->offsets_size,
return_error_line);
if (target_thread)
@ -6373,7 +6373,7 @@ static void print_binder_transaction_ilocked(struct seq_file *m,
seq_printf(m, " node %d", buffer->target_node->debug_id);
seq_printf(m, " size %zd:%zd offset %lx\n",
buffer->data_size, buffer->offsets_size,
proc->alloc.buffer - buffer->user_data);
proc->alloc.vm_start - buffer->user_data);
}
static void print_binder_work_ilocked(struct seq_file *m,
@ -6927,6 +6927,11 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
{} /* terminator */
};
void binder_add_device(struct binder_device *device)
{
hlist_add_head(&device->hlist, &binder_devices);
}
static int __init init_binder_device(const char *name)
{
int ret;

View file

@ -61,7 +61,7 @@ static size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
struct binder_buffer *buffer)
{
if (list_is_last(&buffer->entry, &alloc->buffers))
return alloc->buffer + alloc->buffer_size - buffer->user_data;
return alloc->vm_start + alloc->buffer_size - buffer->user_data;
return binder_buffer_next(buffer)->user_data - buffer->user_data;
}
@ -169,32 +169,33 @@ struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
{
struct binder_buffer *buffer;
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
return buffer;
}
static inline void
binder_set_installed_page(struct binder_lru_page *lru_page,
binder_set_installed_page(struct binder_alloc *alloc,
unsigned long index,
struct page *page)
{
/* Pairs with acquire in binder_get_installed_page() */
smp_store_release(&lru_page->page_ptr, page);
smp_store_release(&alloc->pages[index], page);
}
static inline struct page *
binder_get_installed_page(struct binder_lru_page *lru_page)
binder_get_installed_page(struct binder_alloc *alloc, unsigned long index)
{
/* Pairs with release in binder_set_installed_page() */
return smp_load_acquire(&lru_page->page_ptr);
return smp_load_acquire(&alloc->pages[index]);
}
static void binder_lru_freelist_add(struct binder_alloc *alloc,
unsigned long start, unsigned long end)
{
struct binder_lru_page *page;
unsigned long page_addr;
struct page *page;
trace_binder_update_page_range(alloc, false, start, end);
@ -202,65 +203,159 @@ static void binder_lru_freelist_add(struct binder_alloc *alloc,
size_t index;
int ret;
index = (page_addr - alloc->buffer) / PAGE_SIZE;
page = &alloc->pages[index];
if (!binder_get_installed_page(page))
index = (page_addr - alloc->vm_start) / PAGE_SIZE;
page = binder_get_installed_page(alloc, index);
if (!page)
continue;
trace_binder_free_lru_start(alloc, index);
ret = list_lru_add_obj(&binder_freelist, &page->lru);
ret = list_lru_add(&binder_freelist,
page_to_lru(page),
page_to_nid(page),
NULL);
WARN_ON(!ret);
trace_binder_free_lru_end(alloc, index);
}
}
static inline
void binder_alloc_set_mapped(struct binder_alloc *alloc, bool state)
{
/* pairs with smp_load_acquire in binder_alloc_is_mapped() */
smp_store_release(&alloc->mapped, state);
}
static inline bool binder_alloc_is_mapped(struct binder_alloc *alloc)
{
/* pairs with smp_store_release in binder_alloc_set_mapped() */
return smp_load_acquire(&alloc->mapped);
}
static struct page *binder_page_lookup(struct binder_alloc *alloc,
unsigned long addr)
{
struct mm_struct *mm = alloc->mm;
struct page *page;
long npages = 0;
/*
* Find an existing page in the remote mm. If missing,
* don't attempt to fault-in just propagate an error.
*/
mmap_read_lock(mm);
if (binder_alloc_is_mapped(alloc))
npages = get_user_pages_remote(mm, addr, 1, FOLL_NOFAULT,
&page, NULL);
mmap_read_unlock(mm);
return npages > 0 ? page : NULL;
}
static int binder_page_insert(struct binder_alloc *alloc,
unsigned long addr,
struct page *page)
{
struct mm_struct *mm = alloc->mm;
struct vm_area_struct *vma;
int ret = -ESRCH;
/* attempt per-vma lock first */
vma = lock_vma_under_rcu(mm, addr);
if (vma) {
if (binder_alloc_is_mapped(alloc))
ret = vm_insert_page(vma, addr, page);
vma_end_read(vma);
return ret;
}
/* fall back to mmap_lock */
mmap_read_lock(mm);
vma = vma_lookup(mm, addr);
if (vma && binder_alloc_is_mapped(alloc))
ret = vm_insert_page(vma, addr, page);
mmap_read_unlock(mm);
return ret;
}
static struct page *binder_page_alloc(struct binder_alloc *alloc,
unsigned long index)
{
struct binder_shrinker_mdata *mdata;
struct page *page;
page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
if (!page)
return NULL;
/* allocate and install shrinker metadata under page->private */
mdata = kzalloc(sizeof(*mdata), GFP_KERNEL);
if (!mdata) {
__free_page(page);
return NULL;
}
mdata->alloc = alloc;
mdata->page_index = index;
INIT_LIST_HEAD(&mdata->lru);
set_page_private(page, (unsigned long)mdata);
return page;
}
static void binder_free_page(struct page *page)
{
kfree((struct binder_shrinker_mdata *)page_private(page));
__free_page(page);
}
static int binder_install_single_page(struct binder_alloc *alloc,
struct binder_lru_page *lru_page,
unsigned long index,
unsigned long addr)
{
struct page *page;
int ret = 0;
int ret;
if (!mmget_not_zero(alloc->mm))
return -ESRCH;
/*
* Protected with mmap_sem in write mode as multiple tasks
* might race to install the same page.
*/
mmap_write_lock(alloc->mm);
if (binder_get_installed_page(lru_page))
goto out;
if (!alloc->vma) {
pr_err("%d: %s failed, no vma\n", alloc->pid, __func__);
ret = -ESRCH;
goto out;
}
page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
page = binder_page_alloc(alloc, index);
if (!page) {
pr_err("%d: failed to allocate page\n", alloc->pid);
ret = -ENOMEM;
goto out;
}
ret = vm_insert_page(alloc->vma, addr, page);
if (ret) {
ret = binder_page_insert(alloc, addr, page);
switch (ret) {
case -EBUSY:
/*
* EBUSY is ok. Someone installed the pte first but the
* alloc->pages[index] has not been updated yet. Discard
* our page and look up the one already installed.
*/
ret = 0;
binder_free_page(page);
page = binder_page_lookup(alloc, addr);
if (!page) {
pr_err("%d: failed to find page at offset %lx\n",
alloc->pid, addr - alloc->vm_start);
ret = -ESRCH;
break;
}
fallthrough;
case 0:
/* Mark page installation complete and safe to use */
binder_set_installed_page(alloc, index, page);
break;
default:
binder_free_page(page);
pr_err("%d: %s failed to insert page at offset %lx with %d\n",
alloc->pid, __func__, addr - alloc->buffer, ret);
__free_page(page);
ret = -ENOMEM;
goto out;
alloc->pid, __func__, addr - alloc->vm_start, ret);
break;
}
/* Mark page installation complete and safe to use */
binder_set_installed_page(lru_page, page);
out:
mmap_write_unlock(alloc->mm);
mmput_async(alloc->mm);
return ret;
}
@ -269,7 +364,6 @@ static int binder_install_buffer_pages(struct binder_alloc *alloc,
struct binder_buffer *buffer,
size_t size)
{
struct binder_lru_page *page;
unsigned long start, final;
unsigned long page_addr;
@ -280,15 +374,13 @@ static int binder_install_buffer_pages(struct binder_alloc *alloc,
unsigned long index;
int ret;
index = (page_addr - alloc->buffer) / PAGE_SIZE;
page = &alloc->pages[index];
if (binder_get_installed_page(page))
index = (page_addr - alloc->vm_start) / PAGE_SIZE;
if (binder_get_installed_page(alloc, index))
continue;
trace_binder_alloc_page_start(alloc, index);
ret = binder_install_single_page(alloc, page, page_addr);
ret = binder_install_single_page(alloc, index, page_addr);
if (ret)
return ret;
@ -302,8 +394,8 @@ static int binder_install_buffer_pages(struct binder_alloc *alloc,
static void binder_lru_freelist_del(struct binder_alloc *alloc,
unsigned long start, unsigned long end)
{
struct binder_lru_page *page;
unsigned long page_addr;
struct page *page;
trace_binder_update_page_range(alloc, true, start, end);
@ -311,13 +403,16 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc,
unsigned long index;
bool on_lru;
index = (page_addr - alloc->buffer) / PAGE_SIZE;
page = &alloc->pages[index];
index = (page_addr - alloc->vm_start) / PAGE_SIZE;
page = binder_get_installed_page(alloc, index);
if (page->page_ptr) {
if (page) {
trace_binder_alloc_lru_start(alloc, index);
on_lru = list_lru_del_obj(&binder_freelist, &page->lru);
on_lru = list_lru_del(&binder_freelist,
page_to_lru(page),
page_to_nid(page),
NULL);
WARN_ON(!on_lru);
trace_binder_alloc_lru_end(alloc, index);
@ -329,20 +424,6 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc,
}
}
static inline void binder_alloc_set_vma(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
/* pairs with smp_load_acquire in binder_alloc_get_vma() */
smp_store_release(&alloc->vma, vma);
}
static inline struct vm_area_struct *binder_alloc_get_vma(
struct binder_alloc *alloc)
{
/* pairs with smp_store_release in binder_alloc_set_vma() */
return smp_load_acquire(&alloc->vma);
}
static void debug_no_space_locked(struct binder_alloc *alloc)
{
size_t largest_alloc_size = 0;
@ -576,7 +657,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
int ret;
/* Check binder_alloc is fully initialized */
if (!binder_alloc_get_vma(alloc)) {
if (!binder_alloc_is_mapped(alloc)) {
binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
"%d: binder_alloc_buf, no vma\n",
alloc->pid);
@ -597,10 +678,10 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
if (!next)
return ERR_PTR(-ENOMEM);
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async);
if (IS_ERR(buffer)) {
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
goto out;
}
@ -608,7 +689,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
buffer->offsets_size = offsets_size;
buffer->extra_buffers_size = extra_buffers_size;
buffer->pid = current->tgid;
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
ret = binder_install_buffer_pages(alloc, buffer, size);
if (ret) {
@ -674,8 +755,8 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,
BUG_ON(buffer->free);
BUG_ON(size > buffer_size);
BUG_ON(buffer->transaction != NULL);
BUG_ON(buffer->user_data < alloc->buffer);
BUG_ON(buffer->user_data > alloc->buffer + alloc->buffer_size);
BUG_ON(buffer->user_data < alloc->vm_start);
BUG_ON(buffer->user_data > alloc->vm_start + alloc->buffer_size);
if (buffer->async_transaction) {
alloc->free_async_space += buffer_size;
@ -734,14 +815,13 @@ static struct page *binder_alloc_get_page(struct binder_alloc *alloc,
pgoff_t *pgoffp)
{
binder_size_t buffer_space_offset = buffer_offset +
(buffer->user_data - alloc->buffer);
(buffer->user_data - alloc->vm_start);
pgoff_t pgoff = buffer_space_offset & ~PAGE_MASK;
size_t index = buffer_space_offset >> PAGE_SHIFT;
struct binder_lru_page *lru_page;
lru_page = &alloc->pages[index];
*pgoffp = pgoff;
return lru_page->page_ptr;
return alloc->pages[index];
}
/**
@ -785,17 +865,17 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
* We could eliminate the call to binder_alloc_clear_buf()
* from binder_alloc_deferred_release() by moving this to
* binder_free_buf_locked(). However, that could
* increase contention for the alloc->lock if clear_on_free
* is used frequently for large buffers. This lock is not
* increase contention for the alloc mutex if clear_on_free
* is used frequently for large buffers. The mutex is not
* needed for correctness here.
*/
if (buffer->clear_on_free) {
binder_alloc_clear_buf(alloc, buffer);
buffer->clear_on_free = false;
}
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
binder_free_buf_locked(alloc, buffer);
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
}
/**
@ -816,7 +896,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
{
struct binder_buffer *buffer;
const char *failure_string;
int ret, i;
int ret;
if (unlikely(vma->vm_mm != alloc->mm)) {
ret = -EINVAL;
@ -834,22 +914,17 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
SZ_4M);
mutex_unlock(&binder_alloc_mmap_lock);
alloc->buffer = vma->vm_start;
alloc->vm_start = vma->vm_start;
alloc->pages = kvcalloc(alloc->buffer_size / PAGE_SIZE,
sizeof(alloc->pages[0]),
GFP_KERNEL);
if (alloc->pages == NULL) {
if (!alloc->pages) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
alloc->pages[i].alloc = alloc;
INIT_LIST_HEAD(&alloc->pages[i].lru);
}
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
ret = -ENOMEM;
@ -857,14 +932,14 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
goto err_alloc_buf_struct_failed;
}
buffer->user_data = alloc->buffer;
buffer->user_data = alloc->vm_start;
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
alloc->free_async_space = alloc->buffer_size / 2;
/* Signal binder_alloc is fully initialized */
binder_alloc_set_vma(alloc, vma);
binder_alloc_set_mapped(alloc, true);
return 0;
@ -872,7 +947,7 @@ err_alloc_buf_struct_failed:
kvfree(alloc->pages);
alloc->pages = NULL;
err_alloc_pages_failed:
alloc->buffer = 0;
alloc->vm_start = 0;
mutex_lock(&binder_alloc_mmap_lock);
alloc->buffer_size = 0;
err_already_mapped:
@ -893,8 +968,8 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
struct binder_buffer *buffer;
buffers = 0;
spin_lock(&alloc->lock);
BUG_ON(alloc->vma);
mutex_lock(&alloc->mutex);
BUG_ON(alloc->mapped);
while ((n = rb_first(&alloc->allocated_buffers))) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
@ -925,22 +1000,26 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
int i;
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
struct page *page;
bool on_lru;
if (!alloc->pages[i].page_ptr)
page = binder_get_installed_page(alloc, i);
if (!page)
continue;
on_lru = list_lru_del_obj(&binder_freelist,
&alloc->pages[i].lru);
on_lru = list_lru_del(&binder_freelist,
page_to_lru(page),
page_to_nid(page),
NULL);
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%s: %d: page %d %s\n",
__func__, alloc->pid, i,
on_lru ? "on lru" : "active");
__free_page(alloc->pages[i].page_ptr);
binder_free_page(page);
page_count++;
}
}
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
kvfree(alloc->pages);
if (alloc->mm)
mmdrop(alloc->mm);
@ -964,17 +1043,17 @@ void binder_alloc_print_allocated(struct seq_file *m,
struct binder_buffer *buffer;
struct rb_node *n;
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n",
buffer->debug_id,
buffer->user_data - alloc->buffer,
buffer->user_data - alloc->vm_start,
buffer->data_size, buffer->offsets_size,
buffer->extra_buffers_size,
buffer->transaction ? "active" : "delivered");
}
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
}
/**
@ -985,29 +1064,29 @@ void binder_alloc_print_allocated(struct seq_file *m,
void binder_alloc_print_pages(struct seq_file *m,
struct binder_alloc *alloc)
{
struct binder_lru_page *page;
struct page *page;
int i;
int active = 0;
int lru = 0;
int free = 0;
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
/*
* Make sure the binder_alloc is fully initialized, otherwise we might
* read inconsistent state.
*/
if (binder_alloc_get_vma(alloc) != NULL) {
if (binder_alloc_is_mapped(alloc)) {
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
page = &alloc->pages[i];
if (!page->page_ptr)
page = binder_get_installed_page(alloc, i);
if (!page)
free++;
else if (list_empty(&page->lru))
else if (list_empty(page_to_lru(page)))
active++;
else
lru++;
}
}
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
seq_printf(m, " pages: %d:%d:%d\n", active, lru, free);
seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high);
}
@ -1023,10 +1102,10 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
struct rb_node *n;
int count = 0;
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
count++;
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
return count;
}
@ -1036,12 +1115,12 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
* @alloc: binder_alloc for this proc
*
* Called from binder_vma_close() when releasing address space.
* Clears alloc->vma to prevent new incoming transactions from
* Clears alloc->mapped to prevent new incoming transactions from
* allocating more buffers.
*/
void binder_alloc_vma_close(struct binder_alloc *alloc)
{
binder_alloc_set_vma(alloc, NULL);
binder_alloc_set_mapped(alloc, false);
}
/**
@ -1058,39 +1137,50 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
void *cb_arg)
__must_hold(&lru->lock)
{
struct binder_lru_page *page = container_of(item, typeof(*page), lru);
struct binder_alloc *alloc = page->alloc;
struct binder_shrinker_mdata *mdata = container_of(item, typeof(*mdata), lru);
struct binder_alloc *alloc = mdata->alloc;
struct mm_struct *mm = alloc->mm;
struct vm_area_struct *vma;
struct page *page_to_free;
unsigned long page_addr;
int mm_locked = 0;
size_t index;
if (!mmget_not_zero(mm))
goto err_mmget;
if (!mmap_read_trylock(mm))
goto err_mmap_read_lock_failed;
if (!spin_trylock(&alloc->lock))
goto err_get_alloc_lock_failed;
if (!page->page_ptr)
goto err_page_already_freed;
index = page - alloc->pages;
page_addr = alloc->buffer + index * PAGE_SIZE;
index = mdata->page_index;
page_addr = alloc->vm_start + index * PAGE_SIZE;
vma = vma_lookup(mm, page_addr);
if (vma && vma != binder_alloc_get_vma(alloc))
/* attempt per-vma lock first */
vma = lock_vma_under_rcu(mm, page_addr);
if (!vma) {
/* fall back to mmap_lock */
if (!mmap_read_trylock(mm))
goto err_mmap_read_lock_failed;
mm_locked = 1;
vma = vma_lookup(mm, page_addr);
}
if (!mutex_trylock(&alloc->mutex))
goto err_get_alloc_mutex_failed;
/*
* Since a binder_alloc can only be mapped once, we ensure
* the vma corresponds to this mapping by checking whether
* the binder_alloc is still mapped.
*/
if (vma && !binder_alloc_is_mapped(alloc))
goto err_invalid_vma;
trace_binder_unmap_kernel_start(alloc, index);
page_to_free = page->page_ptr;
page->page_ptr = NULL;
page_to_free = alloc->pages[index];
binder_set_installed_page(alloc, index, NULL);
trace_binder_unmap_kernel_end(alloc, index);
list_lru_isolate(lru, item);
spin_unlock(&alloc->lock);
spin_unlock(&lru->lock);
if (vma) {
@ -1101,17 +1191,23 @@ enum lru_status binder_alloc_free_page(struct list_head *item,
trace_binder_unmap_user_end(alloc, index);
}
mmap_read_unlock(mm);
mutex_unlock(&alloc->mutex);
if (mm_locked)
mmap_read_unlock(mm);
else
vma_end_read(vma);
mmput_async(mm);
__free_page(page_to_free);
binder_free_page(page_to_free);
return LRU_REMOVED_RETRY;
err_invalid_vma:
err_page_already_freed:
spin_unlock(&alloc->lock);
err_get_alloc_lock_failed:
mmap_read_unlock(mm);
mutex_unlock(&alloc->mutex);
err_get_alloc_mutex_failed:
if (mm_locked)
mmap_read_unlock(mm);
else
vma_end_read(vma);
err_mmap_read_lock_failed:
mmput_async(mm);
err_mmget:
@ -1145,7 +1241,7 @@ void binder_alloc_init(struct binder_alloc *alloc)
alloc->pid = current->group_leader->pid;
alloc->mm = current->mm;
mmgrab(alloc->mm);
spin_lock_init(&alloc->lock);
mutex_init(&alloc->mutex);
INIT_LIST_HEAD(&alloc->buffers);
}

View file

@ -9,7 +9,7 @@
#include <linux/rbtree.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/rtmutex.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/list_lru.h>
@ -59,34 +59,43 @@ struct binder_buffer {
};
/**
* struct binder_lru_page - page object used for binder shrinker
* @page_ptr: pointer to physical page in mmap'd space
* @lru: entry in binder_freelist
* @alloc: binder_alloc for a proc
* struct binder_shrinker_mdata - binder metadata used to reclaim pages
* @lru: LRU entry in binder_freelist
* @alloc: binder_alloc owning the page to reclaim
* @page_index: offset in @alloc->pages[] into the page to reclaim
*/
struct binder_lru_page {
struct binder_shrinker_mdata {
struct list_head lru;
struct page *page_ptr;
struct binder_alloc *alloc;
unsigned long page_index;
};
static inline struct list_head *page_to_lru(struct page *p)
{
struct binder_shrinker_mdata *mdata;
mdata = (struct binder_shrinker_mdata *)page_private(p);
return &mdata->lru;
}
/**
* struct binder_alloc - per-binder proc state for binder allocator
* @lock: protects binder_alloc fields
* @vma: vm_area_struct passed to mmap_handler
* (invariant after mmap)
* @mutex: protects binder_alloc fields
* @mm: copy of task->mm (invariant after open)
* @buffer: base of per-proc address space mapped via mmap
* @vm_start: base of per-proc address space mapped via mmap
* @buffers: list of all buffers for this proc
* @free_buffers: rb tree of buffers available for allocation
* sorted by size
* @allocated_buffers: rb tree of allocated buffers sorted by address
* @free_async_space: VA space available for async buffers. This is
* initialized at mmap time to 1/2 the full VA space
* @pages: array of binder_lru_page
* @pages: array of struct page *
* @buffer_size: size of address space specified via mmap
* @pid: pid for associated binder_proc (invariant after init)
* @pages_high: high watermark of offset in @pages
* @mapped: whether the vm area is mapped, each binder instance is
* allowed a single mapping throughout its lifetime
* @oneway_spam_detected: %true if oneway spam detection fired, clear that
* flag once the async buffer has returned to a healthy state
*
@ -96,18 +105,18 @@ struct binder_lru_page {
* struct binder_buffer objects used to track the user buffers
*/
struct binder_alloc {
spinlock_t lock;
struct vm_area_struct *vma;
struct mutex mutex;
struct mm_struct *mm;
unsigned long buffer;
unsigned long vm_start;
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
struct binder_lru_page *pages;
struct page **pages;
size_t buffer_size;
int pid;
size_t pages_high;
bool mapped;
bool oneway_spam_detected;
};
@ -153,9 +162,9 @@ binder_alloc_get_free_async_space(struct binder_alloc *alloc)
{
size_t free_async_space;
spin_lock(&alloc->lock);
mutex_lock(&alloc->mutex);
free_async_space = alloc->free_async_space;
spin_unlock(&alloc->lock);
mutex_unlock(&alloc->mutex);
return free_async_space;
}

View file

@ -104,11 +104,11 @@ static bool check_buffer_pages_allocated(struct binder_alloc *alloc,
end = PAGE_ALIGN(buffer->user_data + size);
page_addr = buffer->user_data;
for (; page_addr < end; page_addr += PAGE_SIZE) {
page_index = (page_addr - alloc->buffer) / PAGE_SIZE;
if (!alloc->pages[page_index].page_ptr ||
!list_empty(&alloc->pages[page_index].lru)) {
page_index = (page_addr - alloc->vm_start) / PAGE_SIZE;
if (!alloc->pages[page_index] ||
!list_empty(page_to_lru(alloc->pages[page_index]))) {
pr_err("expect alloc but is %s at page index %d\n",
alloc->pages[page_index].page_ptr ?
alloc->pages[page_index] ?
"lru" : "free", page_index);
return false;
}
@ -148,10 +148,10 @@ static void binder_selftest_free_buf(struct binder_alloc *alloc,
* if binder shrinker ran during binder_alloc_free_buf
* calls above.
*/
if (list_empty(&alloc->pages[i].lru)) {
if (list_empty(page_to_lru(alloc->pages[i]))) {
pr_err_size_seq(sizes, seq);
pr_err("expect lru but is %s at page index %d\n",
alloc->pages[i].page_ptr ? "alloc" : "free", i);
alloc->pages[i] ? "alloc" : "free", i);
binder_selftest_failures++;
}
}
@ -168,9 +168,9 @@ static void binder_selftest_free_page(struct binder_alloc *alloc)
}
for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
if (alloc->pages[i].page_ptr) {
if (alloc->pages[i]) {
pr_err("expect free but is %s at page index %d\n",
list_empty(&alloc->pages[i].lru) ?
list_empty(page_to_lru(alloc->pages[i])) ?
"alloc" : "lru", i);
binder_selftest_failures++;
}
@ -291,7 +291,7 @@ void binder_selftest_alloc(struct binder_alloc *alloc)
if (!binder_selftest_run)
return;
mutex_lock(&binder_selftest_lock);
if (!binder_selftest_run || !alloc->vma)
if (!binder_selftest_run || !alloc->mapped)
goto done;
pr_info("STARTED\n");
binder_selftest_alloc_offset(alloc, end_offset, 0);

View file

@ -25,8 +25,7 @@ struct binder_context {
/**
* struct binder_device - information about a binder device node
* @hlist: list of binder devices (only used for devices requested via
* CONFIG_ANDROID_BINDER_DEVICES)
* @hlist: list of binder devices
* @miscdev: information about a binder character device node
* @context: binder context information
* @binderfs_inode: This is the inode of the root dentry of the super block
@ -582,4 +581,12 @@ struct binder_object {
};
};
/**
* Add a binder device to binder_devices
* @device: the new binder device to add to the global list
*
* Not reentrant as the list is not protected by any locks
*/
void binder_add_device(struct binder_device *device);
#endif /* _LINUX_BINDER_INTERNAL_H */

View file

@ -328,7 +328,7 @@ TRACE_EVENT(binder_update_page_range,
TP_fast_assign(
__entry->proc = alloc->pid;
__entry->allocate = allocate;
__entry->offset = start - alloc->buffer;
__entry->offset = start - alloc->vm_start;
__entry->size = end - start;
),
TP_printk("proc=%d allocate=%d offset=%zu size=%zu",

View file

@ -207,6 +207,8 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
fsnotify_create(root->d_inode, dentry);
inode_unlock(d_inode(root));
binder_add_device(device);
return 0;
err:

View file

@ -357,6 +357,7 @@ error_alloc_segment:
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
mhi_buf->buf, mhi_buf->dma_addr);
kfree(img_info->mhi_buf);
error_alloc_mhi_buf:
kfree(img_info);

View file

@ -245,6 +245,58 @@ struct mhi_pci_dev_info {
.channel = ch_num, \
}
static const struct mhi_channel_config mhi_qcom_qdu100_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 2),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 2),
MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 128, 1),
MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 128, 1),
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 64, 3),
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 64, 3),
MHI_CHANNEL_CONFIG_UL(9, "QDSS", 64, 3),
MHI_CHANNEL_CONFIG_UL(14, "NMEA", 32, 4),
MHI_CHANNEL_CONFIG_DL(15, "NMEA", 32, 4),
MHI_CHANNEL_CONFIG_UL(16, "CSM_CTRL", 32, 4),
MHI_CHANNEL_CONFIG_DL(17, "CSM_CTRL", 32, 4),
MHI_CHANNEL_CONFIG_UL(40, "MHI_PHC", 32, 4),
MHI_CHANNEL_CONFIG_DL(41, "MHI_PHC", 32, 4),
MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 256, 5),
MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 256, 5),
};
static struct mhi_event_config mhi_qcom_qdu100_events[] = {
/* first ring is control+data ring */
MHI_EVENT_CONFIG_CTRL(0, 64),
/* SAHARA dedicated event ring */
MHI_EVENT_CONFIG_SW_DATA(1, 256),
/* Software channels dedicated event ring */
MHI_EVENT_CONFIG_SW_DATA(2, 64),
MHI_EVENT_CONFIG_SW_DATA(3, 256),
MHI_EVENT_CONFIG_SW_DATA(4, 256),
/* Software IP channels dedicated event ring */
MHI_EVENT_CONFIG_SW_DATA(5, 512),
MHI_EVENT_CONFIG_SW_DATA(6, 512),
MHI_EVENT_CONFIG_SW_DATA(7, 512),
};
static const struct mhi_controller_config mhi_qcom_qdu100_config = {
.max_channels = 128,
.timeout_ms = 120000,
.num_channels = ARRAY_SIZE(mhi_qcom_qdu100_channels),
.ch_cfg = mhi_qcom_qdu100_channels,
.num_events = ARRAY_SIZE(mhi_qcom_qdu100_events),
.event_cfg = mhi_qcom_qdu100_events,
};
static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = {
.name = "qcom-qdu100",
.fw = "qcom/qdu100/xbl_s.melf",
.edl_trigger = true,
.config = &mhi_qcom_qdu100_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.sideband_wake = false,
};
static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 16, 1),
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 16, 1),
@ -742,6 +794,9 @@ static const struct pci_device_id mhi_pci_id_table[] = {
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309),
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info },
/* QDU100, x100-DU */
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0601),
.driver_data = (kernel_ulong_t) &mhi_qcom_qdu100_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */
.driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */
@ -949,7 +1004,7 @@ static int mhi_pci_get_irqs(struct mhi_controller *mhi_cntrl,
*/
mhi_cntrl->nr_irqs = 1 + mhi_cntrl_config->num_events;
nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSI);
nr_vectors = pci_alloc_irq_vectors(pdev, 1, mhi_cntrl->nr_irqs, PCI_IRQ_MSIX | PCI_IRQ_MSI);
if (nr_vectors < 0) {
dev_err(&pdev->dev, "Error allocating MSI vectors %d\n",
nr_vectors);

View file

@ -338,7 +338,10 @@ static void cdx_shutdown(struct device *dev)
{
struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
struct cdx_device *cdx_dev = to_cdx_device(dev);
struct cdx_controller *cdx = cdx_dev->cdx;
if (cdx_dev->is_bus && cdx_dev->enabled && cdx->ops->bus_disable)
cdx->ops->bus_disable(cdx, cdx_dev->bus_num);
if (cdx_drv && cdx_drv->shutdown)
cdx_drv->shutdown(cdx_dev);
}

View file

@ -63,16 +63,30 @@ static DEFINE_MUTEX(misc_mtx);
#define DYNAMIC_MINORS 128 /* like dynamic majors */
static DEFINE_IDA(misc_minors_ida);
static int misc_minor_alloc(void)
static int misc_minor_alloc(int minor)
{
int ret;
int ret = 0;
ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
if (ret >= 0) {
ret = DYNAMIC_MINORS - ret - 1;
if (minor == MISC_DYNAMIC_MINOR) {
/* allocate free id */
ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
if (ret >= 0) {
ret = DYNAMIC_MINORS - ret - 1;
} else {
ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
MINORMASK, GFP_KERNEL);
}
} else {
ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
MINORMASK, GFP_KERNEL);
/* specific minor, check if it is in dynamic or misc dynamic range */
if (minor < DYNAMIC_MINORS) {
minor = DYNAMIC_MINORS - minor - 1;
ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
} else if (minor > MISC_DYNAMIC_MINOR) {
ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
} else {
/* case of non-dynamic minors, no need to allocate id */
ret = 0;
}
}
return ret;
}
@ -219,7 +233,7 @@ int misc_register(struct miscdevice *misc)
mutex_lock(&misc_mtx);
if (is_dynamic) {
int i = misc_minor_alloc();
int i = misc_minor_alloc(misc->minor);
if (i < 0) {
err = -EBUSY;
@ -228,6 +242,7 @@ int misc_register(struct miscdevice *misc)
misc->minor = i;
} else {
struct miscdevice *c;
int i;
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
@ -235,6 +250,12 @@ int misc_register(struct miscdevice *misc)
goto out;
}
}
i = misc_minor_alloc(misc->minor);
if (i < 0) {
err = -EBUSY;
goto out;
}
}
dev = MKDEV(MISC_MAJOR, misc->minor);

View file

@ -883,9 +883,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
if (len + offset > PAGE_SIZE)
len = PAGE_SIZE - offset;
src = kmap_atomic(buf->page);
src = kmap_local_page(buf->page);
memcpy(page_address(page) + offset, src + buf->offset, len);
kunmap_atomic(src);
kunmap_local(src);
sg_set_page(&(sgl->sg[sgl->n]), page, len, offset);
}

View file

@ -350,7 +350,7 @@ static const struct dev_pm_ops fsa9480_pm_ops = {
};
static const struct i2c_device_id fsa9480_id[] = {
{ "fsa9480", 0 },
{ "fsa9480" },
{}
};
MODULE_DEVICE_TABLE(i2c, fsa9480_id);

View file

@ -338,7 +338,7 @@ static const struct of_device_id ptn5150_dt_match[] = {
MODULE_DEVICE_TABLE(of, ptn5150_dt_match);
static const struct i2c_device_id ptn5150_i2c_id[] = {
{ "ptn5150", 0 },
{ "ptn5150" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ptn5150_i2c_id);

View file

@ -1369,6 +1369,8 @@ static int extcon_rtk_type_c_probe(struct platform_device *pdev)
}
type_c->type_c_cfg = devm_kzalloc(dev, sizeof(*type_c_cfg), GFP_KERNEL);
if (!type_c->type_c_cfg)
return -ENOMEM;
memcpy(type_c->type_c_cfg, type_c_cfg, sizeof(*type_c_cfg));

View file

@ -967,18 +967,15 @@ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
/* first client will create kernel thread */
if (!chan->ctrl->task) {
chan->ctrl->task =
kthread_create_on_node(svc_normal_to_secure_thread,
(void *)chan->ctrl,
cpu_to_node(cpu),
"svc_smc_hvc_thread");
kthread_run_on_cpu(svc_normal_to_secure_thread,
(void *)chan->ctrl,
cpu, "svc_smc_hvc_thread");
if (IS_ERR(chan->ctrl->task)) {
dev_err(chan->ctrl->dev,
"failed to create svc_smc_hvc_thread\n");
kfree(p_data);
return -EINVAL;
}
kthread_bind(chan->ctrl->task, cpu);
wake_up_process(chan->ctrl->task);
}
pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,

View file

@ -16,26 +16,26 @@
#include "dfl-afu.h"
void afu_dma_region_init(struct dfl_feature_platform_data *pdata)
void afu_dma_region_init(struct dfl_feature_dev_data *fdata)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
afu->dma_regions = RB_ROOT;
}
/**
* afu_dma_pin_pages - pin pages of given dma memory region
* @pdata: feature device platform data
* @fdata: feature dev data
* @region: dma memory region to be pinned
*
* Pin all the pages of given dfl_afu_dma_region.
* Return 0 for success or negative error code.
*/
static int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata,
static int afu_dma_pin_pages(struct dfl_feature_dev_data *fdata,
struct dfl_afu_dma_region *region)
{
int npages = region->length >> PAGE_SHIFT;
struct device *dev = &pdata->dev->dev;
struct device *dev = &fdata->dev->dev;
int ret, pinned;
ret = account_locked_vm(current->mm, npages, true);
@ -73,17 +73,17 @@ unlock_vm:
/**
* afu_dma_unpin_pages - unpin pages of given dma memory region
* @pdata: feature device platform data
* @fdata: feature dev data
* @region: dma memory region to be unpinned
*
* Unpin all the pages of given dfl_afu_dma_region.
* Return 0 for success or negative error code.
*/
static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata,
static void afu_dma_unpin_pages(struct dfl_feature_dev_data *fdata,
struct dfl_afu_dma_region *region)
{
long npages = region->length >> PAGE_SHIFT;
struct device *dev = &pdata->dev->dev;
struct device *dev = &fdata->dev->dev;
unpin_user_pages(region->pages, npages);
kfree(region->pages);
@ -133,20 +133,20 @@ static bool dma_region_check_iova(struct dfl_afu_dma_region *region,
/**
* afu_dma_region_add - add given dma region to rbtree
* @pdata: feature device platform data
* @fdata: feature dev data
* @region: dma region to be added
*
* Return 0 for success, -EEXIST if dma region has already been added.
*
* Needs to be called with pdata->lock heold.
* Needs to be called with fdata->lock held.
*/
static int afu_dma_region_add(struct dfl_feature_platform_data *pdata,
static int afu_dma_region_add(struct dfl_feature_dev_data *fdata,
struct dfl_afu_dma_region *region)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
struct rb_node **new, *parent = NULL;
dev_dbg(&pdata->dev->dev, "add region (iova = %llx)\n",
dev_dbg(&fdata->dev->dev, "add region (iova = %llx)\n",
(unsigned long long)region->iova);
new = &afu->dma_regions.rb_node;
@ -177,50 +177,50 @@ static int afu_dma_region_add(struct dfl_feature_platform_data *pdata,
/**
* afu_dma_region_remove - remove given dma region from rbtree
* @pdata: feature device platform data
* @fdata: feature dev data
* @region: dma region to be removed
*
* Needs to be called with pdata->lock heold.
* Needs to be called with fdata->lock held.
*/
static void afu_dma_region_remove(struct dfl_feature_platform_data *pdata,
static void afu_dma_region_remove(struct dfl_feature_dev_data *fdata,
struct dfl_afu_dma_region *region)
{
struct dfl_afu *afu;
dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n",
dev_dbg(&fdata->dev->dev, "del region (iova = %llx)\n",
(unsigned long long)region->iova);
afu = dfl_fpga_pdata_get_private(pdata);
afu = dfl_fpga_fdata_get_private(fdata);
rb_erase(&region->node, &afu->dma_regions);
}
/**
* afu_dma_region_destroy - destroy all regions in rbtree
* @pdata: feature device platform data
* @fdata: feature dev data
*
* Needs to be called with pdata->lock heold.
* Needs to be called with fdata->lock held.
*/
void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata)
void afu_dma_region_destroy(struct dfl_feature_dev_data *fdata)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
struct rb_node *node = rb_first(&afu->dma_regions);
struct dfl_afu_dma_region *region;
while (node) {
region = container_of(node, struct dfl_afu_dma_region, node);
dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n",
dev_dbg(&fdata->dev->dev, "del region (iova = %llx)\n",
(unsigned long long)region->iova);
rb_erase(node, &afu->dma_regions);
if (region->iova)
dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
dma_unmap_page(dfl_fpga_fdata_to_parent(fdata),
region->iova, region->length,
DMA_BIDIRECTIONAL);
if (region->pages)
afu_dma_unpin_pages(pdata, region);
afu_dma_unpin_pages(fdata, region);
node = rb_next(node);
kfree(region);
@ -229,7 +229,7 @@ void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata)
/**
* afu_dma_region_find - find the dma region from rbtree based on iova and size
* @pdata: feature device platform data
* @fdata: feature dev data
* @iova: address of the dma memory area
* @size: size of the dma memory area
*
@ -239,14 +239,14 @@ void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata)
* [@iova, @iova+size)
* If nothing is matched returns NULL.
*
* Needs to be called with pdata->lock held.
* Needs to be called with fdata->lock held.
*/
struct dfl_afu_dma_region *
afu_dma_region_find(struct dfl_feature_platform_data *pdata, u64 iova, u64 size)
afu_dma_region_find(struct dfl_feature_dev_data *fdata, u64 iova, u64 size)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
struct rb_node *node = afu->dma_regions.rb_node;
struct device *dev = &pdata->dev->dev;
struct device *dev = &fdata->dev->dev;
while (node) {
struct dfl_afu_dma_region *region;
@ -276,20 +276,20 @@ afu_dma_region_find(struct dfl_feature_platform_data *pdata, u64 iova, u64 size)
/**
* afu_dma_region_find_iova - find the dma region from rbtree by iova
* @pdata: feature device platform data
* @fdata: feature dev data
* @iova: address of the dma region
*
* Needs to be called with pdata->lock held.
* Needs to be called with fdata->lock held.
*/
static struct dfl_afu_dma_region *
afu_dma_region_find_iova(struct dfl_feature_platform_data *pdata, u64 iova)
afu_dma_region_find_iova(struct dfl_feature_dev_data *fdata, u64 iova)
{
return afu_dma_region_find(pdata, iova, 0);
return afu_dma_region_find(fdata, iova, 0);
}
/**
* afu_dma_map_region - map memory region for dma
* @pdata: feature device platform data
* @fdata: feature dev data
* @user_addr: address of the memory region
* @length: size of the memory region
* @iova: pointer of iova address
@ -298,9 +298,10 @@ afu_dma_region_find_iova(struct dfl_feature_platform_data *pdata, u64 iova)
* of the memory region via @iova.
* Return 0 for success, otherwise error code.
*/
int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
int afu_dma_map_region(struct dfl_feature_dev_data *fdata,
u64 user_addr, u64 length, u64 *iova)
{
struct device *dev = &fdata->dev->dev;
struct dfl_afu_dma_region *region;
int ret;
@ -323,47 +324,47 @@ int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
region->length = length;
/* Pin the user memory region */
ret = afu_dma_pin_pages(pdata, region);
ret = afu_dma_pin_pages(fdata, region);
if (ret) {
dev_err(&pdata->dev->dev, "failed to pin memory region\n");
dev_err(dev, "failed to pin memory region\n");
goto free_region;
}
/* Only accept continuous pages, return error else */
if (!afu_dma_check_continuous_pages(region)) {
dev_err(&pdata->dev->dev, "pages are not continuous\n");
dev_err(dev, "pages are not continuous\n");
ret = -EINVAL;
goto unpin_pages;
}
/* As pages are continuous then start to do DMA mapping */
region->iova = dma_map_page(dfl_fpga_pdata_to_parent(pdata),
region->iova = dma_map_page(dfl_fpga_fdata_to_parent(fdata),
region->pages[0], 0,
region->length,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(dfl_fpga_pdata_to_parent(pdata), region->iova)) {
dev_err(&pdata->dev->dev, "failed to map for dma\n");
if (dma_mapping_error(dfl_fpga_fdata_to_parent(fdata), region->iova)) {
dev_err(dev, "failed to map for dma\n");
ret = -EFAULT;
goto unpin_pages;
}
*iova = region->iova;
mutex_lock(&pdata->lock);
ret = afu_dma_region_add(pdata, region);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
ret = afu_dma_region_add(fdata, region);
mutex_unlock(&fdata->lock);
if (ret) {
dev_err(&pdata->dev->dev, "failed to add dma region\n");
dev_err(dev, "failed to add dma region\n");
goto unmap_dma;
}
return 0;
unmap_dma:
dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
dma_unmap_page(dfl_fpga_fdata_to_parent(fdata),
region->iova, region->length, DMA_BIDIRECTIONAL);
unpin_pages:
afu_dma_unpin_pages(pdata, region);
afu_dma_unpin_pages(fdata, region);
free_region:
kfree(region);
return ret;
@ -371,34 +372,34 @@ free_region:
/**
* afu_dma_unmap_region - unmap dma memory region
* @pdata: feature device platform data
* @fdata: feature dev data
* @iova: dma address of the region
*
* Unmap dma memory region based on @iova.
* Return 0 for success, otherwise error code.
*/
int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova)
int afu_dma_unmap_region(struct dfl_feature_dev_data *fdata, u64 iova)
{
struct dfl_afu_dma_region *region;
mutex_lock(&pdata->lock);
region = afu_dma_region_find_iova(pdata, iova);
mutex_lock(&fdata->lock);
region = afu_dma_region_find_iova(fdata, iova);
if (!region) {
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return -EINVAL;
}
if (region->in_use) {
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return -EBUSY;
}
afu_dma_region_remove(pdata, region);
mutex_unlock(&pdata->lock);
afu_dma_region_remove(fdata, region);
mutex_unlock(&fdata->lock);
dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
dma_unmap_page(dfl_fpga_fdata_to_parent(fdata),
region->iova, region->length, DMA_BIDIRECTIONAL);
afu_dma_unpin_pages(pdata, region);
afu_dma_unpin_pages(fdata, region);
kfree(region);
return 0;

View file

@ -28,37 +28,36 @@
#define ERROR_MASK GENMASK_ULL(63, 0)
/* mask or unmask port errors by the error mask register. */
static void __afu_port_err_mask(struct device *dev, bool mask)
static void __afu_port_err_mask(struct dfl_feature_dev_data *fdata, bool mask)
{
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR);
writeq(mask ? ERROR_MASK : 0, base + PORT_ERROR_MASK);
}
static void afu_port_err_mask(struct device *dev, bool mask)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
mutex_lock(&pdata->lock);
__afu_port_err_mask(dev, mask);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
__afu_port_err_mask(fdata, mask);
mutex_unlock(&fdata->lock);
}
/* clear port errors. */
static int afu_port_err_clear(struct device *dev, u64 err)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base_err, *base_hdr;
int enable_ret = 0, ret = -EBUSY;
u64 v;
base_err = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
base_hdr = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base_err = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR);
base_hdr = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
/*
* clear Port Errors
@ -80,12 +79,12 @@ static int afu_port_err_clear(struct device *dev, u64 err)
}
/* Halt Port by keeping Port in reset */
ret = __afu_port_disable(pdev);
ret = __afu_port_disable(fdata);
if (ret)
goto done;
/* Mask all errors */
__afu_port_err_mask(dev, true);
__afu_port_err_mask(fdata, true);
/* Clear errors if err input matches with current port errors.*/
v = readq(base_err + PORT_ERROR);
@ -102,28 +101,28 @@ static int afu_port_err_clear(struct device *dev, u64 err)
}
/* Clear mask */
__afu_port_err_mask(dev, false);
__afu_port_err_mask(fdata, false);
/* Enable the Port by clearing the reset */
enable_ret = __afu_port_enable(pdev);
enable_ret = __afu_port_enable(fdata);
done:
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return enable_ret ? enable_ret : ret;
}
static ssize_t errors_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 error;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
error = readq(base + PORT_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)error);
}
@ -146,15 +145,15 @@ static DEVICE_ATTR_RW(errors);
static ssize_t first_error_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 error;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
error = readq(base + PORT_FIRST_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)error);
}
@ -164,16 +163,16 @@ static ssize_t first_malformed_req_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 req0, req1;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_ERROR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
req0 = readq(base + PORT_MALFORMED_REQ0);
req1 = readq(base + PORT_MALFORMED_REQ1);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%016llx%016llx\n",
(unsigned long long)req1, (unsigned long long)req0);
@ -191,12 +190,14 @@ static umode_t port_err_attrs_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct dfl_feature_dev_data *fdata;
fdata = to_dfl_feature_dev_data(dev);
/*
* sysfs entries are visible only if related private feature is
* enumerated.
*/
if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_ERROR))
if (!dfl_get_feature_by_id(fdata, PORT_FEATURE_ID_ERROR))
return 0;
return attr->mode;

View file

@ -26,7 +26,7 @@
/**
* __afu_port_enable - enable a port by clear reset
* @pdev: port platform device.
* @fdata: port feature dev data.
*
* Enable Port by clear the port soft reset bit, which is set by default.
* The AFU is unable to respond to any MMIO access while in reset.
@ -35,18 +35,17 @@
*
* The caller needs to hold lock for protection.
*/
int __afu_port_enable(struct platform_device *pdev)
int __afu_port_enable(struct dfl_feature_dev_data *fdata)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
u64 v;
WARN_ON(!pdata->disable_count);
WARN_ON(!fdata->disable_count);
if (--pdata->disable_count != 0)
if (--fdata->disable_count != 0)
return 0;
base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
/* Clear port soft reset */
v = readq(base + PORT_HDR_CTRL);
@ -60,7 +59,8 @@ int __afu_port_enable(struct platform_device *pdev)
if (readq_poll_timeout(base + PORT_HDR_CTRL, v,
!(v & PORT_CTRL_SFTRST_ACK),
RST_POLL_INVL, RST_POLL_TIMEOUT)) {
dev_err(&pdev->dev, "timeout, failure to enable device\n");
dev_err(fdata->dfl_cdev->parent,
"timeout, failure to enable device\n");
return -ETIMEDOUT;
}
@ -69,22 +69,21 @@ int __afu_port_enable(struct platform_device *pdev)
/**
* __afu_port_disable - disable a port by hold reset
* @pdev: port platform device.
* @fdata: port feature dev data.
*
* Disable Port by setting the port soft reset bit, it puts the port into reset.
*
* The caller needs to hold lock for protection.
*/
int __afu_port_disable(struct platform_device *pdev)
int __afu_port_disable(struct dfl_feature_dev_data *fdata)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
u64 v;
if (pdata->disable_count++ != 0)
if (fdata->disable_count++ != 0)
return 0;
base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
/* Set port soft reset */
v = readq(base + PORT_HDR_CTRL);
@ -99,7 +98,8 @@ int __afu_port_disable(struct platform_device *pdev)
if (readq_poll_timeout(base + PORT_HDR_CTRL, v,
v & PORT_CTRL_SFTRST_ACK,
RST_POLL_INVL, RST_POLL_TIMEOUT)) {
dev_err(&pdev->dev, "timeout, failure to disable device\n");
dev_err(fdata->dfl_cdev->parent,
"timeout, failure to disable device\n");
return -ETIMEDOUT;
}
@ -118,34 +118,34 @@ int __afu_port_disable(struct platform_device *pdev)
* (disabled). Any attempts on MMIO access to AFU while in reset, will
* result errors reported via port error reporting sub feature (if present).
*/
static int __port_reset(struct platform_device *pdev)
static int __port_reset(struct dfl_feature_dev_data *fdata)
{
int ret;
ret = __afu_port_disable(pdev);
ret = __afu_port_disable(fdata);
if (ret)
return ret;
return __afu_port_enable(pdev);
return __afu_port_enable(fdata);
}
static int port_reset(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
int ret;
mutex_lock(&pdata->lock);
ret = __port_reset(pdev);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
ret = __port_reset(fdata);
mutex_unlock(&fdata->lock);
return ret;
}
static int port_get_id(struct platform_device *pdev)
static int port_get_id(struct dfl_feature_dev_data *fdata)
{
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
}
@ -153,7 +153,8 @@ static int port_get_id(struct platform_device *pdev)
static ssize_t
id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int id = port_get_id(to_platform_device(dev));
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
int id = port_get_id(fdata);
return scnprintf(buf, PAGE_SIZE, "%d\n", id);
}
@ -162,15 +163,15 @@ static DEVICE_ATTR_RO(id);
static ssize_t
ltr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + PORT_HDR_CTRL);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v));
}
@ -179,7 +180,7 @@ static ssize_t
ltr_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
bool ltr;
u64 v;
@ -187,14 +188,14 @@ ltr_store(struct device *dev, struct device_attribute *attr,
if (kstrtobool(buf, &ltr))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + PORT_HDR_CTRL);
v &= ~PORT_CTRL_LATENCY;
v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0);
writeq(v, base + PORT_HDR_CTRL);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return count;
}
@ -203,15 +204,15 @@ static DEVICE_ATTR_RW(ltr);
static ssize_t
ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + PORT_HDR_STS);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v));
}
@ -220,18 +221,18 @@ static ssize_t
ap1_event_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
bool clear;
if (kstrtobool(buf, &clear) || !clear)
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return count;
}
@ -241,15 +242,15 @@ static ssize_t
ap2_event_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + PORT_HDR_STS);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v));
}
@ -258,18 +259,18 @@ static ssize_t
ap2_event_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
bool clear;
if (kstrtobool(buf, &clear) || !clear)
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return count;
}
@ -278,15 +279,15 @@ static DEVICE_ATTR_RW(ap2_event);
static ssize_t
power_state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + PORT_HDR_STS);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v));
}
@ -296,18 +297,18 @@ static ssize_t
userclk_freqcmd_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
u64 userclk_freq_cmd;
void __iomem *base;
if (kstrtou64(buf, 0, &userclk_freq_cmd))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return count;
}
@ -317,18 +318,18 @@ static ssize_t
userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
u64 userclk_freqcntr_cmd;
void __iomem *base;
if (kstrtou64(buf, 0, &userclk_freqcntr_cmd))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return count;
}
@ -338,15 +339,15 @@ static ssize_t
userclk_freqsts_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
u64 userclk_freqsts;
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts);
}
@ -356,15 +357,15 @@ static ssize_t
userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
u64 userclk_freqcntrsts;
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n",
(unsigned long long)userclk_freqcntrsts);
@ -388,10 +389,12 @@ static umode_t port_hdr_attrs_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct dfl_feature_dev_data *fdata;
umode_t mode = attr->mode;
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
fdata = to_dfl_feature_dev_data(dev);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_HEADER);
if (dfl_feature_revision(base) > 0) {
/*
@ -456,21 +459,21 @@ static const struct dfl_feature_ops port_hdr_ops = {
static ssize_t
afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 guidl, guidh;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU);
base = dfl_get_feature_ioaddr_by_id(fdata, PORT_FEATURE_ID_AFU);
mutex_lock(&pdata->lock);
if (pdata->disable_count) {
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
if (fdata->disable_count) {
mutex_unlock(&fdata->lock);
return -EBUSY;
}
guidl = readq(base + GUID_L);
guidh = readq(base + GUID_H);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl);
}
@ -485,12 +488,14 @@ static umode_t port_afu_attrs_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct dfl_feature_dev_data *fdata;
fdata = to_dfl_feature_dev_data(dev);
/*
* sysfs entries are visible only if related private feature is
* enumerated.
*/
if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU))
if (!dfl_get_feature_by_id(fdata, PORT_FEATURE_ID_AFU))
return 0;
return attr->mode;
@ -504,9 +509,10 @@ static const struct attribute_group port_afu_group = {
static int port_afu_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct resource *res = &pdev->resource[feature->resource_index];
return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
return afu_mmio_region_add(fdata,
DFL_PORT_REGION_INDEX_AFU,
resource_size(res), res->start,
DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
@ -525,9 +531,10 @@ static const struct dfl_feature_ops port_afu_ops = {
static int port_stp_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct resource *res = &pdev->resource[feature->resource_index];
return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
return afu_mmio_region_add(fdata,
DFL_PORT_REGION_INDEX_STP,
resource_size(res), res->start,
DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
@ -595,22 +602,18 @@ static struct dfl_feature_driver port_feature_drvs[] = {
static int afu_open(struct inode *inode, struct file *filp)
{
struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
struct dfl_feature_platform_data *pdata;
struct dfl_feature_dev_data *fdata = dfl_fpga_inode_to_feature_dev_data(inode);
struct platform_device *fdev = fdata->dev;
int ret;
pdata = dev_get_platdata(&fdev->dev);
if (WARN_ON(!pdata))
return -ENODEV;
mutex_lock(&pdata->lock);
ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL);
mutex_lock(&fdata->lock);
ret = dfl_feature_dev_use_begin(fdata, filp->f_flags & O_EXCL);
if (!ret) {
dev_dbg(&fdev->dev, "Device File Opened %d Times\n",
dfl_feature_dev_use_count(pdata));
dfl_feature_dev_use_count(fdata));
filp->private_data = fdev;
}
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret;
}
@ -618,29 +621,29 @@ static int afu_open(struct inode *inode, struct file *filp)
static int afu_release(struct inode *inode, struct file *filp)
{
struct platform_device *pdev = filp->private_data;
struct dfl_feature_platform_data *pdata;
struct dfl_feature_dev_data *fdata;
struct dfl_feature *feature;
dev_dbg(&pdev->dev, "Device File Release\n");
pdata = dev_get_platdata(&pdev->dev);
fdata = to_dfl_feature_dev_data(&pdev->dev);
mutex_lock(&pdata->lock);
dfl_feature_dev_use_end(pdata);
mutex_lock(&fdata->lock);
dfl_feature_dev_use_end(fdata);
if (!dfl_feature_dev_use_count(pdata)) {
dfl_fpga_dev_for_each_feature(pdata, feature)
if (!dfl_feature_dev_use_count(fdata)) {
dfl_fpga_dev_for_each_feature(fdata, feature)
dfl_fpga_set_irq_triggers(feature, 0,
feature->nr_irqs, NULL);
__port_reset(pdev);
afu_dma_region_destroy(pdata);
__port_reset(fdata);
afu_dma_region_destroy(fdata);
}
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return 0;
}
static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
static long afu_ioctl_check_extension(struct dfl_feature_dev_data *fdata,
unsigned long arg)
{
/* No extension support for now */
@ -648,7 +651,7 @@ static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
}
static long
afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
afu_ioctl_get_info(struct dfl_feature_dev_data *fdata, void __user *arg)
{
struct dfl_fpga_port_info info;
struct dfl_afu *afu;
@ -662,12 +665,12 @@ afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
if (info.argsz < minsz)
return -EINVAL;
mutex_lock(&pdata->lock);
afu = dfl_fpga_pdata_get_private(pdata);
mutex_lock(&fdata->lock);
afu = dfl_fpga_fdata_get_private(fdata);
info.flags = 0;
info.num_regions = afu->num_regions;
info.num_umsgs = afu->num_umsgs;
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
@ -675,7 +678,7 @@ afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
return 0;
}
static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
static long afu_ioctl_get_region_info(struct dfl_feature_dev_data *fdata,
void __user *arg)
{
struct dfl_fpga_port_region_info rinfo;
@ -691,7 +694,7 @@ static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
if (rinfo.argsz < minsz || rinfo.padding)
return -EINVAL;
ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
ret = afu_mmio_region_get_by_index(fdata, rinfo.index, &region);
if (ret)
return ret;
@ -706,7 +709,7 @@ static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
}
static long
afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
afu_ioctl_dma_map(struct dfl_feature_dev_data *fdata, void __user *arg)
{
struct dfl_fpga_port_dma_map map;
unsigned long minsz;
@ -720,16 +723,16 @@ afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
if (map.argsz < minsz || map.flags)
return -EINVAL;
ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova);
if (ret)
return ret;
if (copy_to_user(arg, &map, sizeof(map))) {
afu_dma_unmap_region(pdata, map.iova);
afu_dma_unmap_region(fdata, map.iova);
return -EFAULT;
}
dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
dev_dbg(&fdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
(unsigned long long)map.user_addr,
(unsigned long long)map.length,
(unsigned long long)map.iova);
@ -738,7 +741,7 @@ afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
}
static long
afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
afu_ioctl_dma_unmap(struct dfl_feature_dev_data *fdata, void __user *arg)
{
struct dfl_fpga_port_dma_unmap unmap;
unsigned long minsz;
@ -751,33 +754,33 @@ afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
if (unmap.argsz < minsz || unmap.flags)
return -EINVAL;
return afu_dma_unmap_region(pdata, unmap.iova);
return afu_dma_unmap_region(fdata, unmap.iova);
}
static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct platform_device *pdev = filp->private_data;
struct dfl_feature_platform_data *pdata;
struct dfl_feature_dev_data *fdata;
struct dfl_feature *f;
long ret;
dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
pdata = dev_get_platdata(&pdev->dev);
fdata = to_dfl_feature_dev_data(&pdev->dev);
switch (cmd) {
case DFL_FPGA_GET_API_VERSION:
return DFL_FPGA_API_VERSION;
case DFL_FPGA_CHECK_EXTENSION:
return afu_ioctl_check_extension(pdata, arg);
return afu_ioctl_check_extension(fdata, arg);
case DFL_FPGA_PORT_GET_INFO:
return afu_ioctl_get_info(pdata, (void __user *)arg);
return afu_ioctl_get_info(fdata, (void __user *)arg);
case DFL_FPGA_PORT_GET_REGION_INFO:
return afu_ioctl_get_region_info(pdata, (void __user *)arg);
return afu_ioctl_get_region_info(fdata, (void __user *)arg);
case DFL_FPGA_PORT_DMA_MAP:
return afu_ioctl_dma_map(pdata, (void __user *)arg);
return afu_ioctl_dma_map(fdata, (void __user *)arg);
case DFL_FPGA_PORT_DMA_UNMAP:
return afu_ioctl_dma_unmap(pdata, (void __user *)arg);
return afu_ioctl_dma_unmap(fdata, (void __user *)arg);
default:
/*
* Let sub-feature's ioctl function to handle the cmd
@ -785,7 +788,7 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
* handled in this sub feature, and returns 0 and other
* error code if cmd is handled.
*/
dfl_fpga_dev_for_each_feature(pdata, f)
dfl_fpga_dev_for_each_feature(fdata, f)
if (f->ops && f->ops->ioctl) {
ret = f->ops->ioctl(pdev, f, cmd, arg);
if (ret != -ENODEV)
@ -805,8 +808,8 @@ static const struct vm_operations_struct afu_vma_ops = {
static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct platform_device *pdev = filp->private_data;
struct dfl_feature_platform_data *pdata;
u64 size = vma->vm_end - vma->vm_start;
struct dfl_feature_dev_data *fdata;
struct dfl_afu_mmio_region region;
u64 offset;
int ret;
@ -814,10 +817,10 @@ static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
if (!(vma->vm_flags & VM_SHARED))
return -EINVAL;
pdata = dev_get_platdata(&pdev->dev);
fdata = to_dfl_feature_dev_data(&pdev->dev);
offset = vma->vm_pgoff << PAGE_SHIFT;
ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
ret = afu_mmio_region_get_by_offset(fdata, offset, size, &region);
if (ret)
return ret;
@ -851,46 +854,45 @@ static const struct file_operations afu_fops = {
static int afu_dev_init(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct dfl_afu *afu;
afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
if (!afu)
return -ENOMEM;
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, afu);
afu_mmio_region_init(pdata);
afu_dma_region_init(pdata);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
dfl_fpga_fdata_set_private(fdata, afu);
afu_mmio_region_init(fdata);
afu_dma_region_init(fdata);
mutex_unlock(&fdata->lock);
return 0;
}
static int afu_dev_destroy(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
mutex_lock(&pdata->lock);
afu_mmio_region_destroy(pdata);
afu_dma_region_destroy(pdata);
dfl_fpga_pdata_set_private(pdata, NULL);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
afu_mmio_region_destroy(fdata);
afu_dma_region_destroy(fdata);
dfl_fpga_fdata_set_private(fdata, NULL);
mutex_unlock(&fdata->lock);
return 0;
}
static int port_enable_set(struct platform_device *pdev, bool enable)
static int port_enable_set(struct dfl_feature_dev_data *fdata, bool enable)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret;
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
if (enable)
ret = __afu_port_enable(pdev);
ret = __afu_port_enable(fdata);
else
ret = __afu_port_disable(pdev);
mutex_unlock(&pdata->lock);
ret = __afu_port_disable(fdata);
mutex_unlock(&fdata->lock);
return ret;
}

View file

@ -12,11 +12,11 @@
/**
* afu_mmio_region_init - init function for afu mmio region support
* @pdata: afu platform device's pdata.
* @fdata: afu feature dev data
*/
void afu_mmio_region_init(struct dfl_feature_platform_data *pdata)
void afu_mmio_region_init(struct dfl_feature_dev_data *fdata)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
INIT_LIST_HEAD(&afu->regions);
}
@ -39,7 +39,7 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
/**
* afu_mmio_region_add - add a mmio region to given feature dev.
*
* @pdata: afu platform device's pdata.
* @fdata: afu feature dev data
* @region_index: region index.
* @region_size: region size.
* @phys: region's physical address of this region.
@ -47,14 +47,15 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
*
* Return: 0 on success, negative error code otherwise.
*/
int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
int afu_mmio_region_add(struct dfl_feature_dev_data *fdata,
u32 region_index, u64 region_size, u64 phys, u32 flags)
{
struct device *dev = &fdata->dev->dev;
struct dfl_afu_mmio_region *region;
struct dfl_afu *afu;
int ret = 0;
region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL);
region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
@ -63,13 +64,13 @@ int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
region->phys = phys;
region->flags = flags;
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
afu = dfl_fpga_pdata_get_private(pdata);
afu = dfl_fpga_fdata_get_private(fdata);
/* check if @index already exists */
if (get_region_by_index(afu, region_index)) {
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
ret = -EEXIST;
goto exit;
}
@ -80,37 +81,37 @@ int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
afu->region_cur_offset += region_size;
afu->num_regions++;
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return 0;
exit:
devm_kfree(&pdata->dev->dev, region);
devm_kfree(dev, region);
return ret;
}
/**
* afu_mmio_region_destroy - destroy all mmio regions under given feature dev.
* @pdata: afu platform device's pdata.
* @fdata: afu feature dev data
*/
void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata)
void afu_mmio_region_destroy(struct dfl_feature_dev_data *fdata)
{
struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
struct dfl_afu *afu = dfl_fpga_fdata_get_private(fdata);
struct dfl_afu_mmio_region *tmp, *region;
list_for_each_entry_safe(region, tmp, &afu->regions, node)
devm_kfree(&pdata->dev->dev, region);
devm_kfree(&fdata->dev->dev, region);
}
/**
* afu_mmio_region_get_by_index - find an afu region by index.
* @pdata: afu platform device's pdata.
* @fdata: afu feature dev data
* @region_index: region index.
* @pregion: ptr to region for result.
*
* Return: 0 on success, negative error code otherwise.
*/
int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
int afu_mmio_region_get_by_index(struct dfl_feature_dev_data *fdata,
u32 region_index,
struct dfl_afu_mmio_region *pregion)
{
@ -118,8 +119,8 @@ int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
struct dfl_afu *afu;
int ret = 0;
mutex_lock(&pdata->lock);
afu = dfl_fpga_pdata_get_private(pdata);
mutex_lock(&fdata->lock);
afu = dfl_fpga_fdata_get_private(fdata);
region = get_region_by_index(afu, region_index);
if (!region) {
ret = -EINVAL;
@ -127,14 +128,14 @@ int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
}
*pregion = *region;
exit:
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret;
}
/**
* afu_mmio_region_get_by_offset - find an afu mmio region by offset and size
*
* @pdata: afu platform device's pdata.
* @fdata: afu feature dev data
* @offset: region offset from start of the device fd.
* @size: region size.
* @pregion: ptr to region for result.
@ -144,7 +145,7 @@ exit:
*
* Return: 0 on success, negative error code otherwise.
*/
int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
int afu_mmio_region_get_by_offset(struct dfl_feature_dev_data *fdata,
u64 offset, u64 size,
struct dfl_afu_mmio_region *pregion)
{
@ -152,8 +153,8 @@ int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
struct dfl_afu *afu;
int ret = 0;
mutex_lock(&pdata->lock);
afu = dfl_fpga_pdata_get_private(pdata);
mutex_lock(&fdata->lock);
afu = dfl_fpga_fdata_get_private(fdata);
for_each_region(region, afu)
if (region->offset <= offset &&
region->offset + region->size >= offset + size) {
@ -162,6 +163,6 @@ int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
}
ret = -EINVAL;
exit:
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret;
}

View file

@ -76,27 +76,27 @@ struct dfl_afu {
struct rb_root dma_regions;
};
/* hold pdata->lock when call __afu_port_enable/disable */
int __afu_port_enable(struct platform_device *pdev);
int __afu_port_disable(struct platform_device *pdev);
/* hold fdata->lock when call __afu_port_enable/disable */
int __afu_port_enable(struct dfl_feature_dev_data *fdata);
int __afu_port_disable(struct dfl_feature_dev_data *fdata);
void afu_mmio_region_init(struct dfl_feature_platform_data *pdata);
int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
void afu_mmio_region_init(struct dfl_feature_dev_data *fdata);
int afu_mmio_region_add(struct dfl_feature_dev_data *fdata,
u32 region_index, u64 region_size, u64 phys, u32 flags);
void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata);
int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
void afu_mmio_region_destroy(struct dfl_feature_dev_data *fdata);
int afu_mmio_region_get_by_index(struct dfl_feature_dev_data *fdata,
u32 region_index,
struct dfl_afu_mmio_region *pregion);
int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
int afu_mmio_region_get_by_offset(struct dfl_feature_dev_data *fdata,
u64 offset, u64 size,
struct dfl_afu_mmio_region *pregion);
void afu_dma_region_init(struct dfl_feature_platform_data *pdata);
void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata);
int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
void afu_dma_region_init(struct dfl_feature_dev_data *fdata);
void afu_dma_region_destroy(struct dfl_feature_dev_data *fdata);
int afu_dma_map_region(struct dfl_feature_dev_data *fdata,
u64 user_addr, u64 length, u64 *iova);
int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova);
int afu_dma_unmap_region(struct dfl_feature_dev_data *fdata, u64 iova);
struct dfl_afu_dma_region *
afu_dma_region_find(struct dfl_feature_platform_data *pdata,
afu_dma_region_find(struct dfl_feature_dev_data *fdata,
u64 iova, u64 size);
extern const struct dfl_feature_ops port_err_ops;

View file

@ -22,34 +22,34 @@
struct fme_br_priv {
struct dfl_fme_br_pdata *pdata;
struct dfl_fpga_port_ops *port_ops;
struct platform_device *port_pdev;
struct dfl_feature_dev_data *port_fdata;
};
static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
{
struct fme_br_priv *priv = bridge->priv;
struct platform_device *port_pdev;
struct dfl_feature_dev_data *port_fdata;
struct dfl_fpga_port_ops *ops;
if (!priv->port_pdev) {
port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
&priv->pdata->port_id,
dfl_fpga_check_port_id);
if (!port_pdev)
if (!priv->port_fdata) {
port_fdata = dfl_fpga_cdev_find_port_data(priv->pdata->cdev,
&priv->pdata->port_id,
dfl_fpga_check_port_id);
if (!port_fdata)
return -ENODEV;
priv->port_pdev = port_pdev;
priv->port_fdata = port_fdata;
}
if (priv->port_pdev && !priv->port_ops) {
ops = dfl_fpga_port_ops_get(priv->port_pdev);
if (priv->port_fdata && !priv->port_ops) {
ops = dfl_fpga_port_ops_get(priv->port_fdata);
if (!ops || !ops->enable_set)
return -ENOENT;
priv->port_ops = ops;
}
return priv->port_ops->enable_set(priv->port_pdev, enable);
return priv->port_ops->enable_set(priv->port_fdata, enable);
}
static const struct fpga_bridge_ops fme_bridge_ops = {
@ -85,8 +85,6 @@ static void fme_br_remove(struct platform_device *pdev)
fpga_bridge_unregister(br);
if (priv->port_pdev)
put_device(&priv->port_pdev->dev);
if (priv->port_ops)
dfl_fpga_port_ops_put(priv->port_ops);
}

View file

@ -42,15 +42,15 @@
static ssize_t pcie0_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 value;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
value = readq(base + PCIE0_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)value);
}
@ -59,7 +59,7 @@ static ssize_t pcie0_errors_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
int ret = 0;
u64 v, val;
@ -67,9 +67,9 @@ static ssize_t pcie0_errors_store(struct device *dev,
if (kstrtou64(buf, 0, &val))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(GENMASK_ULL(63, 0), base + PCIE0_ERROR_MASK);
v = readq(base + PCIE0_ERROR);
@ -79,7 +79,7 @@ static ssize_t pcie0_errors_store(struct device *dev,
ret = -EINVAL;
writeq(0ULL, base + PCIE0_ERROR_MASK);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret ? ret : count;
}
static DEVICE_ATTR_RW(pcie0_errors);
@ -87,15 +87,15 @@ static DEVICE_ATTR_RW(pcie0_errors);
static ssize_t pcie1_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 value;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
value = readq(base + PCIE1_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)value);
}
@ -104,7 +104,7 @@ static ssize_t pcie1_errors_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
int ret = 0;
u64 v, val;
@ -112,9 +112,9 @@ static ssize_t pcie1_errors_store(struct device *dev,
if (kstrtou64(buf, 0, &val))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(GENMASK_ULL(63, 0), base + PCIE1_ERROR_MASK);
v = readq(base + PCIE1_ERROR);
@ -124,7 +124,7 @@ static ssize_t pcie1_errors_store(struct device *dev,
ret = -EINVAL;
writeq(0ULL, base + PCIE1_ERROR_MASK);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret ? ret : count;
}
static DEVICE_ATTR_RW(pcie1_errors);
@ -132,9 +132,10 @@ static DEVICE_ATTR_RW(pcie1_errors);
static ssize_t nonfatal_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
return sprintf(buf, "0x%llx\n",
(unsigned long long)readq(base + RAS_NONFAT_ERROR));
@ -144,9 +145,10 @@ static DEVICE_ATTR_RO(nonfatal_errors);
static ssize_t catfatal_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
return sprintf(buf, "0x%llx\n",
(unsigned long long)readq(base + RAS_CATFAT_ERROR));
@ -156,15 +158,15 @@ static DEVICE_ATTR_RO(catfatal_errors);
static ssize_t inject_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + RAS_ERROR_INJECT);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n",
(unsigned long long)FIELD_GET(INJECT_ERROR_MASK, v));
@ -174,7 +176,7 @@ static ssize_t inject_errors_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u8 inject_error;
u64 v;
@ -185,14 +187,14 @@ static ssize_t inject_errors_store(struct device *dev,
if (inject_error & ~INJECT_ERROR_MASK)
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
v = readq(base + RAS_ERROR_INJECT);
v &= ~INJECT_ERROR_MASK;
v |= FIELD_PREP(INJECT_ERROR_MASK, inject_error);
writeq(v, base + RAS_ERROR_INJECT);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return count;
}
@ -201,15 +203,15 @@ static DEVICE_ATTR_RW(inject_errors);
static ssize_t fme_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 value;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
value = readq(base + FME_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)value);
}
@ -218,7 +220,7 @@ static ssize_t fme_errors_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v, val;
int ret = 0;
@ -226,9 +228,9 @@ static ssize_t fme_errors_store(struct device *dev,
if (kstrtou64(buf, 0, &val))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
writeq(GENMASK_ULL(63, 0), base + FME_ERROR_MASK);
v = readq(base + FME_ERROR);
@ -240,7 +242,7 @@ static ssize_t fme_errors_store(struct device *dev,
/* Workaround: disable MBP_ERROR if feature revision is 0 */
writeq(dfl_feature_revision(base) ? 0ULL : MBP_ERROR,
base + FME_ERROR_MASK);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret ? ret : count;
}
static DEVICE_ATTR_RW(fme_errors);
@ -248,15 +250,15 @@ static DEVICE_ATTR_RW(fme_errors);
static ssize_t first_error_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 value;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
value = readq(base + FME_FIRST_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)value);
}
@ -265,15 +267,15 @@ static DEVICE_ATTR_RO(first_error);
static ssize_t next_error_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 value;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
value = readq(base + FME_NEXT_ERROR);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return sprintf(buf, "0x%llx\n", (unsigned long long)value);
}
@ -295,12 +297,14 @@ static umode_t fme_global_err_attrs_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct dfl_feature_dev_data *fdata;
fdata = to_dfl_feature_dev_data(dev);
/*
* sysfs entries are visible only if related private feature is
* enumerated.
*/
if (!dfl_get_feature_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR))
if (!dfl_get_feature_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR))
return 0;
return attr->mode;
@ -314,12 +318,12 @@ const struct attribute_group fme_global_err_group = {
static void fme_err_mask(struct device *dev, bool mask)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_GLOBAL_ERR);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
/* Workaround: keep MBP_ERROR always masked if revision is 0 */
if (dfl_feature_revision(base))
@ -332,7 +336,7 @@ static void fme_err_mask(struct device *dev, bool mask)
writeq(mask ? ERROR_MASK : 0, base + RAS_NONFAT_ERROR_MASK);
writeq(mask ? ERROR_MASK : 0, base + RAS_CATFAT_ERROR_MASK);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
}
static int fme_global_err_init(struct platform_device *pdev,

View file

@ -28,10 +28,11 @@
static ssize_t ports_num_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_CAP);
@ -47,10 +48,11 @@ static DEVICE_ATTR_RO(ports_num);
static ssize_t bitstream_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_BITSTREAM_ID);
@ -65,10 +67,11 @@ static DEVICE_ATTR_RO(bitstream_id);
static ssize_t bitstream_metadata_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_BITSTREAM_MD);
@ -79,10 +82,11 @@ static DEVICE_ATTR_RO(bitstream_metadata);
static ssize_t cache_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_CAP);
@ -94,10 +98,11 @@ static DEVICE_ATTR_RO(cache_size);
static ssize_t fabric_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_CAP);
@ -109,10 +114,11 @@ static DEVICE_ATTR_RO(fabric_version);
static ssize_t socket_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_CAP);
@ -135,10 +141,10 @@ static const struct attribute_group fme_hdr_group = {
.attrs = fme_hdr_attrs,
};
static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata,
static long fme_hdr_ioctl_release_port(struct dfl_feature_dev_data *fdata,
unsigned long arg)
{
struct dfl_fpga_cdev *cdev = pdata->dfl_cdev;
struct dfl_fpga_cdev *cdev = fdata->dfl_cdev;
int port_id;
if (get_user(port_id, (int __user *)arg))
@ -147,10 +153,10 @@ static long fme_hdr_ioctl_release_port(struct dfl_feature_platform_data *pdata,
return dfl_fpga_cdev_release_port(cdev, port_id);
}
static long fme_hdr_ioctl_assign_port(struct dfl_feature_platform_data *pdata,
static long fme_hdr_ioctl_assign_port(struct dfl_feature_dev_data *fdata,
unsigned long arg)
{
struct dfl_fpga_cdev *cdev = pdata->dfl_cdev;
struct dfl_fpga_cdev *cdev = fdata->dfl_cdev;
int port_id;
if (get_user(port_id, (int __user *)arg))
@ -163,13 +169,13 @@ static long fme_hdr_ioctl(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned int cmd, unsigned long arg)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
switch (cmd) {
case DFL_FPGA_FME_PORT_RELEASE:
return fme_hdr_ioctl_release_port(pdata, arg);
return fme_hdr_ioctl_release_port(fdata, arg);
case DFL_FPGA_FME_PORT_ASSIGN:
return fme_hdr_ioctl_assign_port(pdata, arg);
return fme_hdr_ioctl_assign_port(fdata, arg);
}
return -ENODEV;
@ -411,14 +417,14 @@ static int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev->parent);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev->parent);
struct dfl_feature *feature = dev_get_drvdata(dev);
int ret = 0;
u64 v;
val = clamp_val(val / MICRO, 0, PWR_THRESHOLD_MAX);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
switch (attr) {
case hwmon_power_max:
@ -438,7 +444,7 @@ static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
break;
}
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret;
}
@ -589,7 +595,7 @@ static struct dfl_feature_driver fme_feature_drvs[] = {
},
};
static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
static long fme_ioctl_check_extension(struct dfl_feature_dev_data *fdata,
unsigned long arg)
{
/* No extension support for now */
@ -598,49 +604,46 @@ static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
static int fme_open(struct inode *inode, struct file *filp)
{
struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
struct dfl_feature_dev_data *fdata = dfl_fpga_inode_to_feature_dev_data(inode);
struct platform_device *fdev = fdata->dev;
int ret;
if (WARN_ON(!pdata))
return -ENODEV;
mutex_lock(&pdata->lock);
ret = dfl_feature_dev_use_begin(pdata, filp->f_flags & O_EXCL);
mutex_lock(&fdata->lock);
ret = dfl_feature_dev_use_begin(fdata, filp->f_flags & O_EXCL);
if (!ret) {
dev_dbg(&fdev->dev, "Device File Opened %d Times\n",
dfl_feature_dev_use_count(pdata));
filp->private_data = pdata;
dfl_feature_dev_use_count(fdata));
filp->private_data = fdata;
}
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret;
}
static int fme_release(struct inode *inode, struct file *filp)
{
struct dfl_feature_platform_data *pdata = filp->private_data;
struct platform_device *pdev = pdata->dev;
struct dfl_feature_dev_data *fdata = filp->private_data;
struct platform_device *pdev = fdata->dev;
struct dfl_feature *feature;
dev_dbg(&pdev->dev, "Device File Release\n");
mutex_lock(&pdata->lock);
dfl_feature_dev_use_end(pdata);
mutex_lock(&fdata->lock);
dfl_feature_dev_use_end(fdata);
if (!dfl_feature_dev_use_count(pdata))
dfl_fpga_dev_for_each_feature(pdata, feature)
if (!dfl_feature_dev_use_count(fdata))
dfl_fpga_dev_for_each_feature(fdata, feature)
dfl_fpga_set_irq_triggers(feature, 0,
feature->nr_irqs, NULL);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return 0;
}
static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct dfl_feature_platform_data *pdata = filp->private_data;
struct platform_device *pdev = pdata->dev;
struct dfl_feature_dev_data *fdata = filp->private_data;
struct platform_device *pdev = fdata->dev;
struct dfl_feature *f;
long ret;
@ -650,7 +653,7 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case DFL_FPGA_GET_API_VERSION:
return DFL_FPGA_API_VERSION;
case DFL_FPGA_CHECK_EXTENSION:
return fme_ioctl_check_extension(pdata, arg);
return fme_ioctl_check_extension(fdata, arg);
default:
/*
* Let sub-feature's ioctl function to handle the cmd.
@ -658,7 +661,7 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
* handled in this sub feature, and returns 0 or other
* error code if cmd is handled.
*/
dfl_fpga_dev_for_each_feature(pdata, f) {
dfl_fpga_dev_for_each_feature(fdata, f) {
if (f->ops && f->ops->ioctl) {
ret = f->ops->ioctl(pdev, f, cmd, arg);
if (ret != -ENODEV)
@ -672,27 +675,27 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
static int fme_dev_init(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct dfl_fme *fme;
fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL);
if (!fme)
return -ENOMEM;
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, fme);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
dfl_fpga_fdata_set_private(fdata, fme);
mutex_unlock(&fdata->lock);
return 0;
}
static void fme_dev_destroy(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, NULL);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
dfl_fpga_fdata_set_private(fdata, NULL);
mutex_unlock(&fdata->lock);
}
static const struct file_operations fme_fops = {

View file

@ -65,7 +65,7 @@ static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id)
static int fme_pr(struct platform_device *pdev, unsigned long arg)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
void __user *argp = (void __user *)arg;
struct dfl_fpga_fme_port_pr port_pr;
struct fpga_image_info *info;
@ -87,8 +87,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
return -EINVAL;
/* get fme header region */
fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev,
FME_FEATURE_ID_HEADER);
fme_hdr = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
/* check port id */
v = readq(fme_hdr + FME_HDR_CAP);
@ -123,8 +122,8 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
mutex_lock(&pdata->lock);
fme = dfl_fpga_pdata_get_private(pdata);
mutex_lock(&fdata->lock);
fme = dfl_fpga_fdata_get_private(fdata);
/* fme device has been unregistered. */
if (!fme) {
ret = -EINVAL;
@ -156,7 +155,7 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
put_device(&region->dev);
unlock_exit:
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
free_exit:
vfree(buf);
return ret;
@ -164,16 +163,16 @@ free_exit:
/**
* dfl_fme_create_mgr - create fpga mgr platform device as child device
* @fdata: fme feature dev data
* @feature: sub feature info
* @pdata: fme platform_device's pdata
*
* Return: mgr platform device if successful, and error code otherwise.
*/
static struct platform_device *
dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata,
dfl_fme_create_mgr(struct dfl_feature_dev_data *fdata,
struct dfl_feature *feature)
{
struct platform_device *mgr, *fme = pdata->dev;
struct platform_device *mgr, *fme = fdata->dev;
struct dfl_fme_mgr_pdata mgr_pdata;
int ret = -ENOMEM;
@ -209,11 +208,11 @@ create_mgr_err:
/**
* dfl_fme_destroy_mgr - destroy fpga mgr platform device
* @pdata: fme platform device's pdata
* @fdata: fme feature dev data
*/
static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata)
static void dfl_fme_destroy_mgr(struct dfl_feature_dev_data *fdata)
{
struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
struct dfl_fme *priv = dfl_fpga_fdata_get_private(fdata);
platform_device_unregister(priv->mgr);
}
@ -221,15 +220,15 @@ static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata)
/**
* dfl_fme_create_bridge - create fme fpga bridge platform device as child
*
* @pdata: fme platform device's pdata
* @fdata: fme feature dev data
* @port_id: port id for the bridge to be created.
*
* Return: bridge platform device if successful, and error code otherwise.
*/
static struct dfl_fme_bridge *
dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id)
dfl_fme_create_bridge(struct dfl_feature_dev_data *fdata, int port_id)
{
struct device *dev = &pdata->dev->dev;
struct device *dev = &fdata->dev->dev;
struct dfl_fme_br_pdata br_pdata;
struct dfl_fme_bridge *fme_br;
int ret = -ENOMEM;
@ -238,7 +237,7 @@ dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id)
if (!fme_br)
return ERR_PTR(ret);
br_pdata.cdev = pdata->dfl_cdev;
br_pdata.cdev = fdata->dfl_cdev;
br_pdata.port_id = port_id;
fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE,
@ -274,11 +273,11 @@ static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br)
/**
* dfl_fme_destroy_bridges - destroy all fpga bridge platform device
* @pdata: fme platform device's pdata
* @fdata: fme feature dev data
*/
static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata)
static void dfl_fme_destroy_bridges(struct dfl_feature_dev_data *fdata)
{
struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
struct dfl_fme *priv = dfl_fpga_fdata_get_private(fdata);
struct dfl_fme_bridge *fbridge, *tmp;
list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) {
@ -290,7 +289,7 @@ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata)
/**
* dfl_fme_create_region - create fpga region platform device as child
*
* @pdata: fme platform device's pdata
* @fdata: fme feature dev data
* @mgr: mgr platform device needed for region
* @br: br platform device needed for region
* @port_id: port id
@ -298,12 +297,12 @@ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata)
* Return: fme region if successful, and error code otherwise.
*/
static struct dfl_fme_region *
dfl_fme_create_region(struct dfl_feature_platform_data *pdata,
dfl_fme_create_region(struct dfl_feature_dev_data *fdata,
struct platform_device *mgr,
struct platform_device *br, int port_id)
{
struct dfl_fme_region_pdata region_pdata;
struct device *dev = &pdata->dev->dev;
struct device *dev = &fdata->dev->dev;
struct dfl_fme_region *fme_region;
int ret = -ENOMEM;
@ -353,11 +352,11 @@ static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region)
/**
* dfl_fme_destroy_regions - destroy all fme regions
* @pdata: fme platform device's pdata
* @fdata: fme feature dev data
*/
static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata)
static void dfl_fme_destroy_regions(struct dfl_feature_dev_data *fdata)
{
struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
struct dfl_fme *priv = dfl_fpga_fdata_get_private(fdata);
struct dfl_fme_region *fme_region, *tmp;
list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) {
@ -369,7 +368,7 @@ static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata)
static int pr_mgmt_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct dfl_fme_region *fme_region;
struct dfl_fme_bridge *fme_br;
struct platform_device *mgr;
@ -378,18 +377,17 @@ static int pr_mgmt_init(struct platform_device *pdev,
int ret = -ENODEV, i = 0;
u64 fme_cap, port_offset;
fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev,
FME_FEATURE_ID_HEADER);
fme_hdr = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
priv = dfl_fpga_pdata_get_private(pdata);
mutex_lock(&fdata->lock);
priv = dfl_fpga_fdata_get_private(fdata);
/* Initialize the region and bridge sub device list */
INIT_LIST_HEAD(&priv->region_list);
INIT_LIST_HEAD(&priv->bridge_list);
/* Create fpga mgr platform device */
mgr = dfl_fme_create_mgr(pdata, feature);
mgr = dfl_fme_create_mgr(fdata, feature);
if (IS_ERR(mgr)) {
dev_err(&pdev->dev, "fail to create fpga mgr pdev\n");
goto unlock;
@ -405,7 +403,7 @@ static int pr_mgmt_init(struct platform_device *pdev,
continue;
/* Create bridge for each port */
fme_br = dfl_fme_create_bridge(pdata, i);
fme_br = dfl_fme_create_bridge(fdata, i);
if (IS_ERR(fme_br)) {
ret = PTR_ERR(fme_br);
goto destroy_region;
@ -414,7 +412,7 @@ static int pr_mgmt_init(struct platform_device *pdev,
list_add(&fme_br->node, &priv->bridge_list);
/* Create region for each port */
fme_region = dfl_fme_create_region(pdata, mgr,
fme_region = dfl_fme_create_region(fdata, mgr,
fme_br->br, i);
if (IS_ERR(fme_region)) {
ret = PTR_ERR(fme_region);
@ -423,30 +421,30 @@ static int pr_mgmt_init(struct platform_device *pdev,
list_add(&fme_region->node, &priv->region_list);
}
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return 0;
destroy_region:
dfl_fme_destroy_regions(pdata);
dfl_fme_destroy_bridges(pdata);
dfl_fme_destroy_mgr(pdata);
dfl_fme_destroy_regions(fdata);
dfl_fme_destroy_bridges(fdata);
dfl_fme_destroy_mgr(fdata);
unlock:
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
return ret;
}
static void pr_mgmt_uinit(struct platform_device *pdev,
struct dfl_feature *feature)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
dfl_fme_destroy_regions(pdata);
dfl_fme_destroy_bridges(pdata);
dfl_fme_destroy_mgr(pdata);
mutex_unlock(&pdata->lock);
dfl_fme_destroy_regions(fdata);
dfl_fme_destroy_bridges(fdata);
dfl_fme_destroy_mgr(fdata);
mutex_unlock(&fdata->lock);
}
static long fme_pr_ioctl(struct platform_device *pdev,

View file

@ -119,17 +119,6 @@ static void dfl_id_free(enum dfl_id_type type, int id)
mutex_unlock(&dfl_id_mutex);
}
static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(dfl_devs); i++)
if (!strcmp(dfl_devs[i].name, pdev->name))
return i;
return DFL_ID_MAX;
}
static enum dfl_id_type dfh_id_to_type(u16 id)
{
int i;
@ -156,12 +145,12 @@ static LIST_HEAD(dfl_port_ops_list);
/**
* dfl_fpga_port_ops_get - get matched port ops from the global list
* @pdev: platform device to match with associated port ops.
* @fdata: feature dev data to match with associated port ops.
* Return: matched port ops on success, NULL otherwise.
*
* Please note that must dfl_fpga_port_ops_put after use the port_ops.
*/
struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev)
struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct dfl_feature_dev_data *fdata)
{
struct dfl_fpga_port_ops *ops = NULL;
@ -171,7 +160,7 @@ struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev)
list_for_each_entry(ops, &dfl_port_ops_list, node) {
/* match port_ops using the name of platform device */
if (!strcmp(pdev->name, ops->name)) {
if (!strcmp(fdata->pdev_name, ops->name)) {
if (!try_module_get(ops->owner))
ops = NULL;
goto done;
@ -222,27 +211,26 @@ EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_del);
/**
* dfl_fpga_check_port_id - check the port id
* @pdev: port platform device.
* @fdata: port feature dev data.
* @pport_id: port id to compare.
*
* Return: 1 if port device matches with given port id, otherwise 0.
*/
int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id)
int dfl_fpga_check_port_id(struct dfl_feature_dev_data *fdata, void *pport_id)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_fpga_port_ops *port_ops;
if (pdata->id != FEATURE_DEV_ID_UNUSED)
return pdata->id == *(int *)pport_id;
if (fdata->id != FEATURE_DEV_ID_UNUSED)
return fdata->id == *(int *)pport_id;
port_ops = dfl_fpga_port_ops_get(pdev);
port_ops = dfl_fpga_port_ops_get(fdata);
if (!port_ops || !port_ops->get_id)
return 0;
pdata->id = port_ops->get_id(pdev);
fdata->id = port_ops->get_id(fdata);
dfl_fpga_port_ops_put(port_ops);
return pdata->id == *(int *)pport_id;
return fdata->id == *(int *)pport_id;
}
EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id);
@ -351,10 +339,10 @@ static void release_dfl_dev(struct device *dev)
}
static struct dfl_device *
dfl_dev_add(struct dfl_feature_platform_data *pdata,
dfl_dev_add(struct dfl_feature_dev_data *fdata,
struct dfl_feature *feature)
{
struct platform_device *pdev = pdata->dev;
struct platform_device *pdev = fdata->dev;
struct resource *parent_res;
struct dfl_device *ddev;
int id, i, ret;
@ -380,11 +368,11 @@ dfl_dev_add(struct dfl_feature_platform_data *pdata,
if (ret)
goto put_dev;
ddev->type = feature_dev_id_type(pdev);
ddev->type = fdata->type;
ddev->feature_id = feature->id;
ddev->revision = feature->revision;
ddev->dfh_version = feature->dfh_version;
ddev->cdev = pdata->dfl_cdev;
ddev->cdev = fdata->dfl_cdev;
if (feature->param_size) {
ddev->params = kmemdup(feature->params, feature->param_size, GFP_KERNEL);
if (!ddev->params) {
@ -435,11 +423,11 @@ put_dev:
return ERR_PTR(ret);
}
static void dfl_devs_remove(struct dfl_feature_platform_data *pdata)
static void dfl_devs_remove(struct dfl_feature_dev_data *fdata)
{
struct dfl_feature *feature;
dfl_fpga_dev_for_each_feature(pdata, feature) {
dfl_fpga_dev_for_each_feature(fdata, feature) {
if (feature->ddev) {
device_unregister(&feature->ddev->dev);
feature->ddev = NULL;
@ -447,13 +435,13 @@ static void dfl_devs_remove(struct dfl_feature_platform_data *pdata)
}
}
static int dfl_devs_add(struct dfl_feature_platform_data *pdata)
static int dfl_devs_add(struct dfl_feature_dev_data *fdata)
{
struct dfl_feature *feature;
struct dfl_device *ddev;
int ret;
dfl_fpga_dev_for_each_feature(pdata, feature) {
dfl_fpga_dev_for_each_feature(fdata, feature) {
if (feature->ioaddr)
continue;
@ -462,7 +450,7 @@ static int dfl_devs_add(struct dfl_feature_platform_data *pdata)
goto err;
}
ddev = dfl_dev_add(pdata, feature);
ddev = dfl_dev_add(fdata, feature);
if (IS_ERR(ddev)) {
ret = PTR_ERR(ddev);
goto err;
@ -474,7 +462,7 @@ static int dfl_devs_add(struct dfl_feature_platform_data *pdata)
return 0;
err:
dfl_devs_remove(pdata);
dfl_devs_remove(fdata);
return ret;
}
@ -504,12 +492,12 @@ EXPORT_SYMBOL(dfl_driver_unregister);
*/
void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct dfl_feature *feature;
dfl_devs_remove(pdata);
dfl_devs_remove(fdata);
dfl_fpga_dev_for_each_feature(pdata, feature) {
dfl_fpga_dev_for_each_feature(fdata, feature) {
if (feature->ops) {
if (feature->ops->uinit)
feature->ops->uinit(pdev, feature);
@ -520,7 +508,6 @@ void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit);
static int dfl_feature_instance_init(struct platform_device *pdev,
struct dfl_feature_platform_data *pdata,
struct dfl_feature *feature,
struct dfl_feature_driver *drv)
{
@ -579,16 +566,15 @@ static bool dfl_feature_drv_match(struct dfl_feature *feature,
int dfl_fpga_dev_feature_init(struct platform_device *pdev,
struct dfl_feature_driver *feature_drvs)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct dfl_feature_driver *drv = feature_drvs;
struct dfl_feature *feature;
int ret;
while (drv->ops) {
dfl_fpga_dev_for_each_feature(pdata, feature) {
dfl_fpga_dev_for_each_feature(fdata, feature) {
if (dfl_feature_drv_match(feature, drv)) {
ret = dfl_feature_instance_init(pdev, pdata,
feature, drv);
ret = dfl_feature_instance_init(pdev, feature, drv);
if (ret)
goto exit;
}
@ -596,7 +582,7 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev,
drv++;
}
ret = dfl_devs_add(pdata);
ret = dfl_devs_add(fdata);
if (ret)
goto exit;
@ -695,7 +681,7 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister);
* @nr_irqs: number of irqs for all feature devices.
* @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of
* this device.
* @feature_dev: current feature device.
* @type: the current FIU type.
* @ioaddr: header register region address of current FIU in enumeration.
* @start: register resource start of current FIU.
* @len: max register resource length of current FIU.
@ -708,7 +694,7 @@ struct build_feature_devs_info {
unsigned int nr_irqs;
int *irq_table;
struct platform_device *feature_dev;
enum dfl_id_type type;
void __iomem *ioaddr;
resource_size_t start;
resource_size_t len;
@ -743,50 +729,62 @@ struct dfl_feature_info {
u64 params[];
};
static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev,
struct platform_device *port)
static void dfl_fpga_cdev_add_port_data(struct dfl_fpga_cdev *cdev,
struct dfl_feature_dev_data *fdata)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&port->dev);
mutex_lock(&cdev->lock);
list_add(&pdata->node, &cdev->port_dev_list);
get_device(&pdata->dev->dev);
list_add(&fdata->node, &cdev->port_dev_list);
mutex_unlock(&cdev->lock);
}
/*
* register current feature device, it is called when we need to switch to
* another feature parsing or we have parsed all features on given device
* feature list.
*/
static int build_info_commit_dev(struct build_feature_devs_info *binfo)
static void dfl_id_free_action(void *arg)
{
struct platform_device *fdev = binfo->feature_dev;
struct dfl_feature_platform_data *pdata;
struct dfl_feature_dev_data *fdata = arg;
dfl_id_free(fdata->type, fdata->pdev_id);
}
static struct dfl_feature_dev_data *
binfo_create_feature_dev_data(struct build_feature_devs_info *binfo)
{
enum dfl_id_type type = binfo->type;
struct dfl_feature_info *finfo, *p;
enum dfl_id_type type;
struct dfl_feature_dev_data *fdata;
int ret, index = 0, res_idx = 0;
type = feature_dev_id_type(fdev);
if (WARN_ON_ONCE(type >= DFL_ID_MAX))
return -EINVAL;
return ERR_PTR(-EINVAL);
/*
* we do not need to care for the memory which is associated with
* the platform device. After calling platform_device_unregister(),
* it will be automatically freed by device's release() callback,
* platform_device_release().
*/
pdata = kzalloc(struct_size(pdata, features, binfo->feature_num), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
fdata = devm_kzalloc(binfo->dev, sizeof(*fdata), GFP_KERNEL);
if (!fdata)
return ERR_PTR(-ENOMEM);
pdata->dev = fdev;
pdata->num = binfo->feature_num;
pdata->dfl_cdev = binfo->cdev;
pdata->id = FEATURE_DEV_ID_UNUSED;
mutex_init(&pdata->lock);
lockdep_set_class_and_name(&pdata->lock, &dfl_pdata_keys[type],
fdata->features = devm_kcalloc(binfo->dev, binfo->feature_num,
sizeof(*fdata->features), GFP_KERNEL);
if (!fdata->features)
return ERR_PTR(-ENOMEM);
fdata->resources = devm_kcalloc(binfo->dev, binfo->feature_num,
sizeof(*fdata->resources), GFP_KERNEL);
if (!fdata->resources)
return ERR_PTR(-ENOMEM);
fdata->type = type;
fdata->pdev_id = dfl_id_alloc(type, binfo->dev);
if (fdata->pdev_id < 0)
return ERR_PTR(fdata->pdev_id);
ret = devm_add_action_or_reset(binfo->dev, dfl_id_free_action, fdata);
if (ret)
return ERR_PTR(ret);
fdata->pdev_name = dfl_devs[type].name;
fdata->num = binfo->feature_num;
fdata->dfl_cdev = binfo->cdev;
fdata->id = FEATURE_DEV_ID_UNUSED;
mutex_init(&fdata->lock);
lockdep_set_class_and_name(&fdata->lock, &dfl_pdata_keys[type],
dfl_pdata_key_strings[type]);
/*
@ -795,25 +793,15 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
* works properly for port device.
* and it should always be 0 for fme device.
*/
WARN_ON(pdata->disable_count);
fdev->dev.platform_data = pdata;
/* each sub feature has one MMIO resource */
fdev->num_resources = binfo->feature_num;
fdev->resource = kcalloc(binfo->feature_num, sizeof(*fdev->resource),
GFP_KERNEL);
if (!fdev->resource)
return -ENOMEM;
WARN_ON(fdata->disable_count);
/* fill features and resource information for feature dev */
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
struct dfl_feature *feature = &pdata->features[index++];
struct dfl_feature *feature = &fdata->features[index++];
struct dfl_feature_irq_ctx *ctx;
unsigned int i;
/* save resource information for each feature */
feature->dev = fdev;
feature->id = finfo->fid;
feature->revision = finfo->revision;
feature->dfh_version = finfo->dfh_version;
@ -823,7 +811,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
finfo->params, finfo->param_size,
GFP_KERNEL);
if (!feature->params)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
feature->param_size = finfo->param_size;
}
@ -840,17 +828,17 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
devm_ioremap_resource(binfo->dev,
&finfo->mmio_res);
if (IS_ERR(feature->ioaddr))
return PTR_ERR(feature->ioaddr);
return ERR_CAST(feature->ioaddr);
} else {
feature->resource_index = res_idx;
fdev->resource[res_idx++] = finfo->mmio_res;
fdata->resources[res_idx++] = finfo->mmio_res;
}
if (finfo->nr_irqs) {
ctx = devm_kcalloc(binfo->dev, finfo->nr_irqs,
sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
for (i = 0; i < finfo->nr_irqs; i++)
ctx[i].irq =
@ -864,55 +852,94 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
kfree(finfo);
}
ret = platform_device_add(binfo->feature_dev);
if (!ret) {
if (type == PORT_ID)
dfl_fpga_cdev_add_port_dev(binfo->cdev,
binfo->feature_dev);
else
binfo->cdev->fme_dev =
get_device(&binfo->feature_dev->dev);
/*
* reset it to avoid build_info_free() freeing their resource.
*
* The resource of successfully registered feature devices
* will be freed by platform_device_unregister(). See the
* comments in build_info_create_dev().
*/
binfo->feature_dev = NULL;
}
fdata->resource_num = res_idx;
return fdata;
}
/*
* register current feature device, it is called when we need to switch to
* another feature parsing or we have parsed all features on given device
* feature list.
*/
static int feature_dev_register(struct dfl_feature_dev_data *fdata)
{
struct dfl_feature_platform_data pdata = {};
struct platform_device *fdev;
struct dfl_feature *feature;
int ret;
fdev = platform_device_alloc(fdata->pdev_name, fdata->pdev_id);
if (!fdev)
return -ENOMEM;
fdata->dev = fdev;
fdev->dev.parent = &fdata->dfl_cdev->region->dev;
fdev->dev.devt = dfl_get_devt(dfl_devs[fdata->type].devt_type, fdev->id);
dfl_fpga_dev_for_each_feature(fdata, feature)
feature->dev = fdev;
ret = platform_device_add_resources(fdev, fdata->resources,
fdata->resource_num);
if (ret)
goto err_put_dev;
pdata.fdata = fdata;
ret = platform_device_add_data(fdev, &pdata, sizeof(pdata));
if (ret)
goto err_put_dev;
ret = platform_device_add(fdev);
if (ret)
goto err_put_dev;
return 0;
err_put_dev:
platform_device_put(fdev);
fdata->dev = NULL;
dfl_fpga_dev_for_each_feature(fdata, feature)
feature->dev = NULL;
return ret;
}
static int
build_info_create_dev(struct build_feature_devs_info *binfo,
enum dfl_id_type type)
static void feature_dev_unregister(struct dfl_feature_dev_data *fdata)
{
struct platform_device *fdev;
struct dfl_feature *feature;
if (type >= DFL_ID_MAX)
return -EINVAL;
platform_device_unregister(fdata->dev);
/*
* we use -ENODEV as the initialization indicator which indicates
* whether the id need to be reclaimed
*/
fdev = platform_device_alloc(dfl_devs[type].name, -ENODEV);
if (!fdev)
return -ENOMEM;
fdata->dev = NULL;
binfo->feature_dev = fdev;
binfo->feature_num = 0;
dfl_fpga_dev_for_each_feature(fdata, feature)
feature->dev = NULL;
}
INIT_LIST_HEAD(&binfo->sub_features);
static int build_info_commit_dev(struct build_feature_devs_info *binfo)
{
struct dfl_feature_dev_data *fdata;
int ret;
fdev->id = dfl_id_alloc(type, &fdev->dev);
if (fdev->id < 0)
return fdev->id;
fdata = binfo_create_feature_dev_data(binfo);
if (IS_ERR(fdata))
return PTR_ERR(fdata);
fdev->dev.parent = &binfo->cdev->region->dev;
fdev->dev.devt = dfl_get_devt(dfl_devs[type].devt_type, fdev->id);
ret = feature_dev_register(fdata);
if (ret)
return ret;
if (binfo->type == PORT_ID)
dfl_fpga_cdev_add_port_data(binfo->cdev, fdata);
else
binfo->cdev->fme_dev = get_device(&fdata->dev->dev);
/* reset the binfo for next FIU */
binfo->type = DFL_ID_MAX;
return 0;
}
@ -921,22 +948,11 @@ static void build_info_free(struct build_feature_devs_info *binfo)
{
struct dfl_feature_info *finfo, *p;
/*
* it is a valid id, free it. See comments in
* build_info_create_dev()
*/
if (binfo->feature_dev && binfo->feature_dev->id >= 0) {
dfl_id_free(feature_dev_id_type(binfo->feature_dev),
binfo->feature_dev->id);
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
list_del(&finfo->node);
kfree(finfo);
}
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
list_del(&finfo->node);
kfree(finfo);
}
platform_device_put(binfo->feature_dev);
devm_kfree(binfo->dev, binfo);
}
@ -1025,7 +1041,7 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo,
* Instead, features with interrupt functionality provide
* the information in feature specific registers.
*/
type = feature_dev_id_type(binfo->feature_dev);
type = binfo->type;
if (type == PORT_ID) {
switch (fid) {
case PORT_FEATURE_ID_UINT:
@ -1217,7 +1233,7 @@ static int parse_feature_port_afu(struct build_feature_devs_info *binfo,
return create_feature_instance(binfo, ofst, size, FEATURE_ID_AFU);
}
#define is_feature_dev_detected(binfo) (!!(binfo)->feature_dev)
#define is_feature_dev_detected(binfo) ((binfo)->type != DFL_ID_MAX)
static int parse_feature_afu(struct build_feature_devs_info *binfo,
resource_size_t ofst)
@ -1227,12 +1243,11 @@ static int parse_feature_afu(struct build_feature_devs_info *binfo,
return -EINVAL;
}
switch (feature_dev_id_type(binfo->feature_dev)) {
switch (binfo->type) {
case PORT_ID:
return parse_feature_port_afu(binfo, ofst);
default:
dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n",
binfo->feature_dev->name);
dev_info(binfo->dev, "AFU belonging to FIU is not supported yet.\n");
}
return 0;
@ -1273,6 +1288,7 @@ static void build_info_complete(struct build_feature_devs_info *binfo)
static int parse_feature_fiu(struct build_feature_devs_info *binfo,
resource_size_t ofst)
{
enum dfl_id_type type;
int ret = 0;
u32 offset;
u16 id;
@ -1294,10 +1310,13 @@ static int parse_feature_fiu(struct build_feature_devs_info *binfo,
v = readq(binfo->ioaddr + DFH);
id = FIELD_GET(DFH_ID, v);
/* create platform device for dfl feature dev */
ret = build_info_create_dev(binfo, dfh_id_to_type(id));
if (ret)
return ret;
type = dfh_id_to_type(id);
if (type >= DFL_ID_MAX)
return -EINVAL;
binfo->type = type;
binfo->feature_num = 0;
INIT_LIST_HEAD(&binfo->sub_features);
ret = create_feature_instance(binfo, 0, 0, 0);
if (ret)
@ -1515,13 +1534,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_irq);
static int remove_feature_dev(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
enum dfl_id_type type = feature_dev_id_type(pdev);
int id = pdev->id;
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
platform_device_unregister(pdev);
dfl_id_free(type, id);
feature_dev_unregister(fdata);
return 0;
}
@ -1573,6 +1588,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
goto unregister_region_exit;
}
binfo->type = DFL_ID_MAX;
binfo->dev = info->dev;
binfo->cdev = cdev;
@ -1614,25 +1630,10 @@ EXPORT_SYMBOL_GPL(dfl_fpga_feature_devs_enumerate);
*/
void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev)
{
struct dfl_feature_platform_data *pdata, *ptmp;
mutex_lock(&cdev->lock);
if (cdev->fme_dev)
put_device(cdev->fme_dev);
list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) {
struct platform_device *port_dev = pdata->dev;
/* remove released ports */
if (!device_is_registered(&port_dev->dev)) {
dfl_id_free(feature_dev_id_type(port_dev),
port_dev->id);
platform_device_put(port_dev);
}
list_del(&pdata->node);
put_device(&port_dev->dev);
}
mutex_unlock(&cdev->lock);
remove_feature_devs(cdev);
@ -1643,7 +1644,7 @@ void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev)
EXPORT_SYMBOL_GPL(dfl_fpga_feature_devs_remove);
/**
* __dfl_fpga_cdev_find_port - find a port under given container device
* __dfl_fpga_cdev_find_port_data - find a port under given container device
*
* @cdev: container device
* @data: data passed to match function
@ -1656,23 +1657,20 @@ EXPORT_SYMBOL_GPL(dfl_fpga_feature_devs_remove);
*
* NOTE: you will need to drop the device reference with put_device() after use.
*/
struct platform_device *
__dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data,
int (*match)(struct platform_device *, void *))
struct dfl_feature_dev_data *
__dfl_fpga_cdev_find_port_data(struct dfl_fpga_cdev *cdev, void *data,
int (*match)(struct dfl_feature_dev_data *, void *))
{
struct dfl_feature_platform_data *pdata;
struct platform_device *port_dev;
struct dfl_feature_dev_data *fdata;
list_for_each_entry(pdata, &cdev->port_dev_list, node) {
port_dev = pdata->dev;
if (match(port_dev, data) && get_device(&port_dev->dev))
return port_dev;
list_for_each_entry(fdata, &cdev->port_dev_list, node) {
if (match(fdata, data))
return fdata;
}
return NULL;
}
EXPORT_SYMBOL_GPL(__dfl_fpga_cdev_find_port);
EXPORT_SYMBOL_GPL(__dfl_fpga_cdev_find_port_data);
static int __init dfl_fpga_init(void)
{
@ -1706,33 +1704,28 @@ static int __init dfl_fpga_init(void)
*/
int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id)
{
struct dfl_feature_platform_data *pdata;
struct platform_device *port_pdev;
struct dfl_feature_dev_data *fdata;
int ret = -ENODEV;
mutex_lock(&cdev->lock);
port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id,
dfl_fpga_check_port_id);
if (!port_pdev)
fdata = __dfl_fpga_cdev_find_port_data(cdev, &port_id,
dfl_fpga_check_port_id);
if (!fdata)
goto unlock_exit;
if (!device_is_registered(&port_pdev->dev)) {
if (!fdata->dev) {
ret = -EBUSY;
goto put_dev_exit;
goto unlock_exit;
}
pdata = dev_get_platdata(&port_pdev->dev);
mutex_lock(&pdata->lock);
ret = dfl_feature_dev_use_begin(pdata, true);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
ret = dfl_feature_dev_use_begin(fdata, true);
mutex_unlock(&fdata->lock);
if (ret)
goto put_dev_exit;
goto unlock_exit;
platform_device_del(port_pdev);
feature_dev_unregister(fdata);
cdev->released_port_num++;
put_dev_exit:
put_device(&port_pdev->dev);
unlock_exit:
mutex_unlock(&cdev->lock);
return ret;
@ -1752,34 +1745,29 @@ EXPORT_SYMBOL_GPL(dfl_fpga_cdev_release_port);
*/
int dfl_fpga_cdev_assign_port(struct dfl_fpga_cdev *cdev, int port_id)
{
struct dfl_feature_platform_data *pdata;
struct platform_device *port_pdev;
struct dfl_feature_dev_data *fdata;
int ret = -ENODEV;
mutex_lock(&cdev->lock);
port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id,
dfl_fpga_check_port_id);
if (!port_pdev)
fdata = __dfl_fpga_cdev_find_port_data(cdev, &port_id,
dfl_fpga_check_port_id);
if (!fdata)
goto unlock_exit;
if (device_is_registered(&port_pdev->dev)) {
if (fdata->dev) {
ret = -EBUSY;
goto put_dev_exit;
goto unlock_exit;
}
ret = platform_device_add(port_pdev);
ret = feature_dev_register(fdata);
if (ret)
goto put_dev_exit;
goto unlock_exit;
pdata = dev_get_platdata(&port_pdev->dev);
mutex_lock(&pdata->lock);
dfl_feature_dev_use_end(pdata);
mutex_unlock(&pdata->lock);
mutex_lock(&fdata->lock);
dfl_feature_dev_use_end(fdata);
mutex_unlock(&fdata->lock);
cdev->released_port_num--;
put_dev_exit:
put_device(&port_pdev->dev);
unlock_exit:
mutex_unlock(&cdev->lock);
return ret;
@ -1789,10 +1777,11 @@ EXPORT_SYMBOL_GPL(dfl_fpga_cdev_assign_port);
static void config_port_access_mode(struct device *fme_dev, int port_id,
bool is_vf)
{
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(fme_dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(fme_dev, FME_FEATURE_ID_HEADER);
base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_HEADER);
v = readq(base + FME_HDR_PORT_OFST(port_id));
@ -1816,14 +1805,14 @@ static void config_port_access_mode(struct device *fme_dev, int port_id,
*/
void dfl_fpga_cdev_config_ports_pf(struct dfl_fpga_cdev *cdev)
{
struct dfl_feature_platform_data *pdata;
struct dfl_feature_dev_data *fdata;
mutex_lock(&cdev->lock);
list_for_each_entry(pdata, &cdev->port_dev_list, node) {
if (device_is_registered(&pdata->dev->dev))
list_for_each_entry(fdata, &cdev->port_dev_list, node) {
if (fdata->dev)
continue;
config_port_pf_mode(cdev->fme_dev, pdata->id);
config_port_pf_mode(cdev->fme_dev, fdata->id);
}
mutex_unlock(&cdev->lock);
}
@ -1842,7 +1831,7 @@ EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_pf);
*/
int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs)
{
struct dfl_feature_platform_data *pdata;
struct dfl_feature_dev_data *fdata;
int ret = 0;
mutex_lock(&cdev->lock);
@ -1856,11 +1845,11 @@ int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs)
goto done;
}
list_for_each_entry(pdata, &cdev->port_dev_list, node) {
if (device_is_registered(&pdata->dev->dev))
list_for_each_entry(fdata, &cdev->port_dev_list, node) {
if (fdata->dev)
continue;
config_port_vf_mode(cdev->fme_dev, pdata->id);
config_port_vf_mode(cdev->fme_dev, fdata->id);
}
done:
mutex_unlock(&cdev->lock);
@ -1993,7 +1982,7 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned long arg)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(&pdev->dev);
struct dfl_fpga_irq_set hdr;
s32 *fds;
long ret;
@ -2013,9 +2002,9 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
if (IS_ERR(fds))
return PTR_ERR(fds);
mutex_lock(&pdata->lock);
mutex_lock(&fdata->lock);
ret = dfl_fpga_set_irq_triggers(feature, hdr.start, hdr.count, fds);
mutex_unlock(&pdata->lock);
mutex_unlock(&fdata->lock);
kfree(fds);
return ret;

View file

@ -17,6 +17,7 @@
#include <linux/bitfield.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/dfl.h>
#include <linux/eventfd.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
@ -206,6 +207,8 @@
#define PORT_UINT_CAP_INT_NUM GENMASK_ULL(11, 0) /* Interrupts num */
#define PORT_UINT_CAP_FST_VECT GENMASK_ULL(23, 12) /* First Vector */
struct dfl_feature_dev_data;
/**
* struct dfl_fpga_port_ops - port ops
*
@ -219,15 +222,15 @@ struct dfl_fpga_port_ops {
const char *name;
struct module *owner;
struct list_head node;
int (*get_id)(struct platform_device *pdev);
int (*enable_set)(struct platform_device *pdev, bool enable);
int (*get_id)(struct dfl_feature_dev_data *fdata);
int (*enable_set)(struct dfl_feature_dev_data *fdata, bool enable);
};
void dfl_fpga_port_ops_add(struct dfl_fpga_port_ops *ops);
void dfl_fpga_port_ops_del(struct dfl_fpga_port_ops *ops);
struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev);
struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct dfl_feature_dev_data *fdata);
void dfl_fpga_port_ops_put(struct dfl_fpga_port_ops *ops);
int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id);
int dfl_fpga_check_port_id(struct dfl_feature_dev_data *fdata, void *pport_id);
/**
* struct dfl_feature_id - dfl private feature id
@ -300,26 +303,32 @@ struct dfl_feature {
#define FEATURE_DEV_ID_UNUSED (-1)
/**
* struct dfl_feature_platform_data - platform data for feature devices
* struct dfl_feature_dev_data - dfl enumeration data for dfl feature dev.
*
* @node: node to link feature devs to container device's port_dev_list.
* @lock: mutex to protect platform data.
* @cdev: cdev of feature dev.
* @dev: ptr to platform device linked with this platform data.
* @node: node to link the data structure to container device's port_dev_list.
* @lock: mutex to protect feature dev data.
* @dev: ptr to the feature's platform device linked with this structure.
* @type: type of DFL FIU for the feature dev. See enum dfl_id_type.
* @pdev_id: platform device id for the feature dev.
* @pdev_name: platform device name for the feature dev.
* @dfl_cdev: ptr to container device.
* @id: id used for this feature device.
* @id: id used for the feature device.
* @disable_count: count for port disable.
* @excl_open: set on feature device exclusive open.
* @open_count: count for feature device open.
* @num: number for sub features.
* @private: ptr to feature dev private data.
* @features: sub features of this feature dev.
* @features: sub features for the feature dev.
* @resource_num: number of resources for the feature dev.
* @resources: resources for the feature dev.
*/
struct dfl_feature_platform_data {
struct dfl_feature_dev_data {
struct list_head node;
struct mutex lock;
struct cdev cdev;
struct platform_device *dev;
enum dfl_id_type type;
int pdev_id;
const char *pdev_name;
struct dfl_fpga_cdev *dfl_cdev;
int id;
unsigned int disable_count;
@ -327,55 +336,68 @@ struct dfl_feature_platform_data {
int open_count;
void *private;
int num;
struct dfl_feature features[];
struct dfl_feature *features;
int resource_num;
struct resource *resources;
};
/**
* struct dfl_feature_platform_data - platform data for feature devices
*
* @cdev: cdev of feature dev.
* @fdata: dfl enumeration data for the dfl feature device.
*/
struct dfl_feature_platform_data {
struct cdev cdev;
struct dfl_feature_dev_data *fdata;
};
static inline
int dfl_feature_dev_use_begin(struct dfl_feature_platform_data *pdata,
int dfl_feature_dev_use_begin(struct dfl_feature_dev_data *fdata,
bool excl)
{
if (pdata->excl_open)
if (fdata->excl_open)
return -EBUSY;
if (excl) {
if (pdata->open_count)
if (fdata->open_count)
return -EBUSY;
pdata->excl_open = true;
fdata->excl_open = true;
}
pdata->open_count++;
fdata->open_count++;
return 0;
}
static inline
void dfl_feature_dev_use_end(struct dfl_feature_platform_data *pdata)
void dfl_feature_dev_use_end(struct dfl_feature_dev_data *fdata)
{
pdata->excl_open = false;
fdata->excl_open = false;
if (WARN_ON(pdata->open_count <= 0))
if (WARN_ON(fdata->open_count <= 0))
return;
pdata->open_count--;
fdata->open_count--;
}
static inline
int dfl_feature_dev_use_count(struct dfl_feature_platform_data *pdata)
int dfl_feature_dev_use_count(struct dfl_feature_dev_data *fdata)
{
return pdata->open_count;
return fdata->open_count;
}
static inline
void dfl_fpga_pdata_set_private(struct dfl_feature_platform_data *pdata,
void dfl_fpga_fdata_set_private(struct dfl_feature_dev_data *fdata,
void *private)
{
pdata->private = private;
fdata->private = private;
}
static inline
void *dfl_fpga_pdata_get_private(struct dfl_feature_platform_data *pdata)
void *dfl_fpga_fdata_get_private(struct dfl_feature_dev_data *fdata)
{
return pdata->private;
return fdata->private;
}
struct dfl_feature_ops {
@ -398,37 +420,36 @@ int dfl_fpga_dev_ops_register(struct platform_device *pdev,
struct module *owner);
void dfl_fpga_dev_ops_unregister(struct platform_device *pdev);
static inline
struct platform_device *dfl_fpga_inode_to_feature_dev(struct inode *inode)
static inline struct dfl_feature_dev_data *
dfl_fpga_inode_to_feature_dev_data(struct inode *inode)
{
struct dfl_feature_platform_data *pdata;
pdata = container_of(inode->i_cdev, struct dfl_feature_platform_data,
cdev);
return pdata->dev;
return pdata->fdata;
}
#define dfl_fpga_dev_for_each_feature(pdata, feature) \
for ((feature) = (pdata)->features; \
(feature) < (pdata)->features + (pdata)->num; (feature)++)
#define dfl_fpga_dev_for_each_feature(fdata, feature) \
for ((feature) = (fdata)->features; \
(feature) < (fdata)->features + (fdata)->num; (feature)++)
static inline
struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u16 id)
static inline struct dfl_feature *
dfl_get_feature_by_id(struct dfl_feature_dev_data *fdata, u16 id)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature *feature;
dfl_fpga_dev_for_each_feature(pdata, feature)
dfl_fpga_dev_for_each_feature(fdata, feature)
if (feature->id == id)
return feature;
return NULL;
}
static inline
void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id)
static inline void __iomem *
dfl_get_feature_ioaddr_by_id(struct dfl_feature_dev_data *fdata, u16 id)
{
struct dfl_feature *feature = dfl_get_feature_by_id(dev, id);
struct dfl_feature *feature = dfl_get_feature_by_id(fdata, id);
if (feature && feature->ioaddr)
return feature->ioaddr;
@ -437,10 +458,18 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id)
return NULL;
}
static inline
struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata)
static inline struct dfl_feature_dev_data *
to_dfl_feature_dev_data(struct device *dev)
{
return pdata->dev->dev.parent->parent;
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
return pdata->fdata;
}
static inline
struct device *dfl_fpga_fdata_to_parent(struct dfl_feature_dev_data *fdata)
{
return fdata->dev->dev.parent->parent;
}
static inline bool dfl_feature_is_fme(void __iomem *base)
@ -522,26 +551,21 @@ struct dfl_fpga_cdev *
dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info);
void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev);
/*
* need to drop the device reference with put_device() after use port platform
* device returned by __dfl_fpga_cdev_find_port and dfl_fpga_cdev_find_port
* functions.
*/
struct platform_device *
__dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data,
int (*match)(struct platform_device *, void *));
struct dfl_feature_dev_data *
__dfl_fpga_cdev_find_port_data(struct dfl_fpga_cdev *cdev, void *data,
int (*match)(struct dfl_feature_dev_data *, void *));
static inline struct platform_device *
dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data,
int (*match)(struct platform_device *, void *))
static inline struct dfl_feature_dev_data *
dfl_fpga_cdev_find_port_data(struct dfl_fpga_cdev *cdev, void *data,
int (*match)(struct dfl_feature_dev_data *, void *))
{
struct platform_device *pdev;
struct dfl_feature_dev_data *fdata;
mutex_lock(&cdev->lock);
pdev = __dfl_fpga_cdev_find_port(cdev, data, match);
fdata = __dfl_fpga_cdev_find_port_data(cdev, data, match);
mutex_unlock(&cdev->lock);
return pdev;
return fdata;
}
int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id);

View file

@ -75,22 +75,54 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
}
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
static struct coresight_device *coresight_get_source(struct list_head *path)
{
struct coresight_device *csdev;
if (!path)
return NULL;
csdev = list_first_entry(path, struct coresight_node, link)->csdev;
if (!coresight_is_device_source(csdev))
return NULL;
return csdev;
}
/**
* coresight_blocks_source - checks whether the connection matches the source
* of path if connection is bound to specific source.
* @src: The source device of the trace path
* @conn: The connection of one outport
*
* Return false if the connection doesn't have a source binded or source of the
* path matches the source binds to connection.
*/
static bool coresight_blocks_source(struct coresight_device *src,
struct coresight_connection *conn)
{
return conn->filter_src_fwnode && (conn->filter_src_dev != src);
}
static struct coresight_connection *
coresight_find_out_connection(struct coresight_device *src_dev,
struct coresight_device *dest_dev)
coresight_find_out_connection(struct coresight_device *csdev,
struct coresight_device *out_dev,
struct coresight_device *trace_src)
{
int i;
struct coresight_connection *conn;
for (i = 0; i < src_dev->pdata->nr_outconns; i++) {
conn = src_dev->pdata->out_conns[i];
if (conn->dest_dev == dest_dev)
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
conn = csdev->pdata->out_conns[i];
if (coresight_blocks_source(trace_src, conn))
continue;
if (conn->dest_dev == out_dev)
return conn;
}
dev_err(&src_dev->dev,
"couldn't find output connection, src_dev: %s, dest_dev: %s\n",
dev_name(&src_dev->dev), dev_name(&dest_dev->dev));
dev_err(&csdev->dev,
"couldn't find output connection, csdev: %s, out_dev: %s\n",
dev_name(&csdev->dev), dev_name(&out_dev->dev));
return ERR_PTR(-ENODEV);
}
@ -251,7 +283,8 @@ static void coresight_disable_sink(struct coresight_device *csdev)
static int coresight_enable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
struct coresight_device *child,
struct coresight_device *source)
{
int link_subtype;
struct coresight_connection *inconn, *outconn;
@ -259,8 +292,8 @@ static int coresight_enable_link(struct coresight_device *csdev,
if (!parent || !child)
return -EINVAL;
inconn = coresight_find_out_connection(parent, csdev);
outconn = coresight_find_out_connection(csdev, child);
inconn = coresight_find_out_connection(parent, csdev, source);
outconn = coresight_find_out_connection(csdev, child, source);
link_subtype = csdev->subtype.link_subtype;
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && IS_ERR(inconn))
@ -273,15 +306,16 @@ static int coresight_enable_link(struct coresight_device *csdev,
static void coresight_disable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
struct coresight_device *child,
struct coresight_device *source)
{
struct coresight_connection *inconn, *outconn;
if (!parent || !child)
return;
inconn = coresight_find_out_connection(parent, csdev);
outconn = coresight_find_out_connection(csdev, child);
inconn = coresight_find_out_connection(parent, csdev, source);
outconn = coresight_find_out_connection(csdev, child, source);
link_ops(csdev)->disable(csdev, inconn, outconn);
}
@ -375,7 +409,8 @@ static void coresight_disable_path_from(struct list_head *path,
case CORESIGHT_DEV_TYPE_LINK:
parent = list_prev_entry(nd, link)->csdev;
child = list_next_entry(nd, link)->csdev;
coresight_disable_link(csdev, parent, child);
coresight_disable_link(csdev, parent, child,
coresight_get_source(path));
break;
default:
break;
@ -418,7 +453,9 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode,
u32 type;
struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
struct coresight_device *source;
source = coresight_get_source(path);
list_for_each_entry_reverse(nd, path, link) {
csdev = nd->csdev;
type = csdev->type;
@ -456,7 +493,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode,
case CORESIGHT_DEV_TYPE_LINK:
parent = list_prev_entry(nd, link)->csdev;
child = list_next_entry(nd, link)->csdev;
ret = coresight_enable_link(csdev, parent, child);
ret = coresight_enable_link(csdev, parent, child, source);
if (ret)
goto err;
break;
@ -619,6 +656,7 @@ static void coresight_drop_device(struct coresight_device *csdev)
/**
* _coresight_build_path - recursively build a path from a @csdev to a sink.
* @csdev: The device to start from.
* @source: The trace source device of the path.
* @sink: The final sink we want in this path.
* @path: The list to add devices to.
*
@ -628,6 +666,7 @@ static void coresight_drop_device(struct coresight_device *csdev)
* the source is the first device and the sink the last one.
*/
static int _coresight_build_path(struct coresight_device *csdev,
struct coresight_device *source,
struct coresight_device *sink,
struct list_head *path)
{
@ -641,7 +680,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) &&
sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) {
if (_coresight_build_path(sink, sink, path) == 0) {
if (_coresight_build_path(sink, source, sink, path) == 0) {
found = true;
goto out;
}
@ -652,8 +691,12 @@ static int _coresight_build_path(struct coresight_device *csdev,
struct coresight_device *child_dev;
child_dev = csdev->pdata->out_conns[i]->dest_dev;
if (coresight_blocks_source(source, csdev->pdata->out_conns[i]))
continue;
if (child_dev &&
_coresight_build_path(child_dev, sink, path) == 0) {
_coresight_build_path(child_dev, source, sink, path) == 0) {
found = true;
break;
}
@ -698,7 +741,7 @@ struct list_head *coresight_build_path(struct coresight_device *source,
INIT_LIST_HEAD(path);
rc = _coresight_build_path(source, sink, path);
rc = _coresight_build_path(source, source, sink, path);
if (rc) {
kfree(path);
return ERR_PTR(rc);
@ -927,6 +970,16 @@ static int coresight_orphan_match(struct device *dev, void *data)
for (i = 0; i < src_csdev->pdata->nr_outconns; i++) {
conn = src_csdev->pdata->out_conns[i];
/* Fix filter source device before skip the port */
if (conn->filter_src_fwnode && !conn->filter_src_dev) {
if (dst_csdev &&
(conn->filter_src_fwnode == dst_csdev->dev.fwnode) &&
!WARN_ON_ONCE(!coresight_is_device_source(dst_csdev)))
conn->filter_src_dev = dst_csdev;
else
still_orphan = true;
}
/* Skip the port if it's already connected. */
if (conn->dest_dev)
continue;
@ -977,18 +1030,40 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
csdev, coresight_orphan_match);
}
static int coresight_clear_filter_source(struct device *dev, void *data)
{
int i;
struct coresight_device *source = data;
struct coresight_device *csdev = to_coresight_device(dev);
for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
if (csdev->pdata->out_conns[i]->filter_src_dev == source)
csdev->pdata->out_conns[i]->filter_src_dev = NULL;
}
return 0;
}
/* coresight_remove_conns - Remove other device's references to this device */
static void coresight_remove_conns(struct coresight_device *csdev)
{
int i, j;
struct coresight_connection *conn;
if (coresight_is_device_source(csdev))
bus_for_each_dev(&coresight_bustype, NULL, csdev,
coresight_clear_filter_source);
/*
* Remove the input connection references from the destination device
* for each output connection.
*/
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
conn = csdev->pdata->out_conns[i];
if (conn->filter_src_fwnode) {
conn->filter_src_dev = NULL;
fwnode_handle_put(conn->filter_src_fwnode);
}
if (!conn->dest_dev)
continue;

View file

@ -11,10 +11,12 @@
#include <linux/pm_runtime.h>
#include "coresight-priv.h"
#include "coresight-trace-id.h"
struct dummy_drvdata {
struct device *dev;
struct coresight_device *csdev;
u8 traceid;
};
DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source");
@ -72,6 +74,32 @@ static const struct coresight_ops dummy_sink_cs_ops = {
.sink_ops = &dummy_sink_ops,
};
/* User can get the trace id of dummy source from this node. */
static ssize_t traceid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long val;
struct dummy_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = drvdata->traceid;
return sysfs_emit(buf, "%#lx\n", val);
}
static DEVICE_ATTR_RO(traceid);
static struct attribute *coresight_dummy_attrs[] = {
&dev_attr_traceid.attr,
NULL,
};
static const struct attribute_group coresight_dummy_group = {
.attrs = coresight_dummy_attrs,
};
static const struct attribute_group *coresight_dummy_groups[] = {
&coresight_dummy_group,
NULL,
};
static int dummy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -79,6 +107,11 @@ static int dummy_probe(struct platform_device *pdev)
struct coresight_platform_data *pdata;
struct dummy_drvdata *drvdata;
struct coresight_desc desc = { 0 };
int ret = 0, trace_id = 0;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
if (of_device_is_compatible(node, "arm,coresight-dummy-source")) {
@ -90,6 +123,26 @@ static int dummy_probe(struct platform_device *pdev)
desc.subtype.source_subtype =
CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS;
desc.ops = &dummy_source_cs_ops;
desc.groups = coresight_dummy_groups;
ret = coresight_get_static_trace_id(dev, &trace_id);
if (!ret) {
/* Get the static id if id is set in device tree. */
ret = coresight_trace_id_get_static_system_id(trace_id);
if (ret < 0) {
dev_err(dev, "Fail to get static id.\n");
return ret;
}
} else {
/* Get next available id if id is not set in device tree. */
trace_id = coresight_trace_id_get_system_id();
if (trace_id < 0) {
ret = trace_id;
return ret;
}
}
drvdata->traceid = (u8)trace_id;
} else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) {
desc.name = coresight_alloc_device_name(&sink_devs, dev);
if (!desc.name)
@ -104,27 +157,35 @@ static int dummy_probe(struct platform_device *pdev)
}
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
if (IS_ERR(pdata)) {
ret = PTR_ERR(pdata);
goto free_id;
}
pdev->dev.platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
desc.pdata = pdev->dev.platform_data;
desc.dev = &pdev->dev;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto free_id;
}
pm_runtime_enable(dev);
dev_dbg(dev, "Dummy device initialized\n");
return 0;
ret = 0;
goto out;
free_id:
if (IS_VALID_CS_TRACE_ID(drvdata->traceid))
coresight_trace_id_put_system_id(drvdata->traceid);
out:
return ret;
}
static void dummy_remove(struct platform_device *pdev)
@ -132,6 +193,8 @@ static void dummy_remove(struct platform_device *pdev)
struct dummy_drvdata *drvdata = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
if (IS_VALID_CS_TRACE_ID(drvdata->traceid))
coresight_trace_id_put_system_id(drvdata->traceid);
pm_runtime_disable(dev);
coresight_unregister(drvdata->csdev);
}

View file

@ -86,14 +86,14 @@ static int funnel_enable(struct coresight_device *csdev,
bool first_enable = false;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_read(&in->dest_refcnt) == 0) {
if (in->dest_refcnt == 0) {
if (drvdata->base)
rc = dynamic_funnel_enable_hw(drvdata, in->dest_port);
if (!rc)
first_enable = true;
}
if (!rc)
atomic_inc(&in->dest_refcnt);
in->dest_refcnt++;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (first_enable)
@ -130,7 +130,7 @@ static void funnel_disable(struct coresight_device *csdev,
bool last_disable = false;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_dec_return(&in->dest_refcnt) == 0) {
if (--in->dest_refcnt == 0) {
if (drvdata->base)
dynamic_funnel_disable_hw(drvdata, in->dest_port);
last_disable = true;

View file

@ -243,6 +243,27 @@ static int of_coresight_parse_endpoint(struct device *dev,
conn.dest_fwnode = fwnode_handle_get(rdev_fwnode);
conn.dest_port = rendpoint.port;
/*
* Get the firmware node of the filter source through the
* reference. This could be used to filter the source in
* building path.
*/
conn.filter_src_fwnode =
fwnode_find_reference(&ep->fwnode, "filter-source", 0);
if (IS_ERR(conn.filter_src_fwnode)) {
conn.filter_src_fwnode = NULL;
} else {
conn.filter_src_dev =
coresight_find_csdev_by_fwnode(conn.filter_src_fwnode);
if (conn.filter_src_dev &&
!coresight_is_device_source(conn.filter_src_dev)) {
dev_warn(dev, "port %d: Filter handle is not a trace source : %s\n",
conn.src_port, dev_name(&conn.filter_src_dev->dev));
conn.filter_src_dev = NULL;
conn.filter_src_fwnode = NULL;
}
}
new_conn = coresight_add_out_conn(dev, pdata, &conn);
if (IS_ERR_VALUE(new_conn)) {
fwnode_handle_put(conn.dest_fwnode);
@ -796,6 +817,12 @@ int coresight_get_cpu(struct device *dev)
}
EXPORT_SYMBOL_GPL(coresight_get_cpu);
int coresight_get_static_trace_id(struct device *dev, u32 *id)
{
return fwnode_property_read_u32(dev_fwnode(dev), "arm,static-trace-id", id);
}
EXPORT_SYMBOL_GPL(coresight_get_static_trace_id);
struct coresight_platform_data *
coresight_get_platform_data(struct device *dev)
{

View file

@ -126,7 +126,7 @@ static int replicator_enable(struct coresight_device *csdev,
bool first_enable = false;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_read(&out->src_refcnt) == 0) {
if (out->src_refcnt == 0) {
if (drvdata->base)
rc = dynamic_replicator_enable(drvdata, in->dest_port,
out->src_port);
@ -134,7 +134,7 @@ static int replicator_enable(struct coresight_device *csdev,
first_enable = true;
}
if (!rc)
atomic_inc(&out->src_refcnt);
out->src_refcnt++;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (first_enable)
@ -180,7 +180,7 @@ static void replicator_disable(struct coresight_device *csdev,
bool last_disable = false;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_dec_return(&out->src_refcnt) == 0) {
if (--out->src_refcnt == 0) {
if (drvdata->base)
dynamic_replicator_disable(drvdata, in->dest_port,
out->src_port);

View file

@ -24,7 +24,7 @@ DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
static bool coresight_device_is_tpdm(struct coresight_device *csdev)
{
return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) &&
return (coresight_is_device_source(csdev)) &&
(csdev->subtype.source_subtype ==
CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM);
}
@ -110,6 +110,16 @@ static int tpda_get_element_size(struct tpda_drvdata *drvdata,
csdev->pdata->in_conns[i]->dest_port != inport)
continue;
/*
* If this port has a hardcoded filter, use the source
* device directly.
*/
if (csdev->pdata->in_conns[i]->filter_src_fwnode) {
in = csdev->pdata->in_conns[i]->filter_src_dev;
if (!in)
continue;
}
if (coresight_device_is_tpdm(in)) {
if (drvdata->dsb_esize || drvdata->cmb_esize)
return -EEXIST;
@ -124,7 +134,6 @@ static int tpda_get_element_size(struct tpda_drvdata *drvdata,
}
}
return rc;
}
@ -190,10 +199,10 @@ static int tpda_enable(struct coresight_device *csdev,
int ret = 0;
spin_lock(&drvdata->spinlock);
if (atomic_read(&in->dest_refcnt) == 0) {
if (in->dest_refcnt == 0) {
ret = __tpda_enable(drvdata, in->dest_port);
if (!ret) {
atomic_inc(&in->dest_refcnt);
in->dest_refcnt++;
csdev->refcnt++;
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
}
@ -223,7 +232,7 @@ static void tpda_disable(struct coresight_device *csdev,
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (atomic_dec_return(&in->dest_refcnt) == 0) {
if (--in->dest_refcnt == 0) {
__tpda_disable(drvdata, in->dest_port);
csdev->refcnt--;
}

View file

@ -640,8 +640,7 @@ static ssize_t dsb_mode_store(struct device *dev,
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val < 0) ||
(val & ~TPDM_DSB_MODE_MASK))
if ((kstrtoul(buf, 0, &val)) || (val & ~TPDM_DSB_MODE_MASK))
return -EINVAL;
spin_lock(&drvdata->spinlock);
@ -1308,8 +1307,8 @@ static void tpdm_remove(struct amba_device *adev)
*/
static struct amba_id tpdm_ids[] = {
{
.id = 0x000f0e00,
.mask = 0x000fff00,
.id = 0x001f0e00,
.mask = 0x00ffff00,
},
{ 0, 0, NULL },
};

View file

@ -12,6 +12,12 @@
#include "coresight-trace-id.h"
enum trace_id_flags {
TRACE_ID_ANY = 0x0,
TRACE_ID_PREFER_ODD = 0x1,
TRACE_ID_REQ_STATIC = 0x2,
};
/* Default trace ID map. Used in sysfs mode and for system sources */
static DEFINE_PER_CPU(atomic_t, id_map_default_cpu_ids) = ATOMIC_INIT(0);
static struct coresight_trace_id_map id_map_default = {
@ -74,21 +80,25 @@ static int coresight_trace_id_find_odd_id(struct coresight_trace_id_map *id_map)
* Otherwise allocate next available ID.
*/
static int coresight_trace_id_alloc_new_id(struct coresight_trace_id_map *id_map,
int preferred_id, bool prefer_odd_id)
int preferred_id, unsigned int flags)
{
int id = 0;
/* for backwards compatibility, cpu IDs may use preferred value */
if (IS_VALID_CS_TRACE_ID(preferred_id) &&
!test_bit(preferred_id, id_map->used_ids)) {
id = preferred_id;
goto trace_id_allocated;
} else if (prefer_odd_id) {
if (IS_VALID_CS_TRACE_ID(preferred_id)) {
if (!test_bit(preferred_id, id_map->used_ids)) {
id = preferred_id;
goto trace_id_allocated;
} else if (flags & TRACE_ID_REQ_STATIC)
return -EBUSY;
} else if (flags & TRACE_ID_PREFER_ODD) {
/* may use odd ids to avoid preferred legacy cpu IDs */
id = coresight_trace_id_find_odd_id(id_map);
if (id)
goto trace_id_allocated;
}
} else if (!IS_VALID_CS_TRACE_ID(preferred_id) &&
(flags & TRACE_ID_REQ_STATIC))
return -EINVAL;
/*
* skip reserved bit 0, look at bitmap length of
@ -153,7 +163,7 @@ static int _coresight_trace_id_get_cpu_id(int cpu, struct coresight_trace_id_map
*/
id = coresight_trace_id_alloc_new_id(id_map,
CORESIGHT_LEGACY_CPU_TRACE_ID(cpu),
false);
TRACE_ID_ANY);
if (!IS_VALID_CS_TRACE_ID(id))
goto get_cpu_id_out_unlock;
@ -188,14 +198,14 @@ static void _coresight_trace_id_put_cpu_id(int cpu, struct coresight_trace_id_ma
DUMP_ID_MAP(id_map);
}
static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map)
static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map,
int preferred_id, unsigned int traceid_flags)
{
unsigned long flags;
int id;
spin_lock_irqsave(&id_map->lock, flags);
/* prefer odd IDs for system components to avoid legacy CPU IDS */
id = coresight_trace_id_alloc_new_id(id_map, 0, true);
id = coresight_trace_id_alloc_new_id(id_map, preferred_id, traceid_flags);
spin_unlock_irqrestore(&id_map->lock, flags);
DUMP_ID(id);
@ -255,10 +265,19 @@ EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id_map);
int coresight_trace_id_get_system_id(void)
{
return coresight_trace_id_map_get_system_id(&id_map_default);
/* prefer odd IDs for system components to avoid legacy CPU IDS */
return coresight_trace_id_map_get_system_id(&id_map_default, 0,
TRACE_ID_PREFER_ODD);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_get_system_id);
int coresight_trace_id_get_static_system_id(int trace_id)
{
return coresight_trace_id_map_get_system_id(&id_map_default,
trace_id, TRACE_ID_REQ_STATIC);
}
EXPORT_SYMBOL_GPL(coresight_trace_id_get_static_system_id);
void coresight_trace_id_put_system_id(int id)
{
coresight_trace_id_map_put_system_id(&id_map_default, id);

View file

@ -116,6 +116,15 @@ int coresight_trace_id_read_cpu_id_map(int cpu, struct coresight_trace_id_map *i
*/
int coresight_trace_id_get_system_id(void);
/**
* Allocate a CoreSight static trace ID for a system component.
*
* Used to allocate static IDs for system trace sources such as dummy source.
*
* return: Trace ID or -EINVAL if allocation is impossible.
*/
int coresight_trace_id_get_static_system_id(int id);
/**
* Release an allocated system trace ID.
*

View file

@ -857,8 +857,9 @@ static irqreturn_t intel_th_irq(int irq, void *data)
/**
* intel_th_alloc() - allocate a new Intel TH device and its subdevices
* @dev: parent device
* @drvdata: data private to the driver
* @devres: resources indexed by th_mmio_idx
* @irq: irq number
* @ndevres: number of entries in the @devres resources
*/
struct intel_th *
intel_th_alloc(struct device *dev, const struct intel_th_drvdata *drvdata,

View file

@ -9,37 +9,93 @@
#define _ADXL345_H_
#define ADXL345_REG_DEVID 0x00
#define ADXL345_REG_THRESH_TAP 0x1D
#define ADXL345_REG_OFSX 0x1E
#define ADXL345_REG_OFSY 0x1F
#define ADXL345_REG_OFSZ 0x20
#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
/* Tap duration */
#define ADXL345_REG_DUR 0x21
/* Tap latency */
#define ADXL345_REG_LATENT 0x22
/* Tap window */
#define ADXL345_REG_WINDOW 0x23
/* Activity threshold */
#define ADXL345_REG_THRESH_ACT 0x24
/* Inactivity threshold */
#define ADXL345_REG_THRESH_INACT 0x25
/* Inactivity time */
#define ADXL345_REG_TIME_INACT 0x26
/* Axis enable control for activity and inactivity detection */
#define ADXL345_REG_ACT_INACT_CTRL 0x27
/* Free-fall threshold */
#define ADXL345_REG_THRESH_FF 0x28
/* Free-fall time */
#define ADXL345_REG_TIME_FF 0x29
/* Axis control for single tap or double tap */
#define ADXL345_REG_TAP_AXIS 0x2A
/* Source of single tap or double tap */
#define ADXL345_REG_ACT_TAP_STATUS 0x2B
/* Data rate and power mode control */
#define ADXL345_REG_BW_RATE 0x2C
#define ADXL345_REG_POWER_CTL 0x2D
#define ADXL345_REG_INT_ENABLE 0x2E
#define ADXL345_REG_INT_MAP 0x2F
#define ADXL345_REG_INT_SOURCE 0x30
#define ADXL345_REG_INT_SOURCE_MSK 0xFF
#define ADXL345_REG_DATA_FORMAT 0x31
#define ADXL345_REG_DATAX0 0x32
#define ADXL345_REG_DATAY0 0x34
#define ADXL345_REG_DATAZ0 0x36
#define ADXL345_REG_DATA_AXIS(index) \
(ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
#define ADXL345_REG_XYZ_BASE 0x32
#define ADXL345_REG_DATA_AXIS(index) \
(ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16))
#define ADXL345_REG_FIFO_CTL 0x38
#define ADXL345_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0)
/* 0: INT1, 1: INT2 */
#define ADXL345_FIFO_CTL_TRIGGER_MSK BIT(5)
#define ADXL345_FIFO_CTL_MODE_MSK GENMASK(7, 6)
#define ADXL345_REG_FIFO_STATUS 0x39
#define ADXL345_REG_FIFO_STATUS_MSK 0x3F
#define ADXL345_INT_OVERRUN BIT(0)
#define ADXL345_INT_WATERMARK BIT(1)
#define ADXL345_INT_FREE_FALL BIT(2)
#define ADXL345_INT_INACTIVITY BIT(3)
#define ADXL345_INT_ACTIVITY BIT(4)
#define ADXL345_INT_DOUBLE_TAP BIT(5)
#define ADXL345_INT_SINGLE_TAP BIT(6)
#define ADXL345_INT_DATA_READY BIT(7)
/*
* BW_RATE bits - Bandwidth and output data rate. The default value is
* 0x0A, which translates to a 100 Hz output data rate
*/
#define ADXL345_BW_RATE GENMASK(3, 0)
#define ADXL345_BW_LOW_POWER BIT(4)
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
#define ADXL345_POWER_CTL_MEASURE BIT(3)
#define ADXL345_POWER_CTL_STANDBY 0x00
#define ADXL345_POWER_CTL_WAKEUP GENMASK(1, 0)
#define ADXL345_POWER_CTL_SLEEP BIT(2)
#define ADXL345_POWER_CTL_MEASURE BIT(3)
#define ADXL345_POWER_CTL_AUTO_SLEEP BIT(4)
#define ADXL345_POWER_CTL_LINK BIT(5)
#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */
#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */
#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */
#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */
/* Set the g range */
#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0)
/* Data is left justified */
#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2)
/* Up to 13-bits resolution */
#define ADXL345_DATA_FORMAT_FULL_RES BIT(3)
#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6)
#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7)
#define ADXL345_DATA_FORMAT_2G 0
#define ADXL345_DATA_FORMAT_4G 1
#define ADXL345_DATA_FORMAT_8G 2
#define ADXL345_DATA_FORMAT_16G 3
#define ADXL345_DEVID 0xE5
#define ADXL345_FIFO_SIZE 32
/*
* In full-resolution mode, scale factor is maintained at ~4 mg/LSB
@ -62,6 +118,7 @@ struct adxl345_chip_info {
};
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
bool fifo_delay_default,
int (*setup)(struct device*, struct regmap*));
#endif /* _ADXL345_H_ */

View file

@ -7,6 +7,8 @@
* Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
*/
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
@ -14,36 +16,92 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include "adxl345.h"
struct adxl345_data {
#define ADXL345_FIFO_BYPASS 0
#define ADXL345_FIFO_FIFO 1
#define ADXL345_FIFO_STREAM 2
#define ADXL345_DIRS 3
#define ADXL345_INT_NONE 0xff
#define ADXL345_INT1 0
#define ADXL345_INT2 1
struct adxl345_state {
const struct adxl345_chip_info *info;
struct regmap *regmap;
bool fifo_delay; /* delay: delay is needed for SPI */
int irq;
u8 intio;
u8 int_map;
u8 watermark;
u8 fifo_mode;
__le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN);
};
#define ADXL345_CHANNEL(index, axis) { \
#define ADXL345_CHANNEL(index, reg, axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.address = index, \
.address = (reg), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (index), \
.scan_type = { \
.sign = 's', \
.realbits = 13, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const struct iio_chan_spec adxl345_channels[] = {
ADXL345_CHANNEL(0, X),
ADXL345_CHANNEL(1, Y),
ADXL345_CHANNEL(2, Z),
enum adxl345_chans {
chan_x, chan_y, chan_z,
};
static const struct iio_chan_spec adxl345_channels[] = {
ADXL345_CHANNEL(0, chan_x, X),
ADXL345_CHANNEL(1, chan_y, Y),
ADXL345_CHANNEL(2, chan_z, Z),
};
static const unsigned long adxl345_scan_masks[] = {
BIT(chan_x) | BIT(chan_y) | BIT(chan_z),
0
};
static int adxl345_set_interrupts(struct adxl345_state *st)
{
int ret;
unsigned int int_enable = st->int_map;
unsigned int int_map;
/*
* Any bits set to 0 in the INT map register send their respective
* interrupts to the INT1 pin, whereas bits set to 1 send their respective
* interrupts to the INT2 pin. The intio shall convert this accordingly.
*/
int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK,
st->intio ? st->int_map : ~st->int_map);
ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map);
if (ret)
return ret;
return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, int_enable);
}
static int adxl345_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
struct adxl345_state *st = iio_priv(indio_dev);
__le16 accel;
long long samp_freq_nhz;
unsigned int regval;
@ -56,7 +114,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
* ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
* and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
*/
ret = regmap_bulk_read(data->regmap,
ret = regmap_bulk_read(st->regmap,
ADXL345_REG_DATA_AXIS(chan->address),
&accel, sizeof(accel));
if (ret < 0)
@ -66,10 +124,10 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = data->info->uscale;
*val2 = st->info->uscale;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
ret = regmap_read(data->regmap,
ret = regmap_read(st->regmap,
ADXL345_REG_OFS_AXIS(chan->address), &regval);
if (ret < 0)
return ret;
@ -81,7 +139,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, &regval);
ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
if (ret < 0)
return ret;
@ -99,7 +157,7 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
struct adxl345_state *st = iio_priv(indio_dev);
s64 n;
switch (mask) {
@ -108,14 +166,14 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
* 8-bit resolution at +/- 2g, that is 4x accel data scale
* factor
*/
return regmap_write(data->regmap,
return regmap_write(st->regmap,
ADXL345_REG_OFS_AXIS(chan->address),
val / 4);
case IIO_CHAN_INFO_SAMP_FREQ:
n = div_s64(val * NANOHZ_PER_HZ + val2,
ADXL345_BASE_RATE_NANO_HZ);
return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE,
return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE,
ADXL345_BW_RATE,
clamp_val(ilog2(n), 0,
ADXL345_BW_RATE));
@ -124,6 +182,24 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value)
{
struct adxl345_state *st = iio_priv(indio_dev);
unsigned int fifo_mask = 0x1F;
int ret;
value = min(value, ADXL345_FIFO_SIZE - 1);
ret = regmap_update_bits(st->regmap, ADXL345_REG_FIFO_CTL, fifo_mask, value);
if (ret)
return ret;
st->watermark = value;
st->int_map |= ADXL345_INT_WATERMARK;
return 0;
}
static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
@ -138,6 +214,33 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
/**
* adxl345_set_measure_en() - Enable and disable measuring.
*
* @st: The device data.
* @en: Enable measurements, else standby mode.
*
* For lowest power operation, standby mode can be used. In standby mode,
* current consumption is supposed to be reduced to 0.1uA (typical). In this
* mode no measurements are made. Placing the device into standby mode
* preserves the contents of FIFO.
*
* Return: Returns 0 if successful, or a negative error value.
*/
static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
{
unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY;
return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val);
}
static void adxl345_powerdown(void *ptr)
{
struct adxl345_state *st = ptr;
adxl345_set_measure_en(st, false);
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200"
);
@ -151,37 +254,244 @@ static const struct attribute_group adxl345_attrs_group = {
.attrs = adxl345_attrs,
};
static int adxl345_set_fifo(struct adxl345_state *st)
{
int ret;
/* FIFO should only be configured while in standby mode */
ret = adxl345_set_measure_en(st, false);
if (ret < 0)
return ret;
ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL,
FIELD_PREP(ADXL345_FIFO_CTL_SAMPLES_MSK,
st->watermark) |
FIELD_PREP(ADXL345_FIFO_CTL_TRIGGER_MSK,
st->intio) |
FIELD_PREP(ADXL345_FIFO_CTL_MODE_MSK,
st->fifo_mode));
if (ret < 0)
return ret;
return adxl345_set_measure_en(st, true);
}
/**
* adxl345_get_samples() - Read number of FIFO entries.
* @st: The initialized state instance of this driver.
*
* The sensor does not support treating any axis individually, or exclude them
* from measuring.
*
* Return: negative error, or value.
*/
static int adxl345_get_samples(struct adxl345_state *st)
{
unsigned int regval = 0;
int ret;
ret = regmap_read(st->regmap, ADXL345_REG_FIFO_STATUS, &regval);
if (ret < 0)
return ret;
return FIELD_GET(ADXL345_REG_FIFO_STATUS_MSK, regval);
}
/**
* adxl345_fifo_transfer() - Read samples number of elements.
* @st: The instance of the state object of this sensor.
* @samples: The number of lines in the FIFO referred to as fifo_entry.
*
* It is recommended that a multiple-byte read of all registers be performed to
* prevent a change in data between reads of sequential registers. That is to
* read out the data registers X0, X1, Y0, Y1, Z0, Z1, i.e. 6 bytes at once.
*
* Return: 0 or error value.
*/
static int adxl345_fifo_transfer(struct adxl345_state *st, int samples)
{
size_t count;
int i, ret = 0;
/* count is the 3x the fifo_buf element size, hence 6B */
count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS;
for (i = 0; i < samples; i++) {
/* read 3x 2 byte elements from base address into next fifo_buf position */
ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE,
st->fifo_buf + (i * count / 2), count);
if (ret < 0)
return ret;
/*
* To ensure that the FIFO has completely popped, there must be at least 5
* us between the end of reading the data registers, signified by the
* transition to register 0x38 from 0x37 or the CS pin going high, and the
* start of new reads of the FIFO or reading the FIFO_STATUS register. For
* SPI operation at 1.5 MHz or lower, the register addressing portion of the
* transmission is sufficient delay to ensure the FIFO has completely
* popped. It is necessary for SPI operation greater than 1.5 MHz to
* de-assert the CS pin to ensure a total of 5 us, which is at most 3.4 us
* at 5 MHz operation.
*/
if (st->fifo_delay && samples > 1)
udelay(3);
}
return ret;
}
/**
* adxl345_fifo_reset() - Empty the FIFO in error condition.
* @st: The instance to the state object of the sensor.
*
* Read all elements of the FIFO. Reading the interrupt source register
* resets the sensor.
*/
static void adxl345_fifo_reset(struct adxl345_state *st)
{
int regval;
int samples;
adxl345_set_measure_en(st, false);
samples = adxl345_get_samples(st);
if (samples > 0)
adxl345_fifo_transfer(st, samples);
regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &regval);
adxl345_set_measure_en(st, true);
}
static int adxl345_buffer_postenable(struct iio_dev *indio_dev)
{
struct adxl345_state *st = iio_priv(indio_dev);
int ret;
ret = adxl345_set_interrupts(st);
if (ret < 0)
return ret;
st->fifo_mode = ADXL345_FIFO_STREAM;
return adxl345_set_fifo(st);
}
static int adxl345_buffer_predisable(struct iio_dev *indio_dev)
{
struct adxl345_state *st = iio_priv(indio_dev);
int ret;
st->fifo_mode = ADXL345_FIFO_BYPASS;
ret = adxl345_set_fifo(st);
if (ret < 0)
return ret;
st->int_map = 0x00;
return adxl345_set_interrupts(st);
}
static const struct iio_buffer_setup_ops adxl345_buffer_ops = {
.postenable = adxl345_buffer_postenable,
.predisable = adxl345_buffer_predisable,
};
static int adxl345_get_status(struct adxl345_state *st)
{
int ret;
unsigned int regval;
ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &regval);
if (ret < 0)
return ret;
return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval);
}
static int adxl345_fifo_push(struct iio_dev *indio_dev,
int samples)
{
struct adxl345_state *st = iio_priv(indio_dev);
int i, ret;
if (samples <= 0)
return -EINVAL;
ret = adxl345_fifo_transfer(st, samples);
if (ret)
return ret;
for (i = 0; i < ADXL345_DIRS * samples; i += ADXL345_DIRS)
iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
return 0;
}
/**
* adxl345_irq_handler() - Handle irqs of the ADXL345.
* @irq: The irq being handled.
* @p: The struct iio_device pointer for the device.
*
* Return: The interrupt was handled.
*/
static irqreturn_t adxl345_irq_handler(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct adxl345_state *st = iio_priv(indio_dev);
int int_stat;
int samples;
int_stat = adxl345_get_status(st);
if (int_stat <= 0)
return IRQ_NONE;
if (int_stat & ADXL345_INT_OVERRUN)
goto err;
if (int_stat & ADXL345_INT_WATERMARK) {
samples = adxl345_get_samples(st);
if (samples < 0)
goto err;
if (adxl345_fifo_push(indio_dev, samples) < 0)
goto err;
}
return IRQ_HANDLED;
err:
adxl345_fifo_reset(st);
return IRQ_HANDLED;
}
static const struct iio_info adxl345_info = {
.attrs = &adxl345_attrs_group,
.read_raw = adxl345_read_raw,
.write_raw = adxl345_write_raw,
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
.hwfifo_set_watermark = adxl345_set_watermark,
};
static int adxl345_powerup(void *regmap)
{
return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE);
}
static void adxl345_powerdown(void *regmap)
{
regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY);
}
/**
* adxl345_core_probe() - probe and setup for the adxl345 accelerometer,
* also covers the adlx375 accelerometer
* adxl345_core_probe() - Probe and setup for the accelerometer.
* @dev: Driver model representation of the device
* @regmap: Regmap instance for the device
* @fifo_delay_default: Using FIFO with SPI needs delay
* @setup: Setup routine to be executed right before the standard device
* setup
*
* For SPI operation greater than 1.6 MHz, it is necessary to deassert the CS
* pin to ensure a total delay of 5 us; otherwise, the delay is not sufficient.
* The total delay necessary for 5 MHz operation is at most 3.4 us. This is not
* a concern when using I2C mode because the communication rate is low enough
* to ensure a sufficient delay between FIFO reads.
* Ref: "Retrieving Data from FIFO", p. 21 of 36, Data Sheet ADXL345 Rev. G
*
* Return: 0 on success, negative errno on error
*/
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
bool fifo_delay_default,
int (*setup)(struct device*, struct regmap*))
{
struct adxl345_data *data;
struct adxl345_state *st;
struct iio_dev *indio_dev;
u32 regval;
unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE |
@ -190,30 +500,32 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
ADXL345_DATA_FORMAT_SELF_TEST);
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
data->info = device_get_match_data(dev);
if (!data->info)
st = iio_priv(indio_dev);
st->regmap = regmap;
st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
st->fifo_delay = fifo_delay_default;
indio_dev->name = data->info->name;
indio_dev->name = st->info->name;
indio_dev->info = &adxl345_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl345_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
indio_dev->available_scan_masks = adxl345_scan_masks;
if (setup) {
/* Perform optional initial bus specific configuration */
ret = setup(dev, data->regmap);
ret = setup(dev, st->regmap);
if (ret)
return ret;
/* Enable full-resolution mode */
ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT,
ret = regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT,
data_format_mask,
ADXL345_DATA_FORMAT_FULL_RES);
if (ret)
@ -222,14 +534,14 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
} else {
/* Enable full-resolution mode (init all data_format bits) */
ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
ret = regmap_write(st->regmap, ADXL345_REG_DATA_FORMAT,
ADXL345_DATA_FORMAT_FULL_RES);
if (ret)
return dev_err_probe(dev, ret,
"Failed to set data range\n");
}
ret = regmap_read(data->regmap, ADXL345_REG_DEVID, &regval);
ret = regmap_read(st->regmap, ADXL345_REG_DEVID, &regval);
if (ret < 0)
return dev_err_probe(dev, ret, "Error reading device ID\n");
@ -238,14 +550,43 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
regval, ADXL345_DEVID);
/* Enable measurement mode */
ret = adxl345_powerup(data->regmap);
ret = adxl345_set_measure_en(st, true);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to enable measurement mode\n");
ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap);
ret = devm_add_action_or_reset(dev, adxl345_powerdown, st);
if (ret < 0)
return ret;
st->intio = ADXL345_INT1;
st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
if (st->irq < 0) {
st->intio = ADXL345_INT2;
st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (st->irq < 0)
st->intio = ADXL345_INT_NONE;
}
if (st->intio != ADXL345_INT_NONE) {
/* FIFO_STREAM mode is going to be activated later */
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops);
if (ret)
return ret;
ret = devm_request_threaded_irq(dev, st->irq, NULL,
&adxl345_irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
return ret;
} else {
ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL,
FIELD_PREP(ADXL345_FIFO_CTL_MODE_MSK,
ADXL345_FIFO_BYPASS));
if (ret < 0)
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, "IIO_ADXL345");

View file

@ -27,7 +27,7 @@ static int adxl345_i2c_probe(struct i2c_client *client)
if (IS_ERR(regmap))
return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n");
return adxl345_core_probe(&client->dev, regmap, NULL);
return adxl345_core_probe(&client->dev, regmap, false, NULL);
}
static const struct adxl345_chip_info adxl345_i2c_info = {

View file

@ -12,6 +12,7 @@
#include "adxl345.h"
#define ADXL345_MAX_SPI_FREQ_HZ 5000000
#define ADXL345_MAX_FREQ_NO_FIFO_DELAY 1500000
static const struct regmap_config adxl345_spi_regmap_config = {
.reg_bits = 8,
@ -28,6 +29,7 @@ static int adxl345_spi_setup(struct device *dev, struct regmap *regmap)
static int adxl345_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
bool needs_delay;
/* Bail out if max_speed_hz exceeds 5 MHz */
if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ)
@ -38,10 +40,11 @@ static int adxl345_spi_probe(struct spi_device *spi)
if (IS_ERR(regmap))
return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n");
needs_delay = spi->max_speed_hz > ADXL345_MAX_FREQ_NO_FIFO_DELAY;
if (spi->mode & SPI_3WIRE)
return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup);
return adxl345_core_probe(&spi->dev, regmap, needs_delay, adxl345_spi_setup);
else
return adxl345_core_probe(&spi->dev, regmap, NULL);
return adxl345_core_probe(&spi->dev, regmap, needs_delay, NULL);
}
static const struct adxl345_chip_info adxl345_spi_info = {

View file

@ -66,7 +66,7 @@ struct bma220_data {
struct {
s8 chans[3];
/* Ensure timestamp is naturally aligned. */
s64 timestamp __aligned(8);
aligned_s64 timestamp;
} scan;
u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
};

View file

@ -129,6 +129,8 @@
#define FXLS8962AF_DEVICE_ID 0x62
#define FXLS8964AF_DEVICE_ID 0x84
#define FXLS8974CF_DEVICE_ID 0x86
#define FXLS8967AF_DEVICE_ID 0x87
/* Raw temp channel offset */
#define FXLS8962AF_TEMP_CENTER_VAL 25
@ -766,6 +768,18 @@ static const struct fxls8962af_chip_info fxls_chip_info_table[] = {
.channels = fxls8962af_channels,
.num_channels = ARRAY_SIZE(fxls8962af_channels),
},
[fxls8967af] = {
.chip_id = FXLS8967AF_DEVICE_ID,
.name = "fxls8967af",
.channels = fxls8962af_channels,
.num_channels = ARRAY_SIZE(fxls8962af_channels),
},
[fxls8974cf] = {
.chip_id = FXLS8974CF_DEVICE_ID,
.name = "fxls8974cf",
.channels = fxls8962af_channels,
.num_channels = ARRAY_SIZE(fxls8962af_channels),
},
};
static const struct iio_info fxls8962af_info = {

View file

@ -30,6 +30,8 @@ static int fxls8962af_probe(struct i2c_client *client)
static const struct i2c_device_id fxls8962af_id[] = {
{ "fxls8962af", fxls8962af },
{ "fxls8964af", fxls8964af },
{ "fxls8967af", fxls8967af },
{ "fxls8974cf", fxls8974cf },
{}
};
MODULE_DEVICE_TABLE(i2c, fxls8962af_id);

View file

@ -11,6 +11,8 @@ struct device;
enum {
fxls8962af,
fxls8964af,
fxls8967af,
fxls8974cf,
};
int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq);

View file

@ -38,7 +38,9 @@ static int kx022a_i2c_probe(struct i2c_client *i2c)
static const struct i2c_device_id kx022a_i2c_id[] = {
{ .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info },
{ .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info },
{ .name = "kx134-1211", .driver_data = (kernel_ulong_t)&kx134_chip_info },
{ .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info },
{ .name = "kx134acr-lbz", .driver_data = (kernel_ulong_t)&kx134acr_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id);
@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id);
static const struct of_device_id kx022a_of_match[] = {
{ .compatible = "kionix,kx022a", .data = &kx022a_chip_info },
{ .compatible = "kionix,kx132-1211", .data = &kx132_chip_info },
{ .compatible = "kionix,kx134-1211", .data = &kx134_chip_info },
{ .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info },
{ .compatible = "rohm,kx134acr-lbz", .data = &kx134acr_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, kx022a_of_match);

View file

@ -38,7 +38,9 @@ static int kx022a_spi_probe(struct spi_device *spi)
static const struct spi_device_id kx022a_id[] = {
{ .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info },
{ .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info },
{ .name = "kx134-1211", .driver_data = (kernel_ulong_t)&kx134_chip_info },
{ .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info },
{ .name = "kx134acr-lbz", .driver_data = (kernel_ulong_t)&kx134acr_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, kx022a_id);
@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(spi, kx022a_id);
static const struct of_device_id kx022a_of_match[] = {
{ .compatible = "kionix,kx022a", .data = &kx022a_chip_info },
{ .compatible = "kionix,kx132-1211", .data = &kx132_chip_info },
{ .compatible = "kionix,kx134-1211", .data = &kx134_chip_info },
{ .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info },
{ .compatible = "rohm,kx134acr-lbz", .data = &kx134acr_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, kx022a_of_match);

View file

@ -5,6 +5,7 @@
* ROHM/KIONIX accelerometer driver
*/
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
@ -407,11 +408,21 @@ static const int kx022a_scale_table[][2] = {
{ 0, 4788403 },
};
/* KX134ACR-LBZ ranges are (+/-) 8, 16, 32, 64 G */
static const int kx134acr_lbz_scale_table[][2] = {
{ 0, 2394202 },
{ 0, 4788403 },
{ 0, 9576807 },
{ 0, 19153613 },
};
static int kx022a_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct kx022a_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (const int *)kx022a_accel_samp_freq_table;
@ -420,9 +431,8 @@ static int kx022a_read_avail(struct iio_dev *indio_dev,
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)kx022a_scale_table;
*length = ARRAY_SIZE(kx022a_scale_table) *
ARRAY_SIZE(kx022a_scale_table[0]);
*vals = (const int *)data->chip_info->scale_table;
*length = data->chip_info->scale_table_size;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_LIST;
default:
@ -438,17 +448,17 @@ static void kx022a_reg2freq(unsigned int val, int *val1, int *val2)
*val2 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR][1];
}
static void kx022a_reg2scale(unsigned int val, unsigned int *val1,
unsigned int *val2)
static void kx022a_reg2scale(struct kx022a_data *data, unsigned int val,
unsigned int *val1, unsigned int *val2)
{
val &= KX022A_MASK_GSEL;
val >>= KX022A_GSEL_SHIFT;
*val1 = kx022a_scale_table[val][0];
*val2 = kx022a_scale_table[val][1];
*val1 = data->chip_info->scale_table[val][0];
*val2 = data->chip_info->scale_table[val][1];
}
static int kx022a_turn_on_off_unlocked(struct kx022a_data *data, bool on)
static int __kx022a_turn_on_off(struct kx022a_data *data, bool on)
{
int ret;
@ -469,7 +479,7 @@ static int kx022a_turn_off_lock(struct kx022a_data *data)
int ret;
mutex_lock(&data->mutex);
ret = kx022a_turn_on_off_unlocked(data, false);
ret = __kx022a_turn_on_off(data, false);
if (ret)
mutex_unlock(&data->mutex);
@ -480,7 +490,7 @@ static int kx022a_turn_on_unlock(struct kx022a_data *data)
{
int ret;
ret = kx022a_turn_on_off_unlocked(data, true);
ret = __kx022a_turn_on_off(data, true);
mutex_unlock(&data->mutex);
return ret;
@ -543,11 +553,11 @@ static int kx022a_write_raw(struct iio_dev *idev,
kx022a_turn_on_unlock(data);
break;
case IIO_CHAN_INFO_SCALE:
n = ARRAY_SIZE(kx022a_scale_table);
n = data->chip_info->scale_table_size / 2;
while (n-- > 0)
if (val == kx022a_scale_table[n][0] &&
val2 == kx022a_scale_table[n][1])
if (val == data->chip_info->scale_table[n][0] &&
val2 == data->chip_info->scale_table[n][1])
break;
if (n < 0) {
ret = -EINVAL;
@ -642,7 +652,7 @@ static int kx022a_read_raw(struct iio_dev *idev,
if (ret < 0)
return ret;
kx022a_reg2scale(regval, val, val2);
kx022a_reg2scale(data, regval, val, val2);
return IIO_VAL_INT_PLUS_NANO;
}
@ -912,18 +922,19 @@ static int kx022a_fifo_disable(struct kx022a_data *data)
{
int ret = 0;
ret = kx022a_turn_off_lock(data);
guard(mutex)(&data->mutex);
ret = __kx022a_turn_on_off(data, false);
if (ret)
return ret;
ret = regmap_clear_bits(data->regmap, data->ien_reg, KX022A_MASK_WMI);
if (ret)
goto unlock_out;
return ret;
ret = regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2,
KX022A_MASK_BUF_EN);
if (ret)
goto unlock_out;
return ret;
data->state &= ~KX022A_STATE_FIFO;
@ -931,12 +942,7 @@ static int kx022a_fifo_disable(struct kx022a_data *data)
kfree(data->fifo_buffer);
return kx022a_turn_on_unlock(data);
unlock_out:
mutex_unlock(&data->mutex);
return ret;
return __kx022a_turn_on_off(data, true);
}
static int kx022a_buffer_predisable(struct iio_dev *idev)
@ -959,33 +965,29 @@ static int kx022a_fifo_enable(struct kx022a_data *data)
if (!data->fifo_buffer)
return -ENOMEM;
ret = kx022a_turn_off_lock(data);
guard(mutex)(&data->mutex);
ret = __kx022a_turn_on_off(data, false);
if (ret)
return ret;
/* Update watermark to HW */
ret = kx022a_fifo_set_wmi(data);
if (ret)
goto unlock_out;
return ret;
/* Enable buffer */
ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2,
KX022A_MASK_BUF_EN);
if (ret)
goto unlock_out;
return ret;
data->state |= KX022A_STATE_FIFO;
ret = regmap_set_bits(data->regmap, data->ien_reg,
KX022A_MASK_WMI);
if (ret)
goto unlock_out;
return ret;
return kx022a_turn_on_unlock(data);
unlock_out:
mutex_unlock(&data->mutex);
return ret;
return __kx022a_turn_on_off(data, true);
}
static int kx022a_buffer_postenable(struct iio_dev *idev)
@ -1053,7 +1055,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private)
struct kx022a_data *data = iio_priv(idev);
irqreturn_t ret = IRQ_NONE;
mutex_lock(&data->mutex);
guard(mutex)(&data->mutex);
if (data->trigger_enabled) {
iio_trigger_poll_nested(data->trig);
@ -1068,8 +1070,6 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private)
ret = IRQ_HANDLED;
}
mutex_unlock(&data->mutex);
return ret;
}
@ -1079,32 +1079,26 @@ static int kx022a_trigger_set_state(struct iio_trigger *trig,
struct kx022a_data *data = iio_trigger_get_drvdata(trig);
int ret = 0;
mutex_lock(&data->mutex);
guard(mutex)(&data->mutex);
if (data->trigger_enabled == state)
goto unlock_out;
return 0;
if (data->state & KX022A_STATE_FIFO) {
dev_warn(data->dev, "Can't set trigger when FIFO enabled\n");
ret = -EBUSY;
goto unlock_out;
return -EBUSY;
}
ret = kx022a_turn_on_off_unlocked(data, false);
ret = __kx022a_turn_on_off(data, false);
if (ret)
goto unlock_out;
return ret;
data->trigger_enabled = state;
ret = kx022a_set_drdy_irq(data, state);
if (ret)
goto unlock_out;
return ret;
ret = kx022a_turn_on_off_unlocked(data, true);
unlock_out:
mutex_unlock(&data->mutex);
return ret;
return __kx022a_turn_on_off(data, true);
}
static const struct iio_trigger_ops kx022a_trigger_ops = {
@ -1121,10 +1115,15 @@ static int kx022a_chip_init(struct kx022a_data *data)
return ret;
/*
* I've seen I2C read failures if we poll too fast after the sensor
* reset. Slight delay gives I2C block the time to recover.
* According to the power-on procedure documents, there is (at least)
* 2ms delay required after the software reset. This should be same for
* all, KX022ACR-Z, KX132-1211, KX132ACR-LBZ and KX134ACR-LBZ.
*
* https://fscdn.rohm.com/kionix/en/document/AN010_KX022ACR-Z_Power-on_Procedure_E.pdf
* https://fscdn.rohm.com/kionix/en/document/TN027-Power-On-Procedure.pdf
* https://fscdn.rohm.com/kionix/en/document/AN011_KX134ACR-LBZ_Power-on_Procedure_E.pdf
*/
msleep(1);
msleep(2);
ret = regmap_read_poll_timeout(data->regmap, data->chip_info->cntl2, val,
!(val & KX022A_MASK_SRST),
@ -1158,6 +1157,9 @@ const struct kx022a_chip_info kx022a_chip_info = {
.regmap_config = &kx022a_regmap_config,
.channels = kx022a_channels,
.num_channels = ARRAY_SIZE(kx022a_channels),
.scale_table = kx022a_scale_table,
.scale_table_size = ARRAY_SIZE(kx022a_scale_table) *
ARRAY_SIZE(kx022a_scale_table[0]),
.fifo_length = KX022A_FIFO_LENGTH,
.who = KX022A_REG_WHO,
.id = KX022A_ID,
@ -1183,6 +1185,9 @@ const struct kx022a_chip_info kx132_chip_info = {
.regmap_config = &kx132_regmap_config,
.channels = kx132_channels,
.num_channels = ARRAY_SIZE(kx132_channels),
.scale_table = kx022a_scale_table,
.scale_table_size = ARRAY_SIZE(kx022a_scale_table) *
ARRAY_SIZE(kx022a_scale_table[0]),
.fifo_length = KX132_FIFO_LENGTH,
.who = KX132_REG_WHO,
.id = KX132_ID,
@ -1204,6 +1209,35 @@ const struct kx022a_chip_info kx132_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(kx132_chip_info, "IIO_KX022A");
const struct kx022a_chip_info kx134_chip_info = {
.name = "kx134-1211",
.regmap_config = &kx132_regmap_config,
.channels = kx132_channels,
.num_channels = ARRAY_SIZE(kx132_channels),
.scale_table = kx134acr_lbz_scale_table,
.scale_table_size = ARRAY_SIZE(kx134acr_lbz_scale_table) *
ARRAY_SIZE(kx134acr_lbz_scale_table[0]),
.fifo_length = KX132_FIFO_LENGTH,
.who = KX132_REG_WHO,
.id = KX134_1211_ID,
.cntl = KX132_REG_CNTL,
.cntl2 = KX132_REG_CNTL2,
.odcntl = KX132_REG_ODCNTL,
.buf_cntl1 = KX132_REG_BUF_CNTL1,
.buf_cntl2 = KX132_REG_BUF_CNTL2,
.buf_clear = KX132_REG_BUF_CLEAR,
.buf_status1 = KX132_REG_BUF_STATUS_1,
.buf_smp_lvl_mask = KX132_MASK_BUF_SMP_LVL,
.buf_read = KX132_REG_BUF_READ,
.inc1 = KX132_REG_INC1,
.inc4 = KX132_REG_INC4,
.inc5 = KX132_REG_INC5,
.inc6 = KX132_REG_INC6,
.xout_l = KX132_REG_XOUT_L,
.get_fifo_bytes_available = kx132_get_fifo_bytes_available,
};
EXPORT_SYMBOL_NS_GPL(kx134_chip_info, "IIO_KX022A");
/*
* Despite the naming, KX132ACR-LBZ is not similar to KX132-1211 but it is
* exact subset of KX022A. KX132ACR-LBZ is meant to be used for industrial
@ -1216,6 +1250,9 @@ const struct kx022a_chip_info kx132acr_chip_info = {
.regmap_config = &kx022a_regmap_config,
.channels = kx022a_channels,
.num_channels = ARRAY_SIZE(kx022a_channels),
.scale_table = kx022a_scale_table,
.scale_table_size = ARRAY_SIZE(kx022a_scale_table) *
ARRAY_SIZE(kx022a_scale_table[0]),
.fifo_length = KX022A_FIFO_LENGTH,
.who = KX022A_REG_WHO,
.id = KX132ACR_LBZ_ID,
@ -1236,6 +1273,34 @@ const struct kx022a_chip_info kx132acr_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(kx132acr_chip_info, "IIO_KX022A");
const struct kx022a_chip_info kx134acr_chip_info = {
.name = "kx134acr-lbz",
.regmap_config = &kx022a_regmap_config,
.channels = kx022a_channels,
.num_channels = ARRAY_SIZE(kx022a_channels),
.scale_table = kx134acr_lbz_scale_table,
.scale_table_size = ARRAY_SIZE(kx134acr_lbz_scale_table) *
ARRAY_SIZE(kx134acr_lbz_scale_table[0]),
.fifo_length = KX022A_FIFO_LENGTH,
.who = KX022A_REG_WHO,
.id = KX134ACR_LBZ_ID,
.cntl = KX022A_REG_CNTL,
.cntl2 = KX022A_REG_CNTL2,
.odcntl = KX022A_REG_ODCNTL,
.buf_cntl1 = KX022A_REG_BUF_CNTL1,
.buf_cntl2 = KX022A_REG_BUF_CNTL2,
.buf_clear = KX022A_REG_BUF_CLEAR,
.buf_status1 = KX022A_REG_BUF_STATUS_1,
.buf_read = KX022A_REG_BUF_READ,
.inc1 = KX022A_REG_INC1,
.inc4 = KX022A_REG_INC4,
.inc5 = KX022A_REG_INC5,
.inc6 = KX022A_REG_INC6,
.xout_l = KX022A_REG_XOUT_L,
.get_fifo_bytes_available = kx022a_get_fifo_bytes_available,
};
EXPORT_SYMBOL_NS_GPL(kx134acr_chip_info, "IIO_KX022A");
int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info)
{
static const char * const regulator_names[] = {"io-vdd", "vdd"};

View file

@ -14,6 +14,7 @@
#define KX022A_REG_WHO 0x0f
#define KX022A_ID 0xc8
#define KX132ACR_LBZ_ID 0xd8
#define KX134ACR_LBZ_ID 0xcc
#define KX022A_REG_CNTL2 0x19
#define KX022A_MASK_SRST BIT(7)
@ -77,6 +78,7 @@
#define KX132_REG_WHO 0x13
#define KX132_ID 0x3d
#define KX134_1211_ID 0x46
#define KX132_FIFO_LENGTH 86
@ -135,6 +137,14 @@ struct kx022a_data;
*
* @name: name of the device
* @regmap_config: pointer to register map configuration
* @scale_table: An array of tables of scaling factors for
* a supported acceleration measurement range.
* Each table containing a single scaling
* factor consisting of two integers. The first
* value in a table is the integer part, and
* the second value is the fractional part as
* parts per billion.
* @scale_table_size: Amount of values in tables.
* @channels: pointer to iio_chan_spec array
* @num_channels: number of iio_chan_spec channels
* @fifo_length: number of 16-bit samples in a full buffer
@ -161,6 +171,8 @@ struct kx022a_data;
struct kx022a_chip_info {
const char *name;
const struct regmap_config *regmap_config;
const int (*scale_table)[2];
const int scale_table_size;
const struct iio_chan_spec *channels;
unsigned int num_channels;
unsigned int fifo_length;
@ -187,6 +199,8 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi
extern const struct kx022a_chip_info kx022a_chip_info;
extern const struct kx022a_chip_info kx132_chip_info;
extern const struct kx022a_chip_info kx134_chip_info;
extern const struct kx022a_chip_info kx132acr_chip_info;
extern const struct kx022a_chip_info kx134acr_chip_info;
#endif

View file

@ -35,10 +35,6 @@
#define AD4000_SCALE_OPTIONS 2
#define AD4000_TQUIET1_NS 190
#define AD4000_TQUIET2_NS 60
#define AD4000_TCONV_NS 320
#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \
{ \
.type = IIO_VOLTAGE, \
@ -49,6 +45,7 @@
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
.scan_index = 0, \
.scan_type = { \
.sign = _sign, \
.realbits = _real_bits, \
@ -62,6 +59,12 @@
__AD4000_DIFF_CHANNEL((_sign), (_real_bits), \
((_real_bits) > 16 ? 32 : 16), (_reg_access))
#define AD4000_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \
{ \
AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access), \
IIO_CHAN_SOFT_TIMESTAMP(1), \
}
#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\
{ \
.type = IIO_VOLTAGE, \
@ -71,6 +74,7 @@
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\
.scan_index = 0, \
.scan_type = { \
.sign = _sign, \
.realbits = _real_bits, \
@ -84,6 +88,12 @@
__AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \
((_real_bits) > 16 ? 32 : 16), (_reg_access))
#define AD4000_PSEUDO_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \
{ \
AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access), \
IIO_CHAN_SOFT_TIMESTAMP(1), \
}
static const char * const ad4000_power_supplies[] = {
"vdd", "vio"
};
@ -108,111 +118,280 @@ static const int ad4000_gains[] = {
454, 909, 1000, 1900,
};
struct ad4000_time_spec {
int t_conv_ns;
int t_quiet2_ns;
};
/*
* Same timing specifications for all of AD4000, AD4001, ..., AD4008, AD4010,
* ADAQ4001, and ADAQ4003.
*/
static const struct ad4000_time_spec ad4000_t_spec = {
.t_conv_ns = 320,
.t_quiet2_ns = 60,
};
/* AD4020, AD4021, AD4022 */
static const struct ad4000_time_spec ad4020_t_spec = {
.t_conv_ns = 350,
.t_quiet2_ns = 60,
};
/* AD7983, AD7984 */
static const struct ad4000_time_spec ad7983_t_spec = {
.t_conv_ns = 500,
.t_quiet2_ns = 0,
};
/* AD7980, AD7982 */
static const struct ad4000_time_spec ad7980_t_spec = {
.t_conv_ns = 800,
.t_quiet2_ns = 0,
};
/* AD7946, AD7686, AD7688, AD7988-5, AD7693 */
static const struct ad4000_time_spec ad7686_t_spec = {
.t_conv_ns = 1600,
.t_quiet2_ns = 0,
};
/* AD7690 */
static const struct ad4000_time_spec ad7690_t_spec = {
.t_conv_ns = 2100,
.t_quiet2_ns = 0,
};
/* AD7942, AD7685, AD7687 */
static const struct ad4000_time_spec ad7687_t_spec = {
.t_conv_ns = 3200,
.t_quiet2_ns = 0,
};
/* AD7691 */
static const struct ad4000_time_spec ad7691_t_spec = {
.t_conv_ns = 3700,
.t_quiet2_ns = 0,
};
/* AD7988-1 */
static const struct ad4000_time_spec ad7988_1_t_spec = {
.t_conv_ns = 9500,
.t_quiet2_ns = 0,
};
struct ad4000_chip_info {
const char *dev_name;
struct iio_chan_spec chan_spec;
struct iio_chan_spec reg_access_chan_spec;
struct iio_chan_spec chan_spec[2];
struct iio_chan_spec reg_access_chan_spec[2];
const struct ad4000_time_spec *time_spec;
bool has_hardware_gain;
};
static const struct ad4000_chip_info ad4000_chip_info = {
.dev_name = "ad4000",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4001_chip_info = {
.dev_name = "ad4001",
.chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4002_chip_info = {
.dev_name = "ad4002",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4003_chip_info = {
.dev_name = "ad4003",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4004_chip_info = {
.dev_name = "ad4004",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4005_chip_info = {
.dev_name = "ad4005",
.chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4006_chip_info = {
.dev_name = "ad4006",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4007_chip_info = {
.dev_name = "ad4007",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4008_chip_info = {
.dev_name = "ad4008",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1),
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4010_chip_info = {
.dev_name = "ad4010",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1),
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0),
.reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4011_chip_info = {
.dev_name = "ad4011",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
.time_spec = &ad4000_t_spec,
};
static const struct ad4000_chip_info ad4020_chip_info = {
.dev_name = "ad4020",
.chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1),
.time_spec = &ad4020_t_spec,
};
static const struct ad4000_chip_info ad4021_chip_info = {
.dev_name = "ad4021",
.chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1),
.time_spec = &ad4020_t_spec,
};
static const struct ad4000_chip_info ad4022_chip_info = {
.dev_name = "ad4022",
.chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1),
.time_spec = &ad4020_t_spec,
};
static const struct ad4000_chip_info adaq4001_chip_info = {
.dev_name = "adaq4001",
.chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1),
.time_spec = &ad4000_t_spec,
.has_hardware_gain = true,
};
static const struct ad4000_chip_info adaq4003_chip_info = {
.dev_name = "adaq4003",
.chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1),
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1),
.time_spec = &ad4000_t_spec,
.has_hardware_gain = true,
};
static const struct ad4000_chip_info ad7685_chip_info = {
.dev_name = "ad7685",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.time_spec = &ad7687_t_spec,
};
static const struct ad4000_chip_info ad7686_chip_info = {
.dev_name = "ad7686",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.time_spec = &ad7686_t_spec,
};
static const struct ad4000_chip_info ad7687_chip_info = {
.dev_name = "ad7687",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.time_spec = &ad7687_t_spec,
};
static const struct ad4000_chip_info ad7688_chip_info = {
.dev_name = "ad7688",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.time_spec = &ad7686_t_spec,
};
static const struct ad4000_chip_info ad7690_chip_info = {
.dev_name = "ad7690",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.time_spec = &ad7690_t_spec,
};
static const struct ad4000_chip_info ad7691_chip_info = {
.dev_name = "ad7691",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.time_spec = &ad7691_t_spec,
};
static const struct ad4000_chip_info ad7693_chip_info = {
.dev_name = "ad7693",
.chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0),
.time_spec = &ad7686_t_spec,
};
static const struct ad4000_chip_info ad7942_chip_info = {
.dev_name = "ad7942",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0),
.time_spec = &ad7687_t_spec,
};
static const struct ad4000_chip_info ad7946_chip_info = {
.dev_name = "ad7946",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0),
.time_spec = &ad7686_t_spec,
};
static const struct ad4000_chip_info ad7980_chip_info = {
.dev_name = "ad7980",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.time_spec = &ad7980_t_spec,
};
static const struct ad4000_chip_info ad7982_chip_info = {
.dev_name = "ad7982",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.time_spec = &ad7980_t_spec,
};
static const struct ad4000_chip_info ad7983_chip_info = {
.dev_name = "ad7983",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.time_spec = &ad7983_t_spec,
};
static const struct ad4000_chip_info ad7984_chip_info = {
.dev_name = "ad7984",
.chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0),
.time_spec = &ad7983_t_spec,
};
static const struct ad4000_chip_info ad7988_1_chip_info = {
.dev_name = "ad7988-1",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.time_spec = &ad7988_1_t_spec,
};
static const struct ad4000_chip_info ad7988_5_chip_info = {
.dev_name = "ad7988-5",
.chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0),
.time_spec = &ad7686_t_spec,
};
struct ad4000_state {
struct spi_device *spi;
struct gpio_desc *cnv_gpio;
@ -224,6 +403,7 @@ struct ad4000_state {
bool span_comp;
u16 gain_milli;
int scale_tbl[AD4000_SCALE_OPTIONS][2];
const struct ad4000_time_spec *time_spec;
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
@ -234,7 +414,7 @@ struct ad4000_state {
__be16 sample_buf16;
__be32 sample_buf32;
} data;
s64 timestamp __aligned(8);
aligned_s64 timestamp;
} scan __aligned(IIO_DMA_MINALIGN);
u8 tx_buf[2];
u8 rx_buf[2];
@ -488,16 +668,15 @@ static const struct iio_info ad4000_info = {
static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
const struct iio_chan_spec *chan)
{
unsigned int cnv_pulse_time = AD4000_TCONV_NS;
struct spi_transfer *xfers = st->xfers;
xfers[0].cs_change = 1;
xfers[0].cs_change_delay.value = cnv_pulse_time;
xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns;
xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = &st->scan.data;
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
xfers[1].delay.value = AD4000_TQUIET2_NS;
xfers[1].delay.value = st->time_spec->t_quiet2_ns;
xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
spi_message_init_with_transfers(&st->msg, st->xfers, 2);
@ -515,7 +694,6 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st,
static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st,
const struct iio_chan_spec *chan)
{
unsigned int cnv_to_sdi_time = AD4000_TCONV_NS;
struct spi_transfer *xfers = st->xfers;
/*
@ -523,7 +701,7 @@ static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st,
* going low.
*/
xfers[0].cs_off = 1;
xfers[0].delay.value = cnv_to_sdi_time;
xfers[0].delay.value = st->time_spec->t_conv_ns;
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = &st->scan.data;
@ -562,6 +740,7 @@ static int ad4000_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
st->spi = spi;
st->time_spec = chip->time_spec;
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies),
ad4000_power_supplies);
@ -591,7 +770,7 @@ static int ad4000_probe(struct spi_device *spi)
switch (st->sdi_pin) {
case AD4000_SDI_MOSI:
indio_dev->info = &ad4000_reg_access_info;
indio_dev->channels = &chip->reg_access_chan_spec;
indio_dev->channels = chip->reg_access_chan_spec;
/*
* In "3-wire mode", the ADC SDI line must be kept high when
@ -603,7 +782,7 @@ static int ad4000_probe(struct spi_device *spi)
if (ret < 0)
return ret;
ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels);
ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
if (ret)
return ret;
@ -614,16 +793,16 @@ static int ad4000_probe(struct spi_device *spi)
break;
case AD4000_SDI_VIO:
indio_dev->info = &ad4000_info;
indio_dev->channels = &chip->chan_spec;
ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels);
indio_dev->channels = chip->chan_spec;
ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
if (ret)
return ret;
break;
case AD4000_SDI_CS:
indio_dev->info = &ad4000_info;
indio_dev->channels = &chip->chan_spec;
ret = ad4000_prepare_4wire_mode_message(st, indio_dev->channels);
indio_dev->channels = chip->chan_spec;
ret = ad4000_prepare_4wire_mode_message(st, &indio_dev->channels[0]);
if (ret)
return ret;
@ -637,7 +816,7 @@ static int ad4000_probe(struct spi_device *spi)
}
indio_dev->name = chip->dev_name;
indio_dev->num_channels = 1;
indio_dev->num_channels = 2;
ret = devm_mutex_init(dev, &st->lock);
if (ret)
@ -658,7 +837,7 @@ static int ad4000_probe(struct spi_device *spi)
}
}
ad4000_fill_scale_tbl(st, indio_dev->channels);
ad4000_fill_scale_tbl(st, &indio_dev->channels[0]);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
@ -686,6 +865,21 @@ static const struct spi_device_id ad4000_id[] = {
{ "ad4022", (kernel_ulong_t)&ad4022_chip_info },
{ "adaq4001", (kernel_ulong_t)&adaq4001_chip_info },
{ "adaq4003", (kernel_ulong_t)&adaq4003_chip_info },
{ "ad7685", (kernel_ulong_t)&ad7685_chip_info },
{ "ad7686", (kernel_ulong_t)&ad7686_chip_info },
{ "ad7687", (kernel_ulong_t)&ad7687_chip_info },
{ "ad7688", (kernel_ulong_t)&ad7688_chip_info },
{ "ad7690", (kernel_ulong_t)&ad7690_chip_info },
{ "ad7691", (kernel_ulong_t)&ad7691_chip_info },
{ "ad7693", (kernel_ulong_t)&ad7693_chip_info },
{ "ad7942", (kernel_ulong_t)&ad7942_chip_info },
{ "ad7946", (kernel_ulong_t)&ad7946_chip_info },
{ "ad7980", (kernel_ulong_t)&ad7980_chip_info },
{ "ad7982", (kernel_ulong_t)&ad7982_chip_info },
{ "ad7983", (kernel_ulong_t)&ad7983_chip_info },
{ "ad7984", (kernel_ulong_t)&ad7984_chip_info },
{ "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info },
{ "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4000_id);
@ -707,6 +901,21 @@ static const struct of_device_id ad4000_of_match[] = {
{ .compatible = "adi,ad4022", .data = &ad4022_chip_info },
{ .compatible = "adi,adaq4001", .data = &adaq4001_chip_info },
{ .compatible = "adi,adaq4003", .data = &adaq4003_chip_info },
{ .compatible = "adi,ad7685", .data = &ad7685_chip_info },
{ .compatible = "adi,ad7686", .data = &ad7686_chip_info },
{ .compatible = "adi,ad7687", .data = &ad7687_chip_info },
{ .compatible = "adi,ad7688", .data = &ad7688_chip_info },
{ .compatible = "adi,ad7690", .data = &ad7690_chip_info },
{ .compatible = "adi,ad7691", .data = &ad7691_chip_info },
{ .compatible = "adi,ad7693", .data = &ad7693_chip_info },
{ .compatible = "adi,ad7942", .data = &ad7942_chip_info },
{ .compatible = "adi,ad7946", .data = &ad7946_chip_info },
{ .compatible = "adi,ad7980", .data = &ad7980_chip_info },
{ .compatible = "adi,ad7982", .data = &ad7982_chip_info },
{ .compatible = "adi,ad7983", .data = &ad7983_chip_info },
{ .compatible = "adi,ad7984", .data = &ad7984_chip_info },
{ .compatible = "adi,ad7988-1", .data = &ad7988_1_chip_info },
{ .compatible = "adi,ad7988-5", .data = &ad7988_5_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad4000_of_match);

View file

@ -30,7 +30,7 @@
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <dt-bindings/iio/adi,ad4695.h>
#include <dt-bindings/iio/adc/adi,ad4695.h>
/* AD4695 registers */
#define AD4695_REG_SPI_CONFIG_A 0x0000

View file

@ -95,6 +95,10 @@
#define AD7124_MAX_CONFIGS 8
#define AD7124_MAX_CHANNELS 16
/* AD7124 input sources */
#define AD7124_INPUT_TEMPSENSOR 16
#define AD7124_INPUT_AVSS 17
enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
@ -360,20 +364,21 @@ static int ad7124_find_free_config_slot(struct ad7124_state *st)
return free_cfg_slot;
}
/* Only called during probe, so dev_err_probe() can be used */
static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg)
{
struct device *dev = &st->sd.spi->dev;
unsigned int refsel = cfg->refsel;
switch (refsel) {
case AD7124_REFIN1:
case AD7124_REFIN2:
case AD7124_AVDD_REF:
if (IS_ERR(st->vref[refsel])) {
dev_err(&st->sd.spi->dev,
"Error, trying to use external voltage reference without a %s regulator.\n",
ad7124_ref_names[refsel]);
return PTR_ERR(st->vref[refsel]);
}
if (IS_ERR(st->vref[refsel]))
return dev_err_probe(dev, PTR_ERR(st->vref[refsel]),
"Error, trying to use external voltage reference without a %s regulator.\n",
ad7124_ref_names[refsel]);
cfg->vref_mv = regulator_get_voltage(st->vref[refsel]);
/* Conversion from uV to mV */
cfg->vref_mv /= 1000;
@ -384,8 +389,7 @@ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channe
st->adc_control |= AD7124_ADC_CTRL_REF_EN(1);
return 0;
default:
dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
return -EINVAL;
return dev_err_probe(dev, -EINVAL, "Invalid reference %d\n", refsel);
}
}
@ -571,6 +575,7 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
.data_reg = AD7124_DATA,
.num_slots = 8,
.irq_flags = IRQF_TRIGGER_FALLING,
.num_resetclks = 64,
};
static int ad7124_read_raw(struct iio_dev *indio_dev,
@ -588,26 +593,59 @@ static int ad7124_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
mutex_lock(&st->cfgs_lock);
switch (chan->type) {
case IIO_VOLTAGE:
mutex_lock(&st->cfgs_lock);
idx = st->channels[chan->address].cfg.pga_bits;
*val = st->channels[chan->address].cfg.vref_mv;
if (st->channels[chan->address].cfg.bipolar)
*val2 = chan->scan_type.realbits - 1 + idx;
else
*val2 = chan->scan_type.realbits + idx;
idx = st->channels[chan->address].cfg.pga_bits;
*val = st->channels[chan->address].cfg.vref_mv;
if (st->channels[chan->address].cfg.bipolar)
*val2 = chan->scan_type.realbits - 1 + idx;
else
*val2 = chan->scan_type.realbits + idx;
mutex_unlock(&st->cfgs_lock);
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_TEMP:
/*
* According to the data sheet
* Temperature (°C)
* = ((Conversion 0x800000)/13584) 272.5
* = (Conversion 0x800000 - 13584 * 272.5) / 13584
* = (Conversion 12090248) / 13584
* So scale with 1000/13584 to yield °mC. Reduce by 8 to
* 125/1698.
*/
*val = 125;
*val2 = 1698;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
mutex_unlock(&st->cfgs_lock);
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
mutex_lock(&st->cfgs_lock);
if (st->channels[chan->address].cfg.bipolar)
*val = -(1 << (chan->scan_type.realbits - 1));
else
*val = 0;
switch (chan->type) {
case IIO_VOLTAGE:
mutex_lock(&st->cfgs_lock);
if (st->channels[chan->address].cfg.bipolar)
*val = -(1 << (chan->scan_type.realbits - 1));
else
*val = 0;
mutex_unlock(&st->cfgs_lock);
return IIO_VAL_INT;
case IIO_TEMP:
/* see calculation above */
*val = -12090248;
return IIO_VAL_INT;
default:
return -EINVAL;
}
mutex_unlock(&st->cfgs_lock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&st->cfgs_lock);
*val = st->channels[chan->address].cfg.odr;
@ -751,12 +789,14 @@ static const struct iio_info ad7124_info = {
.attrs = &ad7124_attrs_group,
};
/* Only called during probe, so dev_err_probe() can be used */
static int ad7124_soft_reset(struct ad7124_state *st)
{
struct device *dev = &st->sd.spi->dev;
unsigned int readval, timeout;
int ret;
ret = ad_sd_reset(&st->sd, 64);
ret = ad_sd_reset(&st->sd);
if (ret < 0)
return ret;
@ -765,7 +805,7 @@ static int ad7124_soft_reset(struct ad7124_state *st)
do {
ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval);
if (ret < 0)
return ret;
return dev_err_probe(dev, ret, "Error reading status register\n");
if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
return 0;
@ -774,39 +814,47 @@ static int ad7124_soft_reset(struct ad7124_state *st)
usleep_range(100, 2000);
} while (--timeout);
dev_err(&st->sd.spi->dev, "Soft reset failed\n");
return -EIO;
return dev_err_probe(dev, -EIO, "Soft reset failed\n");
}
static int ad7124_check_chip_id(struct ad7124_state *st)
{
struct device *dev = &st->sd.spi->dev;
unsigned int readval, chip_id, silicon_rev;
int ret;
ret = ad_sd_read_reg(&st->sd, AD7124_ID, 1, &readval);
if (ret < 0)
return ret;
return dev_err_probe(dev, ret, "Failure to read ID register\n");
chip_id = AD7124_DEVICE_ID_GET(readval);
silicon_rev = AD7124_SILICON_REV_GET(readval);
if (chip_id != st->chip_info->chip_id) {
dev_err(&st->sd.spi->dev,
"Chip ID mismatch: expected %u, got %u\n",
st->chip_info->chip_id, chip_id);
return -ENODEV;
}
if (chip_id != st->chip_info->chip_id)
return dev_err_probe(dev, -ENODEV,
"Chip ID mismatch: expected %u, got %u\n",
st->chip_info->chip_id, chip_id);
if (silicon_rev == 0) {
dev_err(&st->sd.spi->dev,
"Silicon revision empty. Chip may not be present\n");
return -ENODEV;
}
if (silicon_rev == 0)
return dev_err_probe(dev, -ENODEV,
"Silicon revision empty. Chip may not be present\n");
return 0;
}
/*
* Input specifiers 8 - 15 are explicitly reserved for ad7124-4
* while they are fine for ad7124-8. Values above 31 don't fit
* into the register field and so are invalid for sure.
*/
static bool ad7124_valid_input_select(unsigned int ain, const struct ad7124_chip_info *info)
{
if (ain >= info->num_inputs && ain < 16)
return false;
return ain <= FIELD_MAX(AD7124_CHANNEL_AINM_MSK);
}
static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
struct device *dev)
{
@ -815,11 +863,23 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
struct ad7124_channel *channels;
struct iio_chan_spec *chan;
unsigned int ain[2], channel = 0, tmp;
unsigned int num_channels;
int ret;
st->num_channels = device_get_child_node_count(dev);
if (!st->num_channels)
return dev_err_probe(dev, -ENODEV, "no channel children\n");
num_channels = device_get_child_node_count(dev);
/*
* The driver assigns each logical channel defined in the device tree
* statically one channel register. So only accept 16 such logical
* channels to not treat CONFIG_0 (i.e. the register following
* CHANNEL_15) as an additional channel register. The driver could be
* improved to lift this limitation.
*/
if (num_channels > AD7124_MAX_CHANNELS)
return dev_err_probe(dev, -EINVAL, "Too many channels defined\n");
/* Add one for temperature */
st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS);
chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
sizeof(*chan), GFP_KERNEL);
@ -838,16 +898,23 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &channel);
if (ret)
return ret;
return dev_err_probe(dev, ret,
"Failed to parse reg property of %pfwP\n", child);
if (channel >= indio_dev->num_channels)
if (channel >= num_channels)
return dev_err_probe(dev, -EINVAL,
"Channel index >= number of channels\n");
"Channel index >= number of channels in %pfwP\n", child);
ret = fwnode_property_read_u32_array(child, "diff-channels",
ain, 2);
if (ret)
return ret;
return dev_err_probe(dev, ret,
"Failed to parse diff-channels property of %pfwP\n", child);
if (!ad7124_valid_input_select(ain[0], st->chip_info) ||
!ad7124_valid_input_select(ain[1], st->chip_info))
return dev_err_probe(dev, -EINVAL,
"diff-channels property of %pfwP contains invalid data\n", child);
st->channels[channel].nr = channel;
st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
@ -874,17 +941,49 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
chan[channel].channel2 = ain[1];
}
if (num_channels < AD7124_MAX_CHANNELS) {
st->channels[num_channels] = (struct ad7124_channel) {
.nr = num_channels,
.ain = AD7124_CHANNEL_AINP(AD7124_INPUT_TEMPSENSOR) |
AD7124_CHANNEL_AINM(AD7124_INPUT_AVSS),
.cfg = {
.bipolar = true,
},
};
chan[num_channels] = (struct iio_chan_spec) {
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_type = {
/*
* You might find it strange that a bipolar
* measurement yields an unsigned value, but
* this matches the device's manual.
*/
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
.address = num_channels,
.scan_index = num_channels,
};
}
return 0;
}
static int ad7124_setup(struct ad7124_state *st)
{
struct device *dev = &st->sd.spi->dev;
unsigned int fclk, power_mode;
int i, ret;
fclk = clk_get_rate(st->mclk);
if (!fclk)
return -EINVAL;
return dev_err_probe(dev, -EINVAL, "Failed to get mclk rate\n");
/* The power mode changes the master clock frequency */
power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz,
@ -893,7 +992,7 @@ static int ad7124_setup(struct ad7124_state *st)
if (fclk != ad7124_master_clk_freq_hz[power_mode]) {
ret = clk_set_rate(st->mclk, fclk);
if (ret)
return ret;
return dev_err_probe(dev, ret, "Failed to set mclk rate\n");
}
/* Set the power mode */
@ -924,7 +1023,7 @@ static int ad7124_setup(struct ad7124_state *st)
ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
if (ret < 0)
return ret;
return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n");
return ret;
}
@ -937,13 +1036,14 @@ static void ad7124_reg_disable(void *r)
static int ad7124_probe(struct spi_device *spi)
{
const struct ad7124_chip_info *info;
struct device *dev = &spi->dev;
struct ad7124_state *st;
struct iio_dev *indio_dev;
int i, ret;
info = spi_get_device_match_data(spi);
if (!info)
return -ENODEV;
return dev_err_probe(dev, -ENODEV, "Failed to get match data\n");
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
@ -978,17 +1078,17 @@ static int ad7124_probe(struct spi_device *spi)
ret = regulator_enable(st->vref[i]);
if (ret)
return ret;
return dev_err_probe(dev, ret, "Failed to enable regulator #%d\n", i);
ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable,
st->vref[i]);
if (ret)
return ret;
return dev_err_probe(dev, ret, "Failed to register disable handler for regulator #%d\n", i);
}
st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
return dev_err_probe(dev, PTR_ERR(st->mclk), "Failed to get mclk\n");
ret = ad7124_soft_reset(st);
if (ret < 0)
@ -1004,10 +1104,13 @@ static int ad7124_probe(struct spi_device *spi)
ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
if (ret < 0)
return ret;
return dev_err_probe(dev, ret, "Failed to setup triggers\n");
return devm_iio_device_register(&spi->dev, indio_dev);
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register iio device\n");
return 0;
}
static const struct of_device_id ad7124_of_match[] = {

View file

@ -150,6 +150,11 @@
#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
#define AD7173_MAX_CONFIGS 8
#define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */
#define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */
#define AD7173_MODE_CAL_SYS_ZERO 0x6 /* System Zero-Scale Calibration */
#define AD7173_MODE_CAL_SYS_FULL 0x7 /* System Full-Scale Calibration */
struct ad7173_device_info {
const unsigned int *sinc5_data_rates;
unsigned int num_sinc5_data_rates;
@ -175,6 +180,7 @@ struct ad7173_device_info {
bool has_input_buf;
bool has_int_ref;
bool has_ref2;
bool has_internal_fs_calibration;
bool higher_gpio_bits;
u8 num_gpios;
};
@ -193,9 +199,9 @@ struct ad7173_channel_config {
};
struct ad7173_channel {
unsigned int chan_reg;
unsigned int ain;
struct ad7173_channel_config cfg;
u8 syscalib_mode;
};
struct ad7173_state {
@ -273,6 +279,7 @@ static const struct ad7173_device_info ad4111_device_info = {
.has_input_buf = true,
.has_current_inputs = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
@ -292,6 +299,7 @@ static const struct ad7173_device_info ad4112_device_info = {
.has_input_buf = true,
.has_current_inputs = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
@ -327,6 +335,7 @@ static const struct ad7173_device_info ad4114_device_info = {
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
@ -344,6 +353,7 @@ static const struct ad7173_device_info ad4115_device_info = {
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 8 * HZ_PER_MHZ,
.sinc5_data_rates = ad4115_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates),
@ -361,6 +371,7 @@ static const struct ad7173_device_info ad4116_device_info = {
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 4 * HZ_PER_MHZ,
.sinc5_data_rates = ad4116_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates),
@ -506,6 +517,105 @@ static const struct regmap_config ad7173_regmap_config = {
.read_flag_mask = BIT(6),
};
enum {
AD7173_SYSCALIB_ZERO_SCALE,
AD7173_SYSCALIB_FULL_SCALE,
};
static const char * const ad7173_syscalib_modes[] = {
[AD7173_SYSCALIB_ZERO_SCALE] = "zero_scale",
[AD7173_SYSCALIB_FULL_SCALE] = "full_scale",
};
static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct ad7173_state *st = iio_priv(indio_dev);
st->channels[chan->channel].syscalib_mode = mode;
return 0;
}
static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad7173_state *st = iio_priv(indio_dev);
return st->channels[chan->channel].syscalib_mode;
}
static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct ad7173_state *st = iio_priv(indio_dev);
bool sys_calib;
int ret, mode;
ret = kstrtobool(buf, &sys_calib);
if (ret)
return ret;
mode = st->channels[chan->channel].syscalib_mode;
if (sys_calib) {
if (mode == AD7173_SYSCALIB_ZERO_SCALE)
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO,
chan->address);
else
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_FULL,
chan->address);
}
return ret ? : len;
}
static const struct iio_enum ad7173_syscalib_mode_enum = {
.items = ad7173_syscalib_modes,
.num_items = ARRAY_SIZE(ad7173_syscalib_modes),
.set = ad7173_set_syscalib_mode,
.get = ad7173_get_syscalib_mode
};
static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = {
{
.name = "sys_calibration",
.write = ad7173_write_syscalib,
.shared = IIO_SEPARATE,
},
IIO_ENUM("sys_calibration_mode", IIO_SEPARATE,
&ad7173_syscalib_mode_enum),
IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
&ad7173_syscalib_mode_enum),
{ }
};
static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_dev)
{
int ret;
int i;
for (i = 0; i < st->num_channels; i++) {
if (indio_dev->channels[i].type != IIO_VOLTAGE)
continue;
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain);
if (ret < 0)
return ret;
if (st->info->has_internal_fs_calibration) {
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL,
st->channels[i].ain);
if (ret < 0)
return ret;
}
}
return 0;
}
static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
unsigned int *mask)
@ -765,6 +875,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info = {
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.data_reg = AD7173_REG_DATA,
.num_resetclks = 64,
};
static int ad7173_setup(struct iio_dev *indio_dev)
@ -802,6 +913,10 @@ static int ad7173_setup(struct iio_dev *indio_dev)
if (!st->config_cnts)
return -ENOMEM;
ret = ad7173_calibrate_all(st, indio_dev);
if (ret)
return ret;
/* All channels are enabled by default after a reset */
return ad7173_disable_all(&st->sd);
}
@ -1024,6 +1139,7 @@ static const struct iio_chan_spec ad7173_channel_template = {
.storagebits = 32,
.endianness = IIO_BE,
},
.ext_info = ad7173_calibsys_ext_info,
};
static const struct iio_chan_spec ad7173_temp_iio_channel_template = {
@ -1317,7 +1433,6 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan->address = chan_index;
chan->scan_index = chan_index;
chan->channel = ain[0];
chan_st_priv->chan_reg = chan_index;
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.odr = 0;

Some files were not shown because too many files have changed in this diff Show more